# LocalStack Resource Provider Scaffolding v2
from __future__ import annotations

import json
from pathlib import Path
from typing import TypedDict

from botocore.exceptions import ClientError

import localstack.services.cloudformation.provider_utils as util
from localstack.services.cloudformation.resource_provider import (
    OperationStatus,
    ProgressEvent,
    ResourceProvider,
    ResourceRequest,
)
from localstack.utils.strings import short_uid


class EventsEventBusPolicyProperties(TypedDict):
    StatementId: str | None
    Action: str | None
    Condition: Condition | None
    EventBusName: str | None
    Id: str | None
    Principal: str | None
    Statement: dict | None


class Condition(TypedDict):
    Key: str | None
    Type: str | None
    Value: str | None


REPEATED_INVOCATION = "repeated_invocation"


class EventsEventBusPolicyProvider(ResourceProvider[EventsEventBusPolicyProperties]):
    TYPE = "AWS::Events::EventBusPolicy"  # Autogenerated. Don't change
    SCHEMA = util.get_schema_path(Path(__file__))  # Autogenerated. Don't change

    def create(
        self,
        request: ResourceRequest[EventsEventBusPolicyProperties],
    ) -> ProgressEvent[EventsEventBusPolicyProperties]:
        """
        Create a new resource.

        Primary identifier fields:
          - /properties/Id

        Required properties:
          - StatementId

        Create-only properties:
          - /properties/EventBusName
          - /properties/StatementId

        Read-only properties:
          - /properties/Id



        """
        model = request.desired_state
        events = request.aws_client_factory.events

        model["Id"] = f"EventBusPolicy-{short_uid()}"

        # either this field  is set or all other fields (Action, Principal, etc.)
        statement = model.get("Statement")
        optional_params = {"EventBusName": model.get("EventBusName")}

        if statement:
            policy = {
                "Version": "2012-10-17",
                "Statement": [{"Sid": model["StatementId"], **statement}],
            }
            events.put_permission(Policy=json.dumps(policy), **optional_params)
        else:
            if model.get("Condition"):
                optional_params.update({"Condition": model.get("Condition")})

            events.put_permission(
                StatementId=model["StatementId"],
                Action=model["Action"],
                Principal=model["Principal"],
                **optional_params,
            )

        return ProgressEvent(
            status=OperationStatus.SUCCESS,
            resource_model=model,
            custom_context=request.custom_context,
        )

    def read(
        self,
        request: ResourceRequest[EventsEventBusPolicyProperties],
    ) -> ProgressEvent[EventsEventBusPolicyProperties]:
        """
        Fetch resource information


        """
        raise NotImplementedError

    def delete(
        self,
        request: ResourceRequest[EventsEventBusPolicyProperties],
    ) -> ProgressEvent[EventsEventBusPolicyProperties]:
        """
        Delete a resource

        """
        model = request.desired_state
        events = request.aws_client_factory.events

        statement_id = model["StatementId"]
        event_bus_name = model.get("EventBusName")

        params = {"StatementId": statement_id, "RemoveAllPermissions": False}

        if event_bus_name:
            params["EventBusName"] = event_bus_name

        # We are using try/except since at the moment
        # CFN doesn't properly resolve dependency between resources
        # so this resource could be deleted if parent resource was deleted first

        try:
            events.remove_permission(**params)
        except ClientError as err:
            is_resource_not_found = err.response["Error"]["Code"] == "ResourceNotFoundException"

            if not is_resource_not_found:
                raise

        return ProgressEvent(
            status=OperationStatus.SUCCESS,
            resource_model=model,
            custom_context=request.custom_context,
        )

    def update(
        self,
        request: ResourceRequest[EventsEventBusPolicyProperties],
    ) -> ProgressEvent[EventsEventBusPolicyProperties]:
        """
        Update a resource


        """
        raise NotImplementedError
