"""Thread-based receipt chain index using DuckDB for O(1) parent lookups."""
from __future__ import annotations

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

from config.paths import DATA_ROOT

__all__ = ["update_chain_index", "lookup_chain_head", "verify_chain"]

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


def _get_connection(tenant: str, *, root: Optional[Path] = None) -> Tuple[duckdb.DuckDBPyConnection, Lock]:
    """Get or create thread-safe DuckDB connection for tenant."""
    if tenant in _CONNECTION_CACHE:
        return _CONNECTION_CACHE[tenant]
    
    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 / f"receipt_index_{tenant}.db"
    conn = duckdb.connect(str(db_path))
    
    # Create index table if not exists
    conn.execute("""
        CREATE TABLE IF NOT EXISTS chain_heads (
            thread_id VARCHAR PRIMARY KEY,
            kind VARCHAR NOT NULL,
            last_hash VARCHAR NOT NULL,
            last_receipt_id VARCHAR NOT NULL,
            last_timestamp TIMESTAMP NOT NULL,
            record_count INTEGER NOT NULL DEFAULT 1,
            updated_at TIMESTAMP NOT NULL
        )
    """)
    conn.execute("CREATE INDEX IF NOT EXISTS idx_kind ON chain_heads(kind)")
    # Note: Removed index on last_timestamp to allow updates via ON CONFLICT
    
    lock = Lock()
    _CONNECTION_CACHE[tenant] = (conn, lock)
    return conn, lock


def update_chain_index(
    tenant: str,
    thread_id: str,
    kind: str,
    receipt_id: str,
    receipt_hash: str,
    timestamp: datetime,
    *,
    root: Optional[Path] = None,
) -> None:
    """
    Atomically update the chain head for a thread.
    
    Args:
        tenant: Tenant identifier for index partitioning
        thread_id: Thread identifier (claim_id, pa_request_id, fraud_case_id, etc.)
        kind: Receipt kind/type for additional filtering
        receipt_id: Current receipt ID
        receipt_hash: Current receipt hash
        timestamp: Receipt timestamp
        root: Optional override for DATA_ROOT
    """
    conn, lock = _get_connection(tenant, root=root)
    now = datetime.now(timezone.utc)
    
    with lock:
        conn.execute("""
            INSERT INTO chain_heads (thread_id, kind, last_hash, last_receipt_id, last_timestamp, record_count, updated_at)
            VALUES (?, ?, ?, ?, ?, 1, ?)
            ON CONFLICT (thread_id) DO UPDATE SET
                last_hash = EXCLUDED.last_hash,
                last_receipt_id = EXCLUDED.last_receipt_id,
                last_timestamp = EXCLUDED.last_timestamp,
                record_count = chain_heads.record_count + 1,
                updated_at = EXCLUDED.updated_at
        """, [thread_id, kind, receipt_hash, receipt_id, timestamp, now])


def lookup_chain_head(
    tenant: str,
    thread_id: str,
    *,
    root: Optional[Path] = None,
) -> Optional[dict]:
    """
    Look up the current chain head for a thread.
    
    Returns:
        Dict with last_hash, last_receipt_id, last_timestamp, record_count if found, None otherwise.
    """
    conn, lock = _get_connection(tenant, root=root)
    
    with lock:
        result = conn.execute(
            "SELECT last_hash, last_receipt_id, last_timestamp, record_count FROM chain_heads WHERE thread_id = ?",
            [thread_id]
        ).fetchone()
    
    if result is None:
        return None
    
    return {
        "last_hash": result[0],
        "last_receipt_id": result[1],
        "last_timestamp": result[2],
        "record_count": result[3],
    }


def verify_chain(
    tenant: str,
    thread_id: str,
    *,
    root: Optional[Path] = None,
) -> Tuple[bool, Optional[str]]:
    """
    Verify chain integrity for a thread.
    
    Returns:
        (is_valid, error_message)
    """
    head = lookup_chain_head(tenant, thread_id, root=root)
    if head is None:
        return False, f"thread_id={thread_id} not found in index"
    
    # Basic sanity checks
    if not head["last_hash"]:
        return False, "missing last_hash"
    if not head["last_receipt_id"]:
        return False, "missing last_receipt_id"
    if head["record_count"] < 1:
        return False, f"invalid record_count={head['record_count']}"
    
    return True, None


def get_thread_stats(tenant: str, *, root: Optional[Path] = None) -> dict:
    """Get aggregate statistics for all threads."""
    conn, lock = _get_connection(tenant, root=root)
    
    with lock:
        result = conn.execute("""
            SELECT 
                COUNT(*) as thread_count,
                SUM(record_count) as total_receipts,
                AVG(record_count) as avg_chain_length,
                MAX(record_count) as max_chain_length,
                MIN(last_timestamp) as earliest_receipt,
                MAX(last_timestamp) as latest_receipt
            FROM chain_heads
        """).fetchone()
    
    if result is None or result[0] == 0:
        return {
            "thread_count": 0,
            "total_receipts": 0,
            "avg_chain_length": 0.0,
            "max_chain_length": 0,
            "earliest_receipt": None,
            "latest_receipt": None,
        }
    
    return {
        "thread_count": result[0],
        "total_receipts": result[1],
        "avg_chain_length": float(result[2]) if result[2] else 0.0,
        "max_chain_length": result[3],
        "earliest_receipt": result[4],
        "latest_receipt": result[5],
    }
