"""Tests for complyform.core.profiles.mapping_loader — including error paths."""

from __future__ import annotations

from typing import TYPE_CHECKING
from unittest.mock import patch

if TYPE_CHECKING:
    from pathlib import Path

import pytest

from complyform.core.errors import ProfileBundleError
from complyform.core.profiles.mapping_loader import (
    _reset_cache,
    get_mappings_for_check,
    get_mappings_for_control,
    load_mappings,
)


@pytest.fixture(autouse=True)
def _clear_cache() -> None:
    _reset_cache()


# -------------------------------------------------------------------
# Happy paths
# -------------------------------------------------------------------


class TestLoadMappings:
    def test_soc2_gcp(self) -> None:
        mf = load_mappings("soc2", "gcp")
        assert mf.framework_id == "soc2"
        assert mf.cloud == "gcp"
        assert mf.schema_version == 1
        assert len(mf.mappings) >= 10

    def test_soc2_aws(self) -> None:
        mf = load_mappings("soc2", "aws")
        assert mf.cloud == "aws"
        assert len(mf.mappings) >= 10

    def test_soc2_azure(self) -> None:
        mf = load_mappings("soc2", "azure")
        assert mf.cloud == "azure"
        assert len(mf.mappings) >= 10

    def test_cis_gcp(self) -> None:
        mf = load_mappings("cis_gcp", "gcp")
        assert mf.framework_id == "cis_gcp"
        assert len(mf.mappings) >= 15

    def test_cis_azure(self) -> None:
        mf = load_mappings("cis_azure", "azure")
        assert len(mf.mappings) >= 15

    def test_cis_aws(self) -> None:
        mf = load_mappings("cis_aws", "aws")
        assert len(mf.mappings) >= 15

    def test_caching(self) -> None:
        mf1 = load_mappings("soc2", "gcp")
        mf2 = load_mappings("soc2", "gcp")
        assert mf1 is mf2


class TestGetMappingsForCheck:
    def test_found(self) -> None:
        entries = get_mappings_for_check("soc2", "gcp", "CKV_GCP_1")
        assert len(entries) >= 1
        assert entries[0].check_id == "CKV_GCP_1"

    def test_not_found(self) -> None:
        entries = get_mappings_for_check("soc2", "gcp", "CKV_NONEXISTENT_999")
        assert entries == []


class TestGetMappingsForControl:
    def test_found(self) -> None:
        entries = get_mappings_for_control("soc2", "gcp", "CC6.1")
        assert len(entries) >= 1
        assert all(e.control_id == "CC6.1" for e in entries)

    def test_not_found(self) -> None:
        entries = get_mappings_for_control("soc2", "gcp", "NONEXISTENT_CTRL")
        assert entries == []


# -------------------------------------------------------------------
# Error / edge paths
# -------------------------------------------------------------------


class TestMappingsNotFound:
    def test_missing_yaml(self) -> None:
        with pytest.raises(ProfileBundleError):
            load_mappings("nonexistent_framework", "gcp")

    def test_missing_cloud_dir(self) -> None:
        with pytest.raises(ProfileBundleError):
            load_mappings("soc2", "oracle")


class TestMappingsMalformed:
    def test_invalid_yaml_schema(self, tmp_path: Path) -> None:
        """Triggers ProfileBundleError on malformed YAML."""
        _reset_cache()
        gcp_dir = tmp_path / "mappings" / "gcp"
        gcp_dir.mkdir(parents=True)
        bad_file = gcp_dir / "bad_fw.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad_fw\n"
            "cloud: gcp\n"
            "mappings: not_a_list\n"
        )
        with (
            patch(
                "complyform.core.profiles.mapping_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_mappings("bad_fw", "gcp")

    def test_missing_cloud_field(self, tmp_path: Path) -> None:
        _reset_cache()
        gcp_dir = tmp_path / "mappings" / "gcp"
        gcp_dir.mkdir(parents=True)
        bad_file = gcp_dir / "bad2.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad2\n"
            # missing cloud
            "mappings: []\n"
        )
        with (
            patch(
                "complyform.core.profiles.mapping_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_mappings("bad2", "gcp")

    def test_bad_mapping_entry(self, tmp_path: Path) -> None:
        _reset_cache()
        aws_dir = tmp_path / "mappings" / "aws"
        aws_dir.mkdir(parents=True)
        bad_file = aws_dir / "bad3.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad3\n"
            "cloud: aws\n"
            "mappings:\n"
            "  - check_id: 123\n"  # strict: int not str
        )
        with (
            patch(
                "complyform.core.profiles.mapping_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_mappings("bad3", "aws")
