"""Parse AWS CloudFormation templates and stack state."""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Optional

from .terraform import _flatten_attributes, TerraformResource


# CloudFormation type to Terraform type mapping
_CF_TO_TF_TYPE: dict[str, str] = {
    "AWS::EC2::Instance": "aws_instance",
    "AWS::EC2::SecurityGroup": "aws_security_group",
    "AWS::EC2::VPC": "aws_vpc",
    "AWS::EC2::Subnet": "aws_subnet",
    "AWS::S3::Bucket": "aws_s3_bucket",
    "AWS::RDS::DBInstance": "aws_db_instance",
    "AWS::Lambda::Function": "aws_lambda_function",
    "AWS::IAM::Role": "aws_iam_role",
    "AWS::IAM::Policy": "aws_iam_policy",
    "AWS::ECS::Cluster": "aws_ecs_cluster",
    "AWS::ECS::Service": "aws_ecs_service",
    "AWS::ELB::LoadBalancer": "aws_elb",
    "AWS::ElasticLoadBalancingV2::LoadBalancer": "aws_lb",
    "AWS::SNS::Topic": "aws_sns_topic",
    "AWS::SQS::Queue": "aws_sqs_queue",
    "AWS::DynamoDB::Table": "aws_dynamodb_table",
    "AWS::KMS::Key": "aws_kms_key",
    "AWS::Logs::LogGroup": "aws_cloudwatch_log_group",
}


@dataclass
class CloudFormationResource:
    """A resource from a CloudFormation template/stack."""
    logical_id: str
    type: str  # AWS::EC2::Instance
    properties: dict[str, Any] = field(default_factory=dict)
    physical_id: Optional[str] = None
    stack_name: Optional[str] = None
    status: Optional[str] = None
    metadata: dict[str, Any] = field(default_factory=dict)

    @property
    def terraform_type(self) -> str:
        """Map to Terraform type for unified policy checking."""
        return _CF_TO_TF_TYPE.get(self.type, self.type.replace("::", "_").lower())

    @property
    def display_name(self) -> str:
        """Human-readable name."""
        return self.properties.get("Name", self.properties.get("Tags", [{}])[0].get("Value", self.logical_id) if self.properties.get("Tags") else self.logical_id)


class CloudFormationParser:
    """Parse CloudFormation templates and describe-stack-resources output."""

    def __init__(self) -> None:
        self.resources: dict[str, CloudFormationResource] = {}
        self.template_version: str = ""
        self.parameters: dict[str, Any] = {}
        self.outputs: dict[str, Any] = {}

    def parse_template_file(self, path: str | Path) -> dict[str, CloudFormationResource]:
        """Parse a CloudFormation template file (JSON or YAML).

        Note: YAML support requires PyYAML to be installed.
        """
        path = Path(path)
        if not path.exists():
            raise FileNotFoundError(f"CloudFormation template not found: {path}")

        with open(path, "r", encoding="utf-8") as f:
            content = f.read()

        if path.suffix in (".yaml", ".yml"):
            try:
                import yaml
                data = yaml.safe_load(content)
            except ImportError:
                raise ImportError("PyYAML required to parse YAML CloudFormation templates")
        else:
            data = json.loads(content)

        return self.parse_template(data)

    def parse_template(self, template: dict[str, Any]) -> dict[str, CloudFormationResource]:
        """Parse a CloudFormation template dict.

        Args:
            template: Parsed CloudFormation template.

        Returns:
            Dictionary mapping logical ID to CloudFormationResource.
        """
        self.resources = {}
        self.template_version = template.get("AWSTemplateFormatVersion", "")
        self.parameters = template.get("Parameters", {})
        self.outputs = template.get("Outputs", {})

        resources = template.get("Resources", {})
        for logical_id, res_data in resources.items():
            res_type = res_data.get("Type", "")
            properties = res_data.get("Properties", {})
            metadata = res_data.get("Metadata", {})

            # Resolve simple Ref/Fn::Sub in properties (for display purposes)
            resolved_props = self._shallow_resolve(properties)

            resource = CloudFormationResource(
                logical_id=logical_id,
                type=res_type,
                properties=_flatten_attributes(resolved_props),
                metadata=metadata,
            )
            self.resources[logical_id] = resource

        return self.resources

    def parse_stack_resources(
        self,
        template: dict[str, Any],
        stack_resources: list[dict[str, Any]],
    ) -> dict[str, CloudFormationResource]:
        """Parse template + describe-stack-resources output to get physical IDs.

        Args:
            template: CloudFormation template.
            stack_resources: List from describe-stack-resources StackResources.
        """
        self.parse_template(template)

        # Enrich with physical resource IDs from describe-stack-resources
        physical_map = {
            sr["LogicalResourceId"]: sr
            for sr in stack_resources
        }

        for logical_id, resource in self.resources.items():
            if logical_id in physical_map:
                sr = physical_map[logical_id]
                resource.physical_id = sr.get("PhysicalResourceId")
                resource.status = sr.get("ResourceStatus")
                resource.stack_name = sr.get("StackName")

        return self.resources

    def _shallow_resolve(self, properties: dict[str, Any]) -> dict[str, Any]:
        """Shallow resolve CloudFormation intrinsic functions for display."""
        result: dict[str, Any] = {}
        for key, value in properties.items():
            if isinstance(value, dict):
                if "Ref" in value:
                    result[key] = f"${{ref:{value['Ref']}}}"
                elif "Fn::Sub" in value:
                    result[key] = f"${{sub:{value['Fn::Sub']}}}" if isinstance(value["Fn::Sub"], str) else str(value["Fn::Sub"])
                elif "Fn::GetAtt" in value:
                    attr_ref = value["Fn::GetAtt"]
                    if isinstance(attr_ref, list):
                        result[key] = f"${{getatt:{'.'.join(attr_ref)}}}"
                    else:
                        result[key] = f"${{getatt:{attr_ref}}}"
                else:
                    result[key] = self._shallow_resolve(value)
            elif isinstance(value, list):
                result[key] = [
                    self._shallow_resolve(item) if isinstance(item, dict) else item
                    for item in value
                ]
            else:
                result[key] = value
        return result

    def to_terraform_resources(self) -> dict[str, TerraformResource]:
        """Convert to TerraformResource format for unified policy checking."""
        result: dict[str, TerraformResource] = {}
        for logical_id, res in self.resources.items():
            tf_res = TerraformResource(
                address=f"cf:{logical_id}",
                type=res.terraform_type,
                name=logical_id,
                provider="cloudformation",
                cloud_provider="aws",
                resource_id=res.physical_id or "",
                attributes=res.properties,
            )
            result[logical_id] = tf_res
        return result
