"""Parse Terraform v4 state JSON into ResourceRecord list + StateMetadata.

Handles cloud filtering, multi-instance (for_each / count) expansion,
module-nested address construction, and large state warnings.
"""

from __future__ import annotations

import json
import re
import sys
from pathlib import Path
from typing import Any

from pydantic import BaseModel, ConfigDict

from complyform.core.errors import (
    StateFileNotFoundError,
    UnsupportedStateFormatError,
)

PROVIDER_MAP: dict[str, str] = {
    "gcp": "google",
    "aws": "aws",
    "azure": "azurerm",
}

_CLOUD_NAMES: dict[str, str] = {
    "google": "GCP",
    "aws": "AWS",
    "azurerm": "Azure",
}

_CLOUD_FLAGS: dict[str, str] = {
    "google": "gcp",
    "aws": "aws",
    "azurerm": "azure",
}

LARGE_RESOURCE_COUNT = 5000
LARGE_FILE_BYTES = 50 * 1024 * 1024  # 50 MB


class ResourceRecord(BaseModel):
    """Single resource extracted from Terraform state."""

    model_config = ConfigDict(strict=True)

    provider: str
    resource_type: str
    name: str
    address: str
    attributes: dict[str, Any]
    module: str | None = None
    source_state: str


class StateMetadata(BaseModel):
    """Metadata extracted from a Terraform state file."""

    model_config = ConfigDict(strict=True)

    terraform_version: str | None = None
    provider_versions: dict[str, str]
    serial: int
    lineage: str | None = None


def _extract_provider_short(provider_uri: str) -> str:
    """Extract short provider name from registry URI.

    e.g. 'provider["registry.terraform.io/hashicorp/google"]' -> 'google'
    """
    match = re.search(r'/([^/"]+)"?\]?$', provider_uri)
    if match:
        return match.group(1)
    parts = provider_uri.rstrip('"]').split("/")
    return parts[-1]


def _derive_source_label(state_path: str) -> str:
    """Derive source_state label from state file path.

    Replace path separators and dots with hyphens, strip leading
    hyphens, lowercase.
    """
    label = re.sub(r"[/\\.]", "-", state_path)
    label = re.sub(r"-tfstate$", "", label)
    label = re.sub(r"-terraform$", "", label)
    label = label.strip("-").lower()
    return label


def _build_address(
    module_path: str | None,
    res_type: str,
    res_name: str,
    index_key: str | int | None,
) -> str:
    """Build full resource address including module and index."""
    base = (
        f"{module_path}.{res_type}.{res_name}"
        if module_path
        else f"{res_type}.{res_name}"
    )
    if index_key is not None:
        if isinstance(index_key, int):
            return f"{base}[{index_key}]"
        return f'{base}["{index_key}"]'
    return base


class StateParser:
    """Parse Terraform v4 state files."""

    def __init__(self, *, verbose: bool = False) -> None:
        self._verbose = verbose

    def parse(
        self,
        state_path: str,
        cloud: str,
    ) -> tuple[list[ResourceRecord], StateMetadata]:
        """Parse .tfstate JSON into (resources, metadata)."""
        path = Path(state_path)
        if not path.exists():
            raise StateFileNotFoundError(state_path)

        raw_bytes = path.read_bytes()

        if len(raw_bytes) > LARGE_FILE_BYTES:
            print(
                f"Warning: Large state file"
                f" ({len(raw_bytes) // (1024 * 1024)}MB)."
                " Parsing may take longer.",
                file=sys.stderr,
            )

        try:
            data: dict[str, Any] = json.loads(raw_bytes)
        except (json.JSONDecodeError, ValueError) as exc:
            raise UnsupportedStateFormatError("corrupt") from exc

        version = data.get("version")
        if version != 4:
            raise UnsupportedStateFormatError(str(version))

        target_provider = PROVIDER_MAP.get(cloud)

        metadata = StateMetadata(
            terraform_version=data.get("terraform_version"),
            provider_versions={},
            serial=data.get("serial", 0),
            lineage=data.get("lineage"),
        )

        if self._verbose:
            print(
                "Provider versions not available in state file"
                " — versions available from"
                " .terraform.lock.hcl",
                file=sys.stderr,
            )

        source_label = _derive_source_label(state_path)
        raw_resources: list[dict[str, Any]] = data.get("resources", [])

        if len(raw_resources) > LARGE_RESOURCE_COUNT:
            print(
                f"Warning: State contains"
                f" {len(raw_resources)} resources"
                " (>5000). Processing may be slow.",
                file=sys.stderr,
            )

        resources: list[ResourceRecord] = []
        skipped: dict[str, int] = {}

        for res in raw_resources:
            provider_uri: str = res.get("provider", "")
            short_provider = _extract_provider_short(provider_uri)

            if target_provider and short_provider != target_provider:
                skipped[short_provider] = skipped.get(short_provider, 0) + 1
                continue

            res_type: str = res.get("type", "")
            res_name: str = res.get("name", "")
            module_path: str | None = res.get("module") or None

            instances: list[dict[str, Any]] = res.get("instances", [])
            for inst in instances:
                attrs: dict[str, Any] = inst.get("attributes", {})
                index_key: str | int | None = inst.get("index_key")

                address = _build_address(module_path, res_type, res_name, index_key)

                resources.append(
                    ResourceRecord(
                        provider=short_provider,
                        resource_type=res_type,
                        name=res_name,
                        address=address,
                        attributes=attrs,
                        module=module_path,
                        source_state=source_label,
                    )
                )

        for prov, count in skipped.items():
            display_name = _CLOUD_NAMES.get(prov, prov)
            cloud_flag = _CLOUD_FLAGS.get(prov, prov)
            print(
                f"Skipped {count} {display_name} resources"
                f" — re-run with --cloud={cloud_flag}"
                " to assess these.",
                file=sys.stderr,
            )

        return resources, metadata
