"""Tests for complyform.core.auth.tier_enforcement."""

from __future__ import annotations

import pytest

from complyform.core.auth.tier_config import TIER_CONFIG
from complyform.core.auth.tier_enforcement import (
    TierInfo,
    check_batch_limit,
    check_discover_limit,
    check_feature,
    check_framework_access,
    check_scan_source,
    default_community_tier_info,
)
from complyform.core.errors import (
    BatchOperationTierError,
    BatchSizeLimitError,
    DiscoverTooManyStatesError,
    FeatureNotAvailableError,
    RemediationTierError,
    ScanSourceTierError,
)


def _make_tier_info(tier: str) -> TierInfo:
    """Build TierInfo from TIER_CONFIG for testing."""
    cfg = TIER_CONFIG[tier]

    def _str_list(v: object) -> list[str]:
        return [str(x) for x in v] if isinstance(v, list) else []

    def _opt_int(v: object) -> int | None:
        return v if isinstance(v, int) else None

    return TierInfo(
        tier=tier,
        assess_frameworks=_str_list(cfg["assess_frameworks"]),
        remediate_frameworks=_str_list(cfg["remediate_frameworks"]),
        features=_str_list(cfg["features"]),
        scan_sources=_str_list(cfg["scan_sources"]),
        project_limit=_opt_int(cfg["project_limit"]),
        batch_limit=_opt_int(cfg["batch_limit"]),
        discover_limit=_opt_int(cfg["discover_limit"]),
        expires_at=None,
    )


class TestCheckFrameworkAccess:
    def test_assess_always_passes(self) -> None:
        ti = _make_tier_info("community")
        check_framework_access(["hipaa", "soc2", "fedramp"], "assess", ti)

    @pytest.mark.parametrize("fw", ["soc2", "cis_gcp", "cis_azure", "cis_aws"])
    def test_community_can_remediate_free(self, fw: str) -> None:
        ti = _make_tier_info("community")
        check_framework_access([fw], "remediate", ti)

    def test_community_blocked_from_hipaa_remediate(self) -> None:
        ti = _make_tier_info("community")
        with pytest.raises(RemediationTierError):
            check_framework_access(["hipaa"], "remediate", ti)

    @pytest.mark.parametrize(
        "fw",
        [
            "hipaa",
            "iso27001",
            "pci_dss",
            "gdpr",
            "nist_800_53",
            "nist_csf_2",
            "ccpa",
        ],
    )
    def test_pro_can_remediate_pro_frameworks(self, fw: str) -> None:
        ti = _make_tier_info("pro")
        check_framework_access([fw], "remediate", ti)

    def test_pro_blocked_from_team_framework(self) -> None:
        ti = _make_tier_info("pro")
        with pytest.raises(RemediationTierError):
            check_framework_access(["nis2"], "remediate", ti)

    def test_enterprise_can_remediate_all(self) -> None:
        ti = _make_tier_info("enterprise")
        check_framework_access(["hipaa", "fedramp", "nca_ecc"], "remediate", ti)


class TestCheckScanSource:
    def test_community_local_state_passes(self) -> None:
        ti = _make_tier_info("community")
        check_scan_source("local_state", ti)

    def test_community_remote_blocked(self) -> None:
        ti = _make_tier_info("community")
        with pytest.raises(ScanSourceTierError):
            check_scan_source("gcs", ti)

    def test_pro_remote_passes(self) -> None:
        ti = _make_tier_info("pro")
        check_scan_source("gcs", ti)

    def test_pro_cloud_api_blocked(self) -> None:
        ti = _make_tier_info("pro")
        with pytest.raises(ScanSourceTierError):
            check_scan_source("cloud_api", ti)

    def test_team_cloud_api_passes(self) -> None:
        ti = _make_tier_info("team")
        check_scan_source("cloud_api", ti)


class TestCheckFeature:
    def test_community_html_report_blocked(self) -> None:
        ti = _make_tier_info("community")
        with pytest.raises(FeatureNotAvailableError):
            check_feature("html_report", ti)

    def test_pro_html_report_passes(self) -> None:
        ti = _make_tier_info("pro")
        check_feature("html_report", ti)

    def test_pro_branded_pdf_blocked(self) -> None:
        ti = _make_tier_info("pro")
        with pytest.raises(FeatureNotAvailableError):
            check_feature("branded_pdf", ti)

    def test_agency_branded_pdf_passes(self) -> None:
        ti = _make_tier_info("agency")
        check_feature("branded_pdf", ti)


class TestCheckBatchLimit:
    def test_community_batch_raises_operation_error(self) -> None:
        ti = _make_tier_info("community")
        with pytest.raises(BatchOperationTierError):
            check_batch_limit(1, ti)

    def test_pro_within_limit_passes(self) -> None:
        ti = _make_tier_info("pro")
        check_batch_limit(10, ti)

    def test_pro_exceeds_limit_raises(self) -> None:
        ti = _make_tier_info("pro")
        with pytest.raises(BatchSizeLimitError):
            check_batch_limit(11, ti)

    def test_enterprise_batch_limit(self) -> None:
        """Enterprise has batch_limit=10."""
        ti = _make_tier_info("enterprise")
        assert ti.batch_limit == 10


class TestCheckDiscoverLimit:
    def test_within_limit_passes(self) -> None:
        ti = _make_tier_info("pro")
        check_discover_limit(50, ti)

    def test_exceeds_limit_raises(self) -> None:
        ti = _make_tier_info("pro")
        with pytest.raises(DiscoverTooManyStatesError):
            check_discover_limit(51, ti)

    def test_at_limit_passes(self) -> None:
        ti = _make_tier_info("enterprise_unlimited")
        check_discover_limit(50, ti)


class TestDefaultCommunityTierInfo:
    def test_returns_community(self) -> None:
        ti = default_community_tier_info()
        assert ti.tier == "community"
        assert ti.expires_at is None

    def test_community_scan_sources(self) -> None:
        ti = default_community_tier_info()
        assert ti.scan_sources == ["local_state"]

    def test_community_no_features(self) -> None:
        ti = default_community_tier_info()
        assert ti.features == []
