"""Tests for the Terraform state parser."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import pytest

from driftguard.iac.terraform import (
    TerraformResource,
    TerraformStateParser,
    _detect_cloud_provider,
    _flatten_attributes,
)


# --- Helper: sample Terraform state v4 ---

SAMPLE_TFSTATE_V4: dict[str, Any] = {
    "version": 4,
    "terraform_version": "1.7.0",
    "serial": 42,
    "lineage": "abc-123",
    "outputs": {},
    "resources": [
        {
            "mode": "managed",
            "type": "azurerm_resource_group",
            "name": "main",
            "provider": 'provider["registry.terraform.io/hashicorp/azurerm"]',
            "instances": [
                {
                    "schema_version": 0,
                    "attributes": {
                        "id": "/subscriptions/xxx/resourceGroups/rg-main",
                        "name": "rg-main",
                        "location": "uksouth",
                        "tags": {
                            "environment": "production",
                            "team": "platform",
                        },
                    },
                    "dependencies": [],
                }
            ],
        },
        {
            "mode": "managed",
            "type": "azurerm_storage_account",
            "name": "data",
            "provider": 'provider["registry.terraform.io/hashicorp/azurerm"]',
            "instances": [
                {
                    "schema_version": 4,
                    "attributes": {
                        "id": "/subscriptions/xxx/storageAccounts/stdata",
                        "name": "stdata",
                        "resource_group_name": "rg-main",
                        "location": "uksouth",
                        "account_tier": "Standard",
                        "account_replication_type": "LRS",
                        "enable_https_traffic_only": True,
                        "allow_blob_public_access": False,
                        "min_tls_version": "TLS1_2",
                        "tags": {"environment": "production"},
                        "primary_access_key": "secret123",
                    },
                    "dependencies": [
                        "azurerm_resource_group.main",
                    ],
                }
            ],
        },
        {
            "mode": "managed",
            "type": "aws_security_group",
            "name": "web",
            "provider": 'provider["registry.terraform.io/hashicorp/aws"]',
            "instances": [
                {
                    "schema_version": 1,
                    "attributes": {
                        "id": "sg-abc123",
                        "name": "web-sg",
                        "description": "Web security group",
                        "vpc_id": "vpc-123",
                        "ingress": [
                            {
                                "from_port": 443,
                                "to_port": 443,
                                "protocol": "tcp",
                                "cidr_blocks": ["0.0.0.0/0"],
                            },
                            {
                                "from_port": 80,
                                "to_port": 80,
                                "protocol": "tcp",
                                "cidr_blocks": ["0.0.0.0/0"],
                            },
                        ],
                        "egress": [
                            {
                                "from_port": 0,
                                "to_port": 0,
                                "protocol": "-1",
                                "cidr_blocks": ["0.0.0.0/0"],
                            }
                        ],
                        "tags": {"Name": "web-sg"},
                    },
                    "dependencies": [],
                }
            ],
        },
        {
            "mode": "data",
            "type": "azurerm_client_config",
            "name": "current",
            "provider": 'provider["registry.terraform.io/hashicorp/azurerm"]',
            "instances": [
                {
                    "schema_version": 0,
                    "attributes": {
                        "client_id": "xxx",
                        "tenant_id": "yyy",
                    },
                }
            ],
        },
    ],
}


SAMPLE_TFSTATE_V3: dict[str, Any] = {
    "version": 3,
    "terraform_version": "0.11.14",
    "serial": 10,
    "modules": [
        {
            "path": ["root"],
            "outputs": {},
            "resources": {
                "aws_instance.web": {
                    "type": "aws_instance",
                    "provider": "aws",
                    "primary": {
                        "id": "i-abc123",
                        "attributes": {
                            "id": "i-abc123",
                            "instance_type": "t3.micro",
                            "ami": "ami-12345",
                            "public_ip": "1.2.3.4",
                        },
                    },
                    "tainted": False,
                },
            },
        }
    ],
}


# --- Tests ---


class TestDetectCloudProvider:
    def test_azure(self) -> None:
        assert _detect_cloud_provider("azurerm_resource_group", "") == "azure"
        assert _detect_cloud_provider("azuread_application", "") == "azure"

    def test_aws(self) -> None:
        assert _detect_cloud_provider("aws_instance", "") == "aws"
        assert _detect_cloud_provider("aws_s3_bucket", "") == "aws"

    def test_gcp(self) -> None:
        assert _detect_cloud_provider("google_compute_instance", "") == "gcp"

    def test_fallback_to_provider_string(self) -> None:
        assert _detect_cloud_provider("custom_resource", "provider[azurerm]") == "azure"
        assert _detect_cloud_provider("custom_resource", "provider[aws]") == "aws"

    def test_unknown(self) -> None:
        assert _detect_cloud_provider("custom_resource", "custom_provider") == "unknown"


class TestFlattenAttributes:
    def test_flat(self) -> None:
        result = _flatten_attributes({"name": "test", "location": "uksouth"})
        assert result == {"name": "test", "location": "uksouth"}

    def test_nested(self) -> None:
        result = _flatten_attributes({"tags": {"env": "prod", "team": "platform"}})
        assert result == {"tags.env": "prod", "tags.team": "platform"}

    def test_skips_volatile(self) -> None:
        result = _flatten_attributes({"name": "test", "etag": "abc", "id": "123"})
        assert "etag" not in result
        assert "id" not in result
        assert "name" in result

    def test_masks_sensitive(self) -> None:
        result = _flatten_attributes({"name": "test", "password": "secret123"})
        assert result["password"] == "<sensitive>"

    def test_preserves_lists(self) -> None:
        result = _flatten_attributes({"cidr_blocks": ["10.0.0.0/8"]})
        assert result["cidr_blocks"] == ["10.0.0.0/8"]

    def test_deep_nesting(self) -> None:
        result = _flatten_attributes({"a": {"b": {"c": "deep"}}})
        assert result["a.b.c"] == "deep"


class TestTerraformStateParserV4:
    def setup_method(self) -> None:
        self.parser = TerraformStateParser()
        self.resources = self.parser.parse(SAMPLE_TFSTATE_V4)

    def test_version_info(self) -> None:
        assert self.parser.state_version == 4
        assert self.parser.terraform_version == "1.7.0"
        assert self.parser.serial == 42

    def test_resource_count(self) -> None:
        # Should skip data sources, so 3 managed resources
        assert len(self.resources) == 3

    def test_data_sources_skipped(self) -> None:
        addresses = list(self.resources.keys())
        assert not any(a.startswith("data.") for a in addresses)

    def test_azure_resource_group(self) -> None:
        rg = self.resources["azurerm_resource_group.main"]
        assert rg.type == "azurerm_resource_group"
        assert rg.name == "main"
        assert rg.cloud_provider == "azure"
        assert rg.resource_id == "/subscriptions/xxx/resourceGroups/rg-main"
        assert rg.attributes["name"] == "rg-main"
        assert rg.attributes["location"] == "uksouth"
        assert rg.attributes["tags.environment"] == "production"

    def test_storage_account(self) -> None:
        sa = self.resources["azurerm_storage_account.data"]
        assert sa.type == "azurerm_storage_account"
        assert sa.cloud_provider == "azure"
        assert sa.attributes["account_tier"] == "Standard"
        assert sa.attributes["enable_https_traffic_only"] is True
        assert sa.attributes["allow_blob_public_access"] is False
        assert sa.attributes["min_tls_version"] == "TLS1_2"
        # Sensitive should be masked
        assert sa.attributes.get("primary_access_key") == "<sensitive>"

    def test_aws_security_group(self) -> None:
        sg = self.resources["aws_security_group.web"]
        assert sg.type == "aws_security_group"
        assert sg.cloud_provider == "aws"
        assert sg.attributes["name"] == "web-sg"
        assert sg.attributes["vpc_id"] == "vpc-123"
        # Ingress should be preserved as list
        assert isinstance(sg.attributes["ingress"], list)
        assert len(sg.attributes["ingress"]) == 2

    def test_dependencies(self) -> None:
        sa = self.resources["azurerm_storage_account.data"]
        assert "azurerm_resource_group.main" in sa.dependencies

    def test_display_name(self) -> None:
        rg = self.resources["azurerm_resource_group.main"]
        assert rg.display_name == "rg-main"

    def test_filter_by_provider(self) -> None:
        azure = self.parser.get_resources_by_provider("azure")
        assert len(azure) == 2
        aws = self.parser.get_resources_by_provider("aws")
        assert len(aws) == 1

    def test_filter_by_type(self) -> None:
        sgs = self.parser.get_resources_by_type("aws_security_group")
        assert len(sgs) == 1

    def test_security_relevant_attributes(self) -> None:
        sg = self.resources["aws_security_group.web"]
        sec_attrs = self.parser.get_security_relevant_attributes(sg)
        assert "ingress" in sec_attrs
        assert "egress" in sec_attrs


class TestTerraformStateParserV3:
    def setup_method(self) -> None:
        self.parser = TerraformStateParser()
        self.resources = self.parser.parse(SAMPLE_TFSTATE_V3)

    def test_version(self) -> None:
        assert self.parser.state_version == 3

    def test_resource_parsed(self) -> None:
        assert len(self.resources) == 1
        assert "aws_instance.web" in self.resources

    def test_attributes(self) -> None:
        inst = self.resources["aws_instance.web"]
        assert inst.type == "aws_instance"
        assert inst.cloud_provider == "aws"
        assert inst.resource_id == "i-abc123"
        assert inst.attributes["instance_type"] == "t3.micro"
        assert inst.attributes["ami"] == "ami-12345"


class TestTerraformStateParserFile:
    def test_parse_file(self, tmp_path: Path) -> None:
        state_file = tmp_path / "test.tfstate"
        state_file.write_text(json.dumps(SAMPLE_TFSTATE_V4))
        parser = TerraformStateParser()
        resources = parser.parse_file(state_file)
        assert len(resources) == 3

    def test_file_not_found(self) -> None:
        parser = TerraformStateParser()
        with pytest.raises(FileNotFoundError):
            parser.parse_file("/nonexistent/path.tfstate")

    def test_unsupported_version(self) -> None:
        parser = TerraformStateParser()
        with pytest.raises(ValueError, match="Unsupported"):
            parser.parse({"version": 1})


class TestTerraformStateWithModules:
    def test_module_resources(self) -> None:
        state = {
            "version": 4,
            "terraform_version": "1.7.0",
            "serial": 1,
            "resources": [
                {
                    "module": "module.network",
                    "mode": "managed",
                    "type": "azurerm_virtual_network",
                    "name": "main",
                    "provider": 'provider["registry.terraform.io/hashicorp/azurerm"]',
                    "instances": [
                        {
                            "attributes": {
                                "id": "/subscriptions/xxx/vnet/main",
                                "name": "vnet-main",
                                "address_space": ["10.0.0.0/16"],
                            },
                        }
                    ],
                }
            ],
        }
        parser = TerraformStateParser()
        resources = parser.parse(state)
        assert len(resources) == 1
        key = list(resources.keys())[0]
        assert "module.network" in key
        res = resources[key]
        assert res.module == "module.network"


class TestTerraformStateWithCount:
    def test_indexed_resources(self) -> None:
        state = {
            "version": 4,
            "terraform_version": "1.7.0",
            "serial": 1,
            "resources": [
                {
                    "mode": "managed",
                    "type": "aws_subnet",
                    "name": "private",
                    "provider": 'provider["registry.terraform.io/hashicorp/aws"]',
                    "instances": [
                        {
                            "index_key": 0,
                            "attributes": {"id": "subnet-1", "cidr_block": "10.0.1.0/24"},
                        },
                        {
                            "index_key": 1,
                            "attributes": {"id": "subnet-2", "cidr_block": "10.0.2.0/24"},
                        },
                    ],
                }
            ],
        }
        parser = TerraformStateParser()
        resources = parser.parse(state)
        assert len(resources) == 2
        assert "aws_subnet.private[0]" in resources
        assert "aws_subnet.private[1]" in resources


class TestTerraformStateWithForEach:
    def test_string_keyed_resources(self) -> None:
        state = {
            "version": 4,
            "terraform_version": "1.7.0",
            "serial": 1,
            "resources": [
                {
                    "mode": "managed",
                    "type": "azurerm_resource_group",
                    "name": "envs",
                    "provider": 'provider["registry.terraform.io/hashicorp/azurerm"]',
                    "instances": [
                        {
                            "index_key": "dev",
                            "attributes": {"id": "rg-dev", "name": "rg-dev", "location": "uksouth"},
                        },
                        {
                            "index_key": "prod",
                            "attributes": {"id": "rg-prod", "name": "rg-prod", "location": "uksouth"},
                        },
                    ],
                }
            ],
        }
        parser = TerraformStateParser()
        resources = parser.parse(state)
        assert len(resources) == 2
        assert 'azurerm_resource_group.envs["dev"]' in resources
        assert 'azurerm_resource_group.envs["prod"]' in resources
