"""
Unit tests for osint_gate — OSINT Authorization Gate (Constitution Article 8).
Tests private target detection, gated tool identification, and authorization checks.
"""

from unittest.mock import MagicMock, patch

import pytest

from familiar.core.osint_gate import (
    _PRIVATE_CIDR_PREFIXES,
    OSINT_GATED_TOOLS,
    _check_osint_authorization,
    _is_private_target,
)


class TestIsPrivateTarget:
    """RFC-1918 private target detection."""

    @pytest.mark.parametrize(
        "target",
        [
            "10.0.0.1",
            "10.255.255.255",
            "172.16.0.1",
            "172.31.255.255",
            "192.168.0.1",
            "192.168.1.100",
            "127.0.0.1",
            "127.0.0.2",
            "localhost",
            "LOCALHOST",
            "::1",
        ],
    )
    def test_private_targets(self, target):
        assert _is_private_target(target) is True

    @pytest.mark.parametrize(
        "target",
        [
            "8.8.8.8",
            "1.1.1.1",
            "203.0.113.1",
            "example.com",
            "172.32.0.1",
            "11.0.0.1",
            "",
        ],
    )
    def test_public_targets(self, target):
        assert _is_private_target(target) is False

    def test_none_target(self):
        assert _is_private_target(None) is False

    def test_whitespace_handling(self):
        assert _is_private_target("  10.0.0.1  ") is True

    def test_case_insensitive(self):
        assert _is_private_target("LOCALHOST") is True


class TestOsintGatedTools:
    """OSINT_GATED_TOOLS registry."""

    def test_is_frozenset(self):
        assert isinstance(OSINT_GATED_TOOLS, frozenset)

    def test_contains_expected_tools(self):
        expected = {
            "harvest_emails",
            "enumerate_subdomains",
            "shodan_lookup",
            "certificate_transparency",
            "search_breach_data",
            "start_spiderfoot_scan",
        }
        assert expected == OSINT_GATED_TOOLS

    def test_scan_network_ports_not_in_set(self):
        """scan_network_ports is handled separately (only external targets)."""
        assert "scan_network_ports" not in OSINT_GATED_TOOLS


class TestCheckOsintAuthorization:
    """Pre-execution authorization gate."""

    def test_non_osint_tool_allowed(self):
        result = _check_osint_authorization("web_search", {"query": "cats"}, MagicMock())
        assert result == ""

    def test_scan_ports_private_allowed(self):
        result = _check_osint_authorization(
            "scan_network_ports", {"target": "192.168.1.1"}, MagicMock()
        )
        assert result == ""

    def test_scan_ports_external_blocked(self):
        result = _check_osint_authorization(
            "scan_network_ports", {"target": "8.8.8.8"}, MagicMock()
        )
        assert "Authorization Required" in result

    def test_gated_tool_blocked_without_auth(self):
        result = _check_osint_authorization("shodan_lookup", {"target": "example.com"}, MagicMock())
        assert "Authorization Required" in result
        assert "example.com" in result

    def test_gated_tool_allowed_with_auth(self):
        with patch("familiar.skills.osint.auth.is_authorized", return_value=True):
            result = _check_osint_authorization(
                "shodan_lookup", {"target": "example.com"}, MagicMock()
            )
            assert result == ""

    def test_no_target_allows(self):
        """No identifiable target → allow (tool will fail with its own error)."""
        result = _check_osint_authorization("harvest_emails", {}, MagicMock())
        assert result == ""

    def test_extracts_domain_key(self):
        result = _check_osint_authorization(
            "enumerate_subdomains", {"domain": "example.org"}, MagicMock()
        )
        assert "example.org" in result

    def test_extracts_email_key(self):
        result = _check_osint_authorization(
            "search_breach_data", {"email": "test@example.com"}, MagicMock()
        )
        assert "test@example.com" in result

    def test_auth_import_error_denies(self):
        """When auth module not available, deny by default."""
        with patch.dict("sys.modules", {"familiar.skills.osint.auth": None}):
            result = _check_osint_authorization(
                "shodan_lookup", {"target": "example.com"}, MagicMock()
            )
            assert "Authorization Required" in result


class TestBackwardCompat:
    """Imports from tool_executor still work."""

    def test_import_from_tool_executor(self):
        from familiar.core.tool_executor import (
            _PRIVATE_CIDR_PREFIXES as te_prefixes,
        )
        from familiar.core.tool_executor import (
            OSINT_GATED_TOOLS as TE_GATED,
        )
        from familiar.core.tool_executor import (
            _check_osint_authorization as te_check,
        )
        from familiar.core.tool_executor import (
            _is_private_target as te_private,
        )

        assert TE_GATED is OSINT_GATED_TOOLS
        assert te_private is _is_private_target
        assert te_check is _check_osint_authorization
        assert te_prefixes is _PRIVATE_CIDR_PREFIXES
