"""Tests for complyform.core.profiles.control_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.control_loader import (
    _reset_cache,
    get_control,
    load_controls,
)


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


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


class TestLoadControls:
    def test_soc2(self) -> None:
        cf = load_controls("soc2")
        assert cf.framework_id == "soc2"
        assert cf.schema_version == 1
        assert len(cf.controls) >= 20

    def test_cis_gcp(self) -> None:
        cf = load_controls("cis_gcp")
        assert cf.framework_id == "cis_gcp"
        assert len(cf.controls) >= 20

    def test_cis_azure(self) -> None:
        cf = load_controls("cis_azure")
        assert cf.framework_id == "cis_azure"
        assert len(cf.controls) >= 20

    def test_cis_aws(self) -> None:
        cf = load_controls("cis_aws")
        assert cf.framework_id == "cis_aws"
        assert len(cf.controls) >= 20

    def test_caching(self) -> None:
        cf1 = load_controls("soc2")
        cf2 = load_controls("soc2")
        assert cf1 is cf2


class TestGetControl:
    def test_found(self) -> None:
        c = get_control("soc2", "CC6.1")
        assert c is not None
        assert c.id == "CC6.1"

    def test_not_found_returns_none(self) -> None:
        c = get_control("soc2", "NONEXISTENT_CTRL")
        assert c is None


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


class TestControlsNotFound:
    def test_missing_yaml(self) -> None:
        with pytest.raises(ProfileBundleError):
            load_controls("nonexistent_framework_xyz")


class TestControlsMalformed:
    def test_invalid_yaml_schema(self, tmp_path: Path) -> None:
        """Triggers ProfileBundleError on malformed YAML."""
        _reset_cache()
        controls_dir = tmp_path / "controls"
        controls_dir.mkdir()
        bad_file = controls_dir / "bad_fw.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad_fw\n"
            "framework_version: '1'\n"
            "controls: not_a_list\n"
        )
        with (
            patch(
                "complyform.core.profiles.control_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_controls("bad_fw")

    def test_missing_required_field_in_control(self, tmp_path: Path) -> None:
        _reset_cache()
        controls_dir = tmp_path / "controls"
        controls_dir.mkdir()
        bad_file = controls_dir / "bad2.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad2\n"
            "framework_version: '1'\n"
            "controls:\n"
            "  - id: 123\n"  # strict mode: int not str
        )
        with (
            patch(
                "complyform.core.profiles.control_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_controls("bad2")

    def test_missing_framework_version(self, tmp_path: Path) -> None:
        _reset_cache()
        controls_dir = tmp_path / "controls"
        controls_dir.mkdir()
        bad_file = controls_dir / "bad3.yaml"
        bad_file.write_text(
            "schema_version: 1\n"
            "framework_id: bad3\n"
            # missing framework_version
            "controls: []\n"
        )
        with (
            patch(
                "complyform.core.profiles.control_loader._PROFILES_DIR",
                tmp_path,
            ),
            pytest.raises(ProfileBundleError),
        ):
            load_controls("bad3")
