"""Hot store: DuckDB in-memory for recent data."""

from __future__ import annotations

import asyncio
import hashlib
import logging
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

import duckdb

if TYPE_CHECKING:
    from pathlib import Path

    from akosha.models import HotRecord

logger = logging.getLogger(__name__)


class HotStore:
    """Hot store with DuckDB in-memory storage."""

    def __init__(self, database_path: str | Path = ":memory:") -> None:
        """Initialize hot store.

        Args:
            database_path: DuckDB database path (":memory:" for in-memory)
        """
        self.db_path = database_path
        self.conn: duckdb.DuckDBPyConnection | None = None
        self._lock = asyncio.Lock()

    async def initialize(self) -> None:
        """Initialize database schema."""
        async with self._lock:
            self.conn = duckdb.connect(str(self.db_path))

            # Create conversations table with HNSW index support
            self.conn.execute("""
                CREATE TABLE IF NOT EXISTS conversations (
                    system_id VARCHAR,
                    conversation_id VARCHAR PRIMARY KEY,
                    content TEXT,
                    embedding FLOAT[384],
                    timestamp TIMESTAMP,
                    metadata JSON,
                    content_hash VARCHAR,
                    uploaded_at TIMESTAMP DEFAULT NOW()
                )
            """)

            # Create HNSW index for vector search
            try:
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS embedding_hnsw_index
                    ON conversations USING HNSW (embedding)
                    WITH (m = 16, ef_construction = 200)
                """)
            except Exception as e:
                logger.warning(f"HNSW index creation failed: {e}")

            # Create indexes for filtered queries (performance optimization)
            try:
                # Index on system_id for fast filtering
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS system_id_index
                    ON conversations (system_id)
                """)
                logger.info("Created system_id index")
            except Exception as e:
                logger.warning(f"system_id index creation failed: {e}")

            try:
                # Index on timestamp for aging queries
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS timestamp_index
                    ON conversations (timestamp)
                """)
                logger.info("Created timestamp index")
            except Exception as e:
                logger.warning(f"timestamp index creation failed: {e}")

            try:
                # Composite index for system_id + timestamp (common query pattern)
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS system_timestamp_index
                    ON conversations (system_id, timestamp)
                """)
                logger.info("Created composite system_id+timestamp index")
            except Exception as e:
                logger.warning(f"Composite index creation failed: {e}")

            logger.info("Hot store initialized")

    async def insert(self, record: HotRecord) -> None:
        """Insert conversation into hot store.

        Args:
            record: Hot record to insert
        """
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            self.conn.execute(
                """
                INSERT INTO conversations
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            """,
                [
                    record.system_id,
                    record.conversation_id,
                    record.content,
                    record.embedding,
                    record.timestamp,
                    record.metadata,
                    self._compute_content_hash(record.content),
                    datetime.now(UTC),
                ],
            )

    async def search_similar(
        self,
        query_embedding: list[float],
        system_id: str | None = None,
        limit: int = 10,
        threshold: float = 0.7,
    ) -> list[dict[str, Any]]:
        """Search for similar conversations using vector similarity.

        Args:
            query_embedding: Query vector (FLOAT[384])
            system_id: Optional system filter
            limit: Maximum results to return
            threshold: Minimum similarity score (0-1)

        Returns:
            List of similar conversations with metadata
        """
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            # Set HNSW search parameters
            import contextlib

            with contextlib.suppress(Exception):
                self.conn.execute("SET hnsw_ef_search = 100")

            # Build query with parameterized WHERE clause (SQL injection prevention)
            # Note: We use separate queries for each case to ensure proper parameterization
            if system_id:
                query = """
                    SELECT
                        system_id,
                        conversation_id,
                        content,
                        timestamp,
                        metadata,
                        array_cosine_similarity(embedding, ?::FLOAT[384]) as similarity
                    FROM conversations
                    WHERE system_id = ?
                    ORDER BY similarity DESC
                    LIMIT ?
                """
                results = self.conn.execute(query, [query_embedding, system_id, limit]).fetchall()
            else:
                query = """
                    SELECT
                        system_id,
                        conversation_id,
                        content,
                        timestamp,
                        metadata,
                        array_cosine_similarity(embedding, ?::FLOAT[384]) as similarity
                    FROM conversations
                    ORDER BY similarity DESC
                    LIMIT ?
                """
                results = self.conn.execute(query, [query_embedding, limit]).fetchall()

            # Filter by threshold
            return [
                {
                    "system_id": r[0],
                    "conversation_id": r[1],
                    "content": r[2],
                    "timestamp": r[3],
                    "metadata": r[4],
                    "similarity": r[5],
                }
                for r in results
                if r[5] >= threshold
            ]

    @staticmethod
    def _compute_content_hash(content: str) -> str:
        """Compute SHA-256 hash of content."""
        return hashlib.sha256(content.encode("utf-8")).hexdigest()

    async def close(self) -> None:
        """Close database connection."""
        async with self._lock:
            if self.conn:
                self.conn.close()
                logger.info("Hot store closed")

    async def initialize_code_graphs_table(self) -> None:
        """Initialize code_graphs table for cross-repo pattern analysis."""
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            # Create code_graphs table
            self.conn.execute("""
                CREATE TABLE IF NOT EXISTS code_graphs (
                    repo_path VARCHAR,
                    commit_hash VARCHAR,
                    nodes_count INTEGER,
                    graph_data JSON,
                    metadata JSON,
                    ingested_at TIMESTAMP DEFAULT NOW(),
                    PRIMARY KEY (repo_path, commit_hash)
                )
            """)

            # Create indexes for common queries
            try:
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS code_graphs_repo_index
                    ON code_graphs (repo_path)
                """)
                logger.info("Created code_graphs repo_path index")
            except Exception as e:
                logger.warning(f"code_graphs repo_path index creation failed: {e}")

            try:
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS code_graphs_nodes_index
                    ON code_graphs (nodes_count DESC)
                """)
                logger.info("Created code_graphs nodes_count index")
            except Exception as e:
                logger.warning(f"code_graphs nodes_count index creation failed: {e}")

            try:
                self.conn.execute("""
                    CREATE INDEX IF NOT EXISTS code_graphs_ingested_index
                    ON code_graphs (ingested_at DESC)
                """)
                logger.info("Created code_graphs ingested_at index")
            except Exception as e:
                logger.warning(f"code_graphs ingested_at index creation failed: {e}")

            logger.info("Code graphs table initialized")

    async def store_code_graph(
        self,
        repo_path: str,
        commit_hash: str,
        nodes_count: int,
        graph_data: dict[str, Any],
        metadata: dict[str, Any],
    ) -> None:
        """Store a code graph in the hot store.

        Args:
            repo_path: Path to the repository
            commit_hash: Git commit hash
            nodes_count: Number of nodes in the code graph
            graph_data: Complete code graph data (nodes, edges, etc.)
            metadata: Optional metadata dictionary
        """
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            import json

            self.conn.execute(
                """
                INSERT OR REPLACE INTO code_graphs
                (repo_path, commit_hash, nodes_count, graph_data, metadata, ingested_at)
                VALUES (?, ?, ?, ?, ?, ?)
            """,
                [
                    repo_path,
                    commit_hash,
                    nodes_count,
                    json.dumps(graph_data),
                    json.dumps(metadata),
                    datetime.now(UTC),
                ],
            )

    async def get_code_graph(
        self,
        repo_path: str,
        commit_hash: str,
    ) -> dict[str, Any] | None:
        """Get a code graph by repo path and commit hash.

        Args:
            repo_path: Path to the repository
            commit_hash: Git commit hash

        Returns:
            Code graph data or None if not found
        """
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            result = self.conn.execute(
                """
                SELECT repo_path, commit_hash, nodes_count, graph_data, metadata, ingested_at
                FROM code_graphs
                WHERE repo_path = ? AND commit_hash = ?
                """,
                [repo_path, commit_hash],
            ).fetchone()

            if not result or len(result) < 6:
                return None

            import json

            return {
                "repo_path": result[0],
                "commit_hash": result[1],
                "nodes_count": result[2],
                "graph_data": json.loads(result[3]) if result[3] else {},
                "metadata": json.loads(result[4]) if result[4] else {},
                "ingested_at": result[5],
            }

    async def list_code_graphs(
        self,
        repo_path: str | None = None,
        limit: int = 100,
    ) -> list[dict[str, Any]]:
        """List code graphs with optional filtering.

        Args:
            repo_path: Optional repo path filter
            limit: Maximum number of results

        Returns:
            List of code graph summaries
        """
        async with self._lock:
            if not self.conn:
                raise RuntimeError("Hot store not initialized")

            if repo_path:
                results = self.conn.execute(
                    """
                    SELECT repo_path, commit_hash, nodes_count, ingested_at
                    FROM code_graphs
                    WHERE repo_path = ?
                    ORDER BY ingested_at DESC
                    LIMIT ?
                    """,
                    [repo_path, limit],
                ).fetchall()
            else:
                results = self.conn.execute(
                    """
                    SELECT repo_path, commit_hash, nodes_count, ingested_at
                    FROM code_graphs
                    ORDER BY ingested_at DESC
                    LIMIT ?
                    """,
                    [limit],
                ).fetchall()

            return [
                {
                    "repo_path": r[0],
                    "commit_hash": r[1],
                    "nodes_count": r[2],
                    "ingested_at": r[3],
                }
                for r in results
            ]
