"""Tests for configuration hierarchy resolver and publisher."""

import uuid

import pytest

from src.services.config_resolver import (
    ALL_CATEGORIES,
    SYSTEM_DEFAULTS,
    _normalise_disclosed_vendor_ids,
    _normalise_enabled_categories,
    build_public_config,
    resolve_config,
)


class TestResolveConfig:
    def test_returns_system_defaults_for_empty_config(self):
        result = resolve_config({})
        assert result["blocking_mode"] == "opt_in"
        assert result["consent_expiry_days"] == 365
        assert result["gcm_enabled"] is True
        assert result["tcf_enabled"] is False
        assert result["gpp_enabled"] is True
        assert result["gpp_supported_apis"] == ["usnat"]
        assert result["gpc_enabled"] is True
        assert result["gpc_jurisdictions"] == [
            "US-CA",
            "US-CO",
            "US-CT",
            "US-TX",
            "US-MT",
        ]
        assert result["gpc_global_honour"] is False

    def test_site_config_overrides_defaults(self):
        site_config = {
            "blocking_mode": "opt_out",
            "consent_expiry_days": 180,
            "tcf_enabled": True,
        }
        result = resolve_config(site_config)
        assert result["blocking_mode"] == "opt_out"
        assert result["consent_expiry_days"] == 180
        assert result["tcf_enabled"] is True
        # Non-overridden values stay as defaults
        assert result["gcm_enabled"] is True

    def test_org_defaults_override_system_defaults(self):
        org_defaults = {"consent_expiry_days": 90}
        result = resolve_config({}, org_defaults=org_defaults)
        assert result["consent_expiry_days"] == 90

    def test_site_config_overrides_org_defaults(self):
        org_defaults = {"consent_expiry_days": 90}
        site_config = {"consent_expiry_days": 30}
        result = resolve_config(site_config, org_defaults=org_defaults)
        assert result["consent_expiry_days"] == 30

    def test_none_values_in_site_config_do_not_override(self):
        site_config = {"blocking_mode": None, "consent_expiry_days": 180}
        result = resolve_config(site_config)
        # None should not override the default
        assert result["blocking_mode"] == "opt_in"
        assert result["consent_expiry_days"] == 180

    def test_regional_override_applied(self):
        site_config = {
            "blocking_mode": "opt_in",
            "regional_modes": {"US-CA": "opt_out", "EU": "opt_in"},
        }
        result = resolve_config(site_config, region="US-CA")
        assert result["blocking_mode"] == "opt_out"

    def test_regional_override_falls_back_to_default(self):
        site_config = {
            "blocking_mode": "opt_in",
            "regional_modes": {"EU": "opt_in", "DEFAULT": "opt_out"},
        }
        result = resolve_config(site_config, region="BR")
        assert result["blocking_mode"] == "opt_out"

    def test_regional_override_no_match_keeps_site_config(self):
        site_config = {
            "blocking_mode": "opt_in",
            "regional_modes": {"EU": "opt_out"},
        }
        result = resolve_config(site_config, region="JP")
        assert result["blocking_mode"] == "opt_in"

    def test_no_region_ignores_regional_modes(self):
        site_config = {
            "blocking_mode": "opt_in",
            "regional_modes": {"US-CA": "opt_out"},
        }
        result = resolve_config(site_config)
        assert result["blocking_mode"] == "opt_in"

    def test_gpp_site_config_overrides_defaults(self):
        site_config = {
            "gpp_enabled": False,
            "gpp_supported_apis": ["usca", "usva"],
        }
        result = resolve_config(site_config)
        assert result["gpp_enabled"] is False
        assert result["gpp_supported_apis"] == ["usca", "usva"]

    def test_gpc_site_config_overrides_defaults(self):
        site_config = {
            "gpc_enabled": False,
            "gpc_global_honour": True,
            "gpc_jurisdictions": ["US-CA"],
        }
        result = resolve_config(site_config)
        assert result["gpc_enabled"] is False
        assert result["gpc_global_honour"] is True
        assert result["gpc_jurisdictions"] == ["US-CA"]

    def test_gpp_gpc_org_defaults_override_system(self):
        org_defaults = {
            "gpp_enabled": False,
            "gpc_global_honour": True,
        }
        result = resolve_config({}, org_defaults=org_defaults)
        assert result["gpp_enabled"] is False
        assert result["gpc_global_honour"] is True
        # Non-overridden GPP/GPC fields stay as system defaults
        assert result["gpc_enabled"] is True

    def test_gpp_gpc_site_overrides_org(self):
        org_defaults = {"gpp_supported_apis": ["usca"]}
        site_config = {"gpp_supported_apis": ["usnat", "usco"]}
        result = resolve_config(site_config, org_defaults=org_defaults)
        assert result["gpp_supported_apis"] == ["usnat", "usco"]

    def test_group_defaults_override_org_defaults(self):
        org_defaults = {"consent_expiry_days": 90, "tcf_enabled": True}
        group_defaults = {"consent_expiry_days": 60}
        result = resolve_config(
            {},
            org_defaults=org_defaults,
            group_defaults=group_defaults,
        )
        assert result["consent_expiry_days"] == 60  # Group overrides org
        assert result["tcf_enabled"] is True  # Still from org

    def test_site_config_overrides_group_defaults(self):
        group_defaults = {"consent_expiry_days": 60}
        site_config = {"consent_expiry_days": 30}
        result = resolve_config(site_config, group_defaults=group_defaults)
        assert result["consent_expiry_days"] == 30  # Site overrides group

    def test_none_in_group_defaults_does_not_override(self):
        org_defaults = {"consent_expiry_days": 90}
        group_defaults = {"consent_expiry_days": None}
        result = resolve_config(
            {},
            org_defaults=org_defaults,
            group_defaults=group_defaults,
        )
        assert result["consent_expiry_days"] == 90  # Org value preserved

    def test_full_hierarchy(self):
        org_defaults = {
            "consent_expiry_days": 90,
            "tcf_enabled": True,
        }
        site_config = {
            "consent_expiry_days": 60,
            "banner_config": {"primaryColour": "#ff0000"},
            "regional_modes": {"EU": "opt_in", "US": "opt_out"},
        }
        result = resolve_config(site_config, org_defaults=org_defaults, region="US")
        assert result["consent_expiry_days"] == 60  # Site overrides org
        assert result["tcf_enabled"] is True  # From org defaults
        assert result["blocking_mode"] == "opt_out"  # Regional override
        assert result["banner_config"] == {"primaryColour": "#ff0000"}

    def test_full_hierarchy_with_group(self):
        org_defaults = {
            "consent_expiry_days": 90,
            "tcf_enabled": True,
            "blocking_mode": "opt_in",
        }
        group_defaults = {
            "consent_expiry_days": 60,
            "privacy_policy_url": "https://group.example.com/privacy",
        }
        site_config = {
            "banner_config": {"primaryColour": "#ff0000"},
            "regional_modes": {"US": "opt_out"},
        }
        result = resolve_config(
            site_config,
            org_defaults=org_defaults,
            group_defaults=group_defaults,
            region="US",
        )
        assert result["consent_expiry_days"] == 60  # From group
        assert result["tcf_enabled"] is True  # From org
        assert result["blocking_mode"] == "opt_out"  # Regional override
        assert result["privacy_policy_url"] == "https://group.example.com/privacy"  # From group
        assert result["banner_config"] == {"primaryColour": "#ff0000"}  # From site


class TestBuildPublicConfig:
    def test_includes_required_fields(self):
        site_id = str(uuid.uuid4())
        resolved = {**SYSTEM_DEFAULTS, "id": "config-123"}
        result = build_public_config(site_id, resolved)

        assert result["site_id"] == site_id
        assert result["id"] == "config-123"
        assert result["blocking_mode"] == "opt_in"
        assert result["consent_expiry_days"] == 365
        assert "gcm_enabled" in result
        assert "tcf_enabled" in result
        assert "banner_config" in result
        assert result["gpp_enabled"] is True
        assert result["gpp_supported_apis"] == ["usnat"]
        assert result["gpc_enabled"] is True
        assert result["gpc_jurisdictions"] == [
            "US-CA",
            "US-CO",
            "US-CT",
            "US-TX",
            "US-MT",
        ]
        assert result["gpc_global_honour"] is False

    def test_strips_unknown_internal_fields(self):
        site_id = str(uuid.uuid4())
        resolved = {
            **SYSTEM_DEFAULTS,
            "id": "",
            "internal_field": "should_not_appear",
            "scan_enabled": True,
        }
        result = build_public_config(site_id, resolved)
        assert "internal_field" not in result
        assert "scan_enabled" not in result


class TestConfigRoutes:
    def test_resolved_config_route_registered(self, app):
        routes = [r.path for r in app.routes]
        assert "/api/v1/config/sites/{site_id}/resolved" in routes

    def test_publish_route_registered(self, app):
        routes = [r.path for r in app.routes]
        assert "/api/v1/config/sites/{site_id}/publish" in routes

    def test_inheritance_route_registered(self, app):
        routes = [r.path for r in app.routes]
        assert "/api/v1/config/sites/{site_id}/inheritance" in routes

    @pytest.mark.asyncio
    async def test_publish_requires_auth(self, client):
        site_id = uuid.uuid4()
        resp = await client.post(f"/api/v1/config/sites/{site_id}/publish")
        assert resp.status_code == 401


class TestEnabledCategories:
    """Cascade semantics for ``enabled_categories``."""

    def test_system_default_is_all_five(self):
        result = resolve_config({})
        assert result["enabled_categories"] == ALL_CATEGORIES

    def test_site_override_narrows_system_default(self):
        result = resolve_config({"enabled_categories": ["necessary", "analytics"]})
        assert result["enabled_categories"] == ["necessary", "analytics"]

    def test_site_override_beats_org_override(self):
        result = resolve_config(
            site_config={"enabled_categories": ["necessary", "marketing"]},
            org_defaults={"enabled_categories": ["necessary", "analytics"]},
        )
        assert result["enabled_categories"] == ["necessary", "marketing"]

    def test_group_override_beats_org_override_when_site_unset(self):
        result = resolve_config(
            site_config={},
            org_defaults={"enabled_categories": ["necessary", "analytics"]},
            group_defaults={"enabled_categories": ["necessary", "functional"]},
        )
        assert result["enabled_categories"] == ["necessary", "functional"]

    def test_unset_site_inherits_org(self):
        result = resolve_config(
            site_config={},
            org_defaults={"enabled_categories": ["necessary", "marketing"]},
        )
        assert result["enabled_categories"] == ["necessary", "marketing"]

    def test_necessary_forced_in_when_missing_from_override(self):
        """Operators can't accidentally drop ``necessary``."""
        result = resolve_config({"enabled_categories": ["analytics", "marketing"]})
        assert "necessary" in result["enabled_categories"]
        assert result["enabled_categories"] == ["necessary", "analytics", "marketing"]

    def test_unknown_slugs_are_stripped(self):
        result = resolve_config({"enabled_categories": ["necessary", "spam", "analytics"]})
        assert result["enabled_categories"] == ["necessary", "analytics"]

    def test_empty_list_falls_back_to_default(self):
        """An empty list is treated as 'no categories configured' → default."""
        result = resolve_config({"enabled_categories": []})
        assert result["enabled_categories"] == ALL_CATEGORIES

    def test_non_list_value_falls_back_to_default(self):
        result = resolve_config({"enabled_categories": "not-a-list"})  # type: ignore[dict-item]
        assert result["enabled_categories"] == ALL_CATEGORIES

    def test_result_is_in_canonical_display_order(self):
        """Insertion order from the cascade must not leak into the output."""
        result = resolve_config({"enabled_categories": ["marketing", "necessary", "analytics"]})
        assert result["enabled_categories"] == ["necessary", "analytics", "marketing"]

    def test_public_config_includes_enabled_categories(self):
        resolved = resolve_config({"enabled_categories": ["necessary", "analytics"]})
        public = build_public_config("site-xyz", resolved)
        assert public["enabled_categories"] == ["necessary", "analytics"]

    def test_normalise_handles_none(self):
        assert _normalise_enabled_categories(None) == ALL_CATEGORIES

    def test_normalise_preserves_explicit_full_list(self):
        assert _normalise_enabled_categories(list(ALL_CATEGORIES)) == ALL_CATEGORIES


class TestDisclosedVendorIds:
    """Cascade and normalisation for the TCF v2.3 DisclosedVendors plumbing."""

    def test_system_default_is_empty(self):
        result = resolve_config({})
        # System default; unconfigured installs ship an empty DisclosedVendors
        # segment (spec-valid, operationally meaningless until populated).
        assert result["disclosed_vendor_ids"] == []

    def test_site_override_takes_precedence(self):
        org = {"disclosed_vendor_ids": [1, 2, 3]}
        group = {"disclosed_vendor_ids": [10, 20]}
        site = {"disclosed_vendor_ids": [100, 200]}
        result = resolve_config(site, org_defaults=org, group_defaults=group)
        assert result["disclosed_vendor_ids"] == [100, 200]

    def test_group_override_takes_precedence_over_org(self):
        org = {"disclosed_vendor_ids": [1, 2]}
        group = {"disclosed_vendor_ids": [10, 20]}
        result = resolve_config({}, org_defaults=org, group_defaults=group)
        assert result["disclosed_vendor_ids"] == [10, 20]

    def test_org_default_inherited_when_site_unset(self):
        org = {"disclosed_vendor_ids": [42, 100]}
        result = resolve_config({}, org_defaults=org)
        assert result["disclosed_vendor_ids"] == [42, 100]

    def test_public_config_normalises_to_sorted_unique_ints(self):
        resolved = resolve_config({"disclosed_vendor_ids": [5, 1, 5, 3, 1]})
        public = build_public_config("site-xyz", resolved)
        assert public["disclosed_vendor_ids"] == [1, 3, 5]

    def test_normalise_strips_non_int_garbage(self):
        # A misconfigured admin save could land strings/booleans/zero/negatives
        # in JSONB. The banner is fed a deterministic shape regardless.
        assert _normalise_disclosed_vendor_ids([1, "2", 0, -3, True, 4.5, 7]) == [1, 7]

    def test_normalise_handles_none(self):
        assert _normalise_disclosed_vendor_ids(None) == []

    def test_normalise_handles_non_list(self):
        assert _normalise_disclosed_vendor_ids("not a list") == []
        assert _normalise_disclosed_vendor_ids(42) == []
        assert _normalise_disclosed_vendor_ids({"1": True}) == []

    def test_normalise_dedupes_and_sorts(self):
        assert _normalise_disclosed_vendor_ids([5, 1, 5, 3, 1, 5]) == [1, 3, 5]


class TestGvlVersionAndPurposes:
    """Public config exposes the new TCF v2.3 plumbing fields."""

    def test_gvl_version_defaults_to_none(self):
        public = build_public_config("site-xyz", resolve_config({}))
        assert public["gvl_version"] is None

    def test_gvl_version_passed_through(self):
        public = build_public_config(
            "site-xyz",
            resolve_config({}),
            gvl_version=157,
        )
        assert public["gvl_version"] == 157

    def test_category_tcf_purposes_defaults_to_empty(self):
        public = build_public_config("site-xyz", resolve_config({}))
        assert public["category_tcf_purposes"] == {}

    def test_category_tcf_purposes_passed_through(self):
        mapping = {"analytics": [7, 8], "marketing": [4, 11]}
        public = build_public_config(
            "site-xyz",
            resolve_config({}),
            category_tcf_purposes=mapping,
        )
        assert public["category_tcf_purposes"] == mapping
