from dataclasses import dataclass
from typing import Any, TypedDict, final

import pulumi
import pulumi_aws

from stelvio import context
from stelvio.component import Component
from stelvio.dns import DnsProviderNotConfiguredError, Record


@final
@dataclass(frozen=True)
class AcmValidatedDomainResources:
    certificate: pulumi_aws.acm.Certificate
    validation_record: Record
    cert_validation: pulumi_aws.acm.CertificateValidation


class AcmValidatedDomainCustomizationDict(TypedDict, total=False):
    certificate: pulumi_aws.acm.CertificateArgs | dict[str, Any] | None
    validation_record: (
        dict[str, Any] | None
    )  # No specific Plumi Args type here, because cross cloud compat
    cert_validation: pulumi_aws.acm.CertificateValidationArgs | dict[str, Any] | None


@final
class AcmValidatedDomain(
    Component[AcmValidatedDomainResources, AcmValidatedDomainCustomizationDict]
):
    def __init__(
        self,
        name: str,
        domain_name: str,
        customize: AcmValidatedDomainCustomizationDict | None = None,
        region: str | None = None,
    ):
        self.domain_name = domain_name
        self._region = region
        super().__init__(name, customize=customize)

    def _create_resources(self) -> AcmValidatedDomainResources:
        dns = context().dns
        if dns is None:
            raise DnsProviderNotConfiguredError(
                "DNS provider is not configured in the context. "
                "Please set up a DNS provider to use custom domains."
            )

        # Create a provider only when a specific region is requested and it differs
        # from the user's configured region. When region is None or matches the
        # configured region, the default provider is used.
        provider = None
        if self._region and self._region != context().aws.region:
            provider = pulumi_aws.Provider(
                context().prefix(f"{self.name}-{self._region}-provider"),
                region=self._region,
            )

        provider_opts = pulumi.ResourceOptions(provider=provider) if provider else None

        # 1 - Issue Certificate
        certificate = pulumi_aws.acm.Certificate(
            context().prefix(f"{self.name}-certificate"),
            **self._customizer(
                "certificate",
                {
                    "domain_name": self.domain_name,
                    "validation_method": "DNS",
                },
            ),
            opts=provider_opts,
        )

        # 2 - Validate Certificate with DNS PROVIDER
        first_option = certificate.domain_validation_options.apply(lambda options: options[0])
        validation_record = dns.create_caa_record(
            resource_name=context().prefix(f"{self.name}-certificate-validation-record"),
            name=first_option.apply(lambda opt: opt["resource_record_name"]),
            **self._customizer(
                "validation_record",
                {
                    "record_type": first_option.apply(lambda opt: opt["resource_record_type"]),
                    "content": first_option.apply(lambda opt: opt["resource_record_value"]),
                    "ttl": 1,
                },
            ),
        )

        # 3 - Wait for validation - use the validation record's FQDN to ensure it exists
        cert_validation = pulumi_aws.acm.CertificateValidation(
            context().prefix(f"{self.name}-certificate-validation"),
            **self._customizer(
                "cert_validation",
                {
                    "certificate_arn": certificate.arn,
                    # This ensures validation_record exists
                    "validation_record_fqdns": [validation_record.name],
                },
            ),
            opts=pulumi.ResourceOptions(
                depends_on=[certificate, validation_record.pulumi_resource],
                provider=provider,
            ),
        )

        return AcmValidatedDomainResources(
            certificate=certificate,
            validation_record=validation_record,
            cert_validation=cert_validation,
        )
