# 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 Cache

SQLite-backed authorization store for OSINT target consent.
Implements the enforcement layer of Constitution Article 8:
"The Public Record Is Not a License"

Each authorization record stores: target, justification, scope,
the tool(s) authorized, and a 1-hour expiry.
"""

import logging
import sqlite3
import time
from pathlib import Path

logger = logging.getLogger(__name__)

AUTH_TTL_SECONDS = 3600  # 1 hour


def _get_db_path() -> Path:
    try:
        from familiar.core.paths import DATA_DIR

        db_dir = DATA_DIR / "osint"
    except ImportError:
        db_dir = Path.home() / ".familiar" / "data" / "osint"
    db_dir.mkdir(parents=True, exist_ok=True)
    return db_dir / "auth.db"


def _get_conn() -> sqlite3.Connection:
    conn = sqlite3.connect(str(_get_db_path()), timeout=10)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("""
        CREATE TABLE IF NOT EXISTS authorizations (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            target      TEXT NOT NULL,
            tool_scope  TEXT NOT NULL DEFAULT '*',
            justification TEXT NOT NULL,
            scope       TEXT NOT NULL,
            created_at  REAL NOT NULL,
            expires_at  REAL NOT NULL
        )
    """)
    conn.execute("CREATE INDEX IF NOT EXISTS idx_auth_target ON authorizations(target, expires_at)")
    conn.commit()
    return conn


def is_authorized(target: str, tool_name: str = "*") -> bool:
    """Return True if an unexpired authorization exists for this target and tool."""
    now = time.time()
    try:
        conn = _get_conn()
        with conn:
            row = conn.execute(
                """
                SELECT id FROM authorizations
                WHERE target = ?
                  AND expires_at > ?
                  AND (tool_scope = '*' OR tool_scope = ?)
                LIMIT 1
                """,
                (target.lower().strip(), now, tool_name),
            ).fetchone()
        return row is not None
    except Exception as e:
        logger.warning("OSINT auth lookup failed: %s", e)
        return False


def authorize_target(
    target: str,
    justification: str,
    scope: str,
    tool_scope: str = "*",
    ttl: int = AUTH_TTL_SECONDS,
) -> dict:
    """
    Record explicit authorization for an OSINT target.

    Returns a dict with the authorization record details.
    """
    now = time.time()
    expires = now + ttl
    target = target.lower().strip()

    try:
        conn = _get_conn()
        with conn:
            cursor = conn.execute(
                """
                INSERT INTO authorizations
                    (target, tool_scope, justification, scope, created_at, expires_at)
                VALUES (?, ?, ?, ?, ?, ?)
                """,
                (target, tool_scope, justification, scope, now, expires),
            )
            auth_id = cursor.lastrowid
        logger.info(
            "OSINT authorization granted: target=%s scope=%s ttl=%ds",
            target,
            scope,
            ttl,
        )
        return {
            "id": auth_id,
            "target": target,
            "justification": justification,
            "scope": scope,
            "tool_scope": tool_scope,
            "expires_in": ttl,
            "expires_at_iso": _epoch_to_iso(expires),
        }
    except Exception as e:
        logger.error("OSINT authorize_target failed: %s", e)
        raise


def list_authorizations(include_expired: bool = False) -> list[dict]:
    """List authorization records."""
    now = time.time()
    try:
        conn = _get_conn()
        with conn:
            if include_expired:
                rows = conn.execute(
                    "SELECT * FROM authorizations ORDER BY expires_at DESC"
                ).fetchall()
            else:
                rows = conn.execute(
                    "SELECT * FROM authorizations WHERE expires_at > ? ORDER BY expires_at DESC",
                    (now,),
                ).fetchall()
        return [dict(r) for r in rows]
    except Exception as e:
        logger.warning("OSINT list_authorizations failed: %s", e)
        return []


def revoke_target(target: str) -> int:
    """Revoke all authorizations for a target. Returns count removed."""
    target = target.lower().strip()
    try:
        conn = _get_conn()
        with conn:
            cursor = conn.execute("DELETE FROM authorizations WHERE target = ?", (target,))
            return cursor.rowcount
    except Exception as e:
        logger.warning("OSINT revoke_target failed: %s", e)
        return 0


def purge_expired() -> int:
    """Delete expired authorization records. Returns count removed."""
    now = time.time()
    try:
        conn = _get_conn()
        with conn:
            cursor = conn.execute("DELETE FROM authorizations WHERE expires_at <= ?", (now,))
            return cursor.rowcount
    except Exception as e:
        logger.warning("OSINT purge_expired failed: %s", e)
        return 0


def _epoch_to_iso(epoch: float) -> str:
    import datetime

    return datetime.datetime.fromtimestamp(epoch, tz=datetime.timezone.utc).strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
