"""Tests for complyform.core.scanner.state_parser."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from complyform.core.errors import (
    StateFileNotFoundError,
    UnsupportedStateFormatError,
)
from complyform.core.scanner.state_parser import (
    StateParser,
    _derive_source_label,
)

FIXTURES = Path(__file__).parent / "fixtures"


@pytest.fixture
def parser() -> StateParser:
    return StateParser()


class TestParseGCPState:
    def test_happy_path(self, parser: StateParser) -> None:
        resources, meta = parser.parse(str(FIXTURES / "gcp_state.json"), "gcp")
        assert len(resources) == 5
        types = {r.resource_type for r in resources}
        assert "google_compute_instance" in types
        assert "google_sql_database_instance" in types
        assert "google_storage_bucket" in types

    def test_provider_field(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "gcp_state.json"), "gcp")
        for r in resources:
            assert r.provider == "google"

    def test_attributes_populated(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "gcp_state.json"), "gcp")
        sql = [
            r for r in resources if r.resource_type == "google_sql_database_instance"
        ][0]
        assert sql.attributes["database_version"] == "POSTGRES_15"


class TestParseAWSState:
    def test_aws_resources(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "aws_state.json"), "aws")
        assert len(resources) == 5
        types = {r.resource_type for r in resources}
        assert "aws_instance" in types
        assert "aws_s3_bucket" in types
        for r in resources:
            assert r.provider == "aws"


class TestParseAzureState:
    def test_azure_resources(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "azure_state.json"), "azure")
        assert len(resources) == 4
        types = {r.resource_type for r in resources}
        assert "azurerm_virtual_machine" in types
        assert "azurerm_key_vault" in types
        for r in resources:
            assert r.provider == "azurerm"


class TestParseMixedState:
    def test_filters_cloud(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "mixed_state.json"), "gcp")
        assert len(resources) == 2
        for r in resources:
            assert r.provider == "google"

    def test_skip_counts(
        self, parser: StateParser, capsys: pytest.CaptureFixture[str]
    ) -> None:
        parser.parse(str(FIXTURES / "mixed_state.json"), "gcp")
        captured = capsys.readouterr()
        assert "Skipped 3 AWS resources" in captured.err
        assert "--cloud=aws" in captured.err


class TestParseErrors:
    def test_v3_state_raises(self, parser: StateParser) -> None:
        with pytest.raises(UnsupportedStateFormatError):
            parser.parse(str(FIXTURES / "v3_state.json"), "gcp")

    def test_missing_file_raises(self, parser: StateParser) -> None:
        with pytest.raises(StateFileNotFoundError):
            parser.parse("/nonexistent/path.tfstate", "gcp")

    def test_corrupt_json_raises(self, parser: StateParser, tmp_path: Path) -> None:
        bad = tmp_path / "bad.tfstate"
        bad.write_text("{invalid json!!!")
        with pytest.raises(UnsupportedStateFormatError):
            parser.parse(str(bad), "gcp")


class TestParseEmptyState:
    def test_empty_returns_no_resources(self, parser: StateParser) -> None:
        resources, meta = parser.parse(str(FIXTURES / "empty_state.json"), "gcp")
        assert resources == []
        assert meta.serial == 0
        assert meta.terraform_version == "1.9.0"


class TestModuleNestedAddress:
    def test_module_in_address(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "gcp_state.json"), "gcp")
        mod_resources = [r for r in resources if r.module]
        assert len(mod_resources) >= 1
        r = mod_resources[0]
        assert r.address.startswith("module.")
        assert r.module == "module.networking"


class TestForEachInstances:
    def test_for_each_creates_multiple(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "for_each_state.json"), "gcp")
        buckets = [r for r in resources if r.resource_type == "google_storage_bucket"]
        assert len(buckets) == 2
        addrs = {r.address for r in buckets}
        assert 'google_storage_bucket.regional["us"]' in addrs
        assert 'google_storage_bucket.regional["eu"]' in addrs


class TestCountInstances:
    def test_count_creates_multiple(self, parser: StateParser) -> None:
        resources, _ = parser.parse(str(FIXTURES / "for_each_state.json"), "gcp")
        workers = [r for r in resources if r.resource_type == "google_compute_instance"]
        assert len(workers) == 3
        addrs = sorted(r.address for r in workers)
        assert addrs[0] == "google_compute_instance.workers[0]"
        assert addrs[1] == "google_compute_instance.workers[1]"
        assert addrs[2] == "google_compute_instance.workers[2]"


class TestSourceStateLabel:
    @pytest.mark.parametrize(
        ("path", "expected"),
        [
            (
                "./environments/prod/terraform.tfstate",
                "environments-prod",
            ),
            ("terraform.tfstate", "terraform"),
            (
                "/home/user/infra/main.tfstate",
                "home-user-infra-main",
            ),
            ("./simple.tfstate", "simple"),
        ],
    )
    def test_derivation(self, path: str, expected: str) -> None:
        assert _derive_source_label(path) == expected


class TestMetadataExtraction:
    def test_fields(self, parser: StateParser) -> None:
        _, meta = parser.parse(str(FIXTURES / "gcp_state.json"), "gcp")
        assert meta.terraform_version == "1.9.0"
        assert meta.serial == 42
        assert meta.lineage == "abc-123-def"
        assert meta.provider_versions == {}


class TestLargeStateWarning:
    def test_large_resource_count_warning(
        self,
        parser: StateParser,
        tmp_path: Path,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        """Build state with >5000 resource entries."""
        resources = []
        for i in range(5001):
            resources.append(
                {
                    "mode": "managed",
                    "type": "google_compute_instance",
                    "name": f"vm{i}",
                    "provider": 'provider["registry.terraform.io/hashicorp/google"]',
                    "instances": [{"attributes": {"id": f"vm-{i}"}}],
                }
            )
        state = {
            "version": 4,
            "terraform_version": "1.9.0",
            "serial": 1,
            "lineage": "large",
            "resources": resources,
        }
        p = tmp_path / "large.tfstate"
        p.write_text(json.dumps(state))
        parser.parse(str(p), "gcp")
        captured = capsys.readouterr()
        assert ">5000" in captured.err
