# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
OSINT Authorization Gate (Constitution Article 8)
"The Public Record Is Not a License"

Pre-execution hook that blocks OSINT tools unless the operator has
explicitly authorized the target via confirm_osint_target().

Extracted from tool_executor.py for clarity and testability.
"""

import logging

logger = logging.getLogger(__name__)

# Tools that require explicit per-target authorization before execution.
# Internal/private RFC-1918 targets are exempt from external recon gates.
OSINT_GATED_TOOLS: frozenset = frozenset(
    {
        "harvest_emails",
        "enumerate_subdomains",
        "shodan_lookup",
        "certificate_transparency",
        "search_breach_data",
        "start_spiderfoot_scan",
    }
)

# scan_network_ports is gated only for external (non-RFC-1918) targets — handled inline.
_PRIVATE_CIDR_PREFIXES = (
    "10.",
    "172.16.",
    "172.17.",
    "172.18.",
    "172.19.",
    "172.20.",
    "172.21.",
    "172.22.",
    "172.23.",
    "172.24.",
    "172.25.",
    "172.26.",
    "172.27.",
    "172.28.",
    "172.29.",
    "172.30.",
    "172.31.",
    "192.168.",
    "127.",
    "::1",
    "localhost",
)


def _is_private_target(target: str) -> bool:
    """Return True if target is a private/loopback address (RFC-1918)."""
    t = (target or "").lower().strip()
    return any(t.startswith(prefix) for prefix in _PRIVATE_CIDR_PREFIXES)


def _check_osint_authorization(tool_name: str, tool_input: dict, session) -> str:
    """
    Pre-execution hook: block OSINT tools that lack explicit authorization.

    Returns an error string if blocked, empty string if allowed.
    Checks a SQLite authorization cache (1-hour TTL) keyed by target.
    """
    # scan_network_ports: only gate external targets
    if tool_name == "scan_network_ports":
        target = (tool_input or {}).get("target", "")
        if _is_private_target(target):
            return ""  # internal network — allowed

    elif tool_name not in OSINT_GATED_TOOLS:
        return ""  # not an OSINT-gated tool

    # Extract the target from tool input
    target = ""
    for key in ("target", "domain", "ip", "email", "query"):
        target = (tool_input or {}).get(key, "")
        if target:
            break

    if not target:
        return ""  # no target identifiable — allow (tool will fail with its own error)

    # Check authorization cache
    try:
        from familiar.skills.osint.auth import is_authorized

        if is_authorized(target, tool_name):
            return ""
    except ImportError:
        # Auth module not yet initialized — deny by default on first use
        pass
    except Exception as e:
        logger.warning("OSINT auth check error: %s", e)

    return (
        f"\u26a0\ufe0f OSINT Authorization Required (Constitution Article 8).\n"
        f"Tool '{tool_name}' targets '{target}' — an external entity requiring "
        f"explicit authorization.\n"
        f"The public record is not a license. Call confirm_osint_target(target='{target}', "
        f"justification='<reason>', scope='<scope>') first to establish authorization."
    )
