# LocalStack Resource Provider Scaffolding v2
from __future__ import annotations

import json
from pathlib import Path
from typing import TypedDict

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


class EventsRuleProperties(TypedDict):
    Arn: str | None
    Description: str | None
    EventBusName: str | None
    EventPattern: dict | None
    Id: str | None
    Name: str | None
    RoleArn: str | None
    ScheduleExpression: str | None
    State: str | None
    Targets: list[Target] | None


class HttpParameters(TypedDict):
    HeaderParameters: dict | None
    PathParameterValues: list[str] | None
    QueryStringParameters: dict | None


class DeadLetterConfig(TypedDict):
    Arn: str | None


class RunCommandTarget(TypedDict):
    Key: str | None
    Values: list[str] | None


class RunCommandParameters(TypedDict):
    RunCommandTargets: list[RunCommandTarget] | None


class InputTransformer(TypedDict):
    InputTemplate: str | None
    InputPathsMap: dict | None


class KinesisParameters(TypedDict):
    PartitionKeyPath: str | None


class RedshiftDataParameters(TypedDict):
    Database: str | None
    Sql: str | None
    DbUser: str | None
    SecretManagerArn: str | None
    StatementName: str | None
    WithEvent: bool | None


class SqsParameters(TypedDict):
    MessageGroupId: str | None


class PlacementConstraint(TypedDict):
    Expression: str | None
    Type: str | None


class PlacementStrategy(TypedDict):
    Field: str | None
    Type: str | None


class CapacityProviderStrategyItem(TypedDict):
    CapacityProvider: str | None
    Base: int | None
    Weight: int | None


class Tag(TypedDict):
    Key: str | None
    Value: str | None


class AwsVpcConfiguration(TypedDict):
    Subnets: list[str] | None
    AssignPublicIp: str | None
    SecurityGroups: list[str] | None


class NetworkConfiguration(TypedDict):
    AwsVpcConfiguration: AwsVpcConfiguration | None


class EcsParameters(TypedDict):
    TaskDefinitionArn: str | None
    CapacityProviderStrategy: list[CapacityProviderStrategyItem] | None
    EnableECSManagedTags: bool | None
    EnableExecuteCommand: bool | None
    Group: str | None
    LaunchType: str | None
    NetworkConfiguration: NetworkConfiguration | None
    PlacementConstraints: list[PlacementConstraint] | None
    PlacementStrategies: list[PlacementStrategy] | None
    PlatformVersion: str | None
    PropagateTags: str | None
    ReferenceId: str | None
    TagList: list[Tag] | None
    TaskCount: int | None


class BatchRetryStrategy(TypedDict):
    Attempts: int | None


class BatchArrayProperties(TypedDict):
    Size: int | None


class BatchParameters(TypedDict):
    JobDefinition: str | None
    JobName: str | None
    ArrayProperties: BatchArrayProperties | None
    RetryStrategy: BatchRetryStrategy | None


class SageMakerPipelineParameter(TypedDict):
    Name: str | None
    Value: str | None


class SageMakerPipelineParameters(TypedDict):
    PipelineParameterList: list[SageMakerPipelineParameter] | None


class RetryPolicy(TypedDict):
    MaximumEventAgeInSeconds: int | None
    MaximumRetryAttempts: int | None


class Target(TypedDict):
    Arn: str | None
    Id: str | None
    BatchParameters: BatchParameters | None
    DeadLetterConfig: DeadLetterConfig | None
    EcsParameters: EcsParameters | None
    HttpParameters: HttpParameters | None
    Input: str | None
    InputPath: str | None
    InputTransformer: InputTransformer | None
    KinesisParameters: KinesisParameters | None
    RedshiftDataParameters: RedshiftDataParameters | None
    RetryPolicy: RetryPolicy | None
    RoleArn: str | None
    RunCommandParameters: RunCommandParameters | None
    SageMakerPipelineParameters: SageMakerPipelineParameters | None
    SqsParameters: SqsParameters | None


REPEATED_INVOCATION = "repeated_invocation"

MATCHING_OPERATIONS = [
    "prefix",
    "cidr",
    "exists",
    "suffix",
    "anything-but",
    "numeric",
    "equals-ignore-case",
    "wildcard",
]


def extract_rule_name(rule_id: str) -> str:
    return rule_id.rsplit("|", maxsplit=1)[-1]


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

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

        Primary identifier fields:
          - /properties/Id

        Create-only properties:
          - /properties/Name
          - /properties/EventBusName

        Read-only properties:
          - /properties/Id
          - /properties/Arn


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

        name = model.get("Name")
        if not name:
            name = util.generate_default_name(
                stack_name=request.stack_name, logical_resource_id=request.logical_resource_id
            )

        if event_bus_name := model.get("EventBusName"):
            model["Id"] = "|".join(
                [
                    event_bus_name,
                    name,
                ]
            )
        else:
            model["Id"] = name

        attrs = [
            "ScheduleExpression",
            "EventPattern",
            "State",
            "Description",
            "Name",
            "EventBusName",
        ]

        params = util.select_attributes(model, attrs)

        def wrap_in_lists(o, **kwargs):
            if isinstance(o, dict):
                for k, v in o.items():
                    if not isinstance(v, (dict, list)) and k not in MATCHING_OPERATIONS:
                        o[k] = [v]
            return o

        pattern = params.get("EventPattern")
        if isinstance(pattern, dict):
            wrapped = common.recurse_object(pattern, wrap_in_lists)
            params["EventPattern"] = json.dumps(wrapped)

        params["Name"] = name
        result = events.put_rule(**params)
        model["Arn"] = result["RuleArn"]

        # put targets
        event_bus_name = model.get("EventBusName")
        targets = model.get("Targets") or []

        if targets:
            put_targets_kwargs = {"Rule": extract_rule_name(model["Id"]), "Targets": targets}
            if event_bus_name:
                put_targets_kwargs["EventBusName"] = event_bus_name

            put_targets_kwargs = util.convert_request_kwargs(
                put_targets_kwargs,
                events.meta.service_model.operation_model("PutTargets").input_shape,
            )

            events.put_targets(**put_targets_kwargs)

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

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


        """
        raise NotImplementedError

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


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

        rule_name = extract_rule_name(model["Id"])
        targets = events.list_targets_by_rule(Rule=rule_name)["Targets"]
        target_ids = [tgt["Id"] for tgt in targets]
        if targets:
            events.remove_targets(Rule=rule_name, Ids=target_ids, Force=True)
        events.delete_rule(Name=rule_name)

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

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


        """
        raise NotImplementedError
