"""Idempotency tracking for API endpoints."""
from __future__ import annotations

import duckdb
from pathlib import Path
from typing import Optional, Tuple
from datetime import datetime, timedelta, timezone
from threading import Lock

from config.paths import DATA_ROOT

__all__ = ["check_idempotency", "record_idempotency", "IdempotencyConflict"]

_CONNECTION_CACHE: dict[str, Tuple[duckdb.DuckDBPyConnection, Lock]] = {}


class IdempotencyConflict(Exception):
    """Raised when an idempotent operation is repeated with the same key."""
    
    def __init__(self, existing_receipt_id: str, existing_ts: datetime):
        self.existing_receipt_id = existing_receipt_id
        self.existing_ts = existing_ts
        super().__init__(f"Operation already completed: {existing_receipt_id} at {existing_ts}")


def _get_connection(*, root: Optional[Path] = None) -> Tuple[duckdb.DuckDBPyConnection, Lock]:
    """Get or create thread-safe DuckDB connection for idempotency tracking."""
    cache_key = "global"
    if cache_key in _CONNECTION_CACHE:
        return _CONNECTION_CACHE[cache_key]
    
    base_path = Path(root or DATA_ROOT)
    index_dir = base_path / "receipts" / "indexes"
    index_dir.mkdir(parents=True, exist_ok=True)
    
    db_path = index_dir / "idempotency.db"
    conn = duckdb.connect(str(db_path))
    
    # Create idempotency table
    conn.execute("""
        CREATE TABLE IF NOT EXISTS idempotency_keys (
            endpoint VARCHAR NOT NULL,
            resource_id VARCHAR NOT NULL,
            idempotency_key VARCHAR NOT NULL,
            receipt_id VARCHAR NOT NULL,
            created_at TIMESTAMP NOT NULL,
            expires_at TIMESTAMP NOT NULL,
            PRIMARY KEY (endpoint, resource_id, idempotency_key)
        )
    """)
    conn.execute("CREATE INDEX IF NOT EXISTS idx_expires ON idempotency_keys(expires_at)")
    
    lock = Lock()
    _CONNECTION_CACHE[cache_key] = (conn, lock)
    return conn, lock


def check_idempotency(
    endpoint: str,
    resource_id: str,
    idempotency_key: str,
    *,
    root: Optional[Path] = None,
) -> Optional[str]:
    """
    Check if an operation with this idempotency key was already completed.
    
    Returns:
        Existing receipt_id if found, None otherwise.
    """
    conn, lock = _get_connection(root=root)
    
    with lock:
        # Clean up expired entries first
        conn.execute("DELETE FROM idempotency_keys WHERE expires_at < ?", [datetime.now(timezone.utc)])
        
        result = conn.execute(
            """
            SELECT receipt_id, created_at 
            FROM idempotency_keys 
            WHERE endpoint = ? AND resource_id = ? AND idempotency_key = ?
            """,
            [endpoint, resource_id, idempotency_key]
        ).fetchone()
    
    if result:
        return result[0]  # Return existing receipt_id
    return None


def record_idempotency(
    endpoint: str,
    resource_id: str,
    idempotency_key: str,
    receipt_id: str,
    *,
    ttl_hours: int = 24,
    root: Optional[Path] = None,
) -> None:
    """
    Record an idempotency key for a completed operation.
    
    Args:
        endpoint: API endpoint identifier (e.g., "review")
        resource_id: Resource being acted upon (e.g., receipt_id being reviewed)
        idempotency_key: Client-provided idempotency key
        receipt_id: Receipt ID of the completed operation
        ttl_hours: How long to retain this idempotency record (default 24h)
        root: Optional override for DATA_ROOT
    """
    conn, lock = _get_connection(root=root)
    now = datetime.now(timezone.utc)
    expires_at = now + timedelta(hours=ttl_hours)
    
    with lock:
        try:
            conn.execute(
                """
                INSERT INTO idempotency_keys (endpoint, resource_id, idempotency_key, receipt_id, created_at, expires_at)
                VALUES (?, ?, ?, ?, ?, ?)
                """,
                [endpoint, resource_id, idempotency_key, receipt_id, now, expires_at]
            )
        except duckdb.ConstraintException:
            # Already exists - fetch it for the conflict exception
            result = conn.execute(
                """
                SELECT receipt_id, created_at 
                FROM idempotency_keys 
                WHERE endpoint = ? AND resource_id = ? AND idempotency_key = ?
                """,
                [endpoint, resource_id, idempotency_key]
            ).fetchone()
            if result:
                raise IdempotencyConflict(existing_receipt_id=result[0], existing_ts=result[1])


def cleanup_expired(*, root: Optional[Path] = None) -> int:
    """Remove expired idempotency keys. Returns count of deleted records."""
    conn, lock = _get_connection(root=root)
    
    with lock:
        result = conn.execute(
            "DELETE FROM idempotency_keys WHERE expires_at < ? RETURNING *",
            [datetime.now(timezone.utc)]
        )
        return len(result.fetchall())
