"""LanceDB vector store pro knowledge plane."""

from __future__ import annotations

import hashlib
import logging
from dataclasses import dataclass, field
from typing import Any

import lancedb
import pyarrow as pa  # type: ignore[import-untyped]
from lancedb import DBConnection

from cyberbrain26.knowledge_plane.embedding_config import EmbeddingConfig

logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class VectorDocument:
    """Jeden záznam ve vector store."""

    id: str
    vector: list[float]
    text: str
    source_id: str
    path: str
    mtime: float
    sha256: str
    chunk_index: int
    metadata: dict[str, Any] = field(default_factory=dict)


class KnowledgeVectorStore:
    """Uložiště vektorů nad LanceDB pro knowledge plane."""

    def __init__(
        self,
        config: EmbeddingConfig | None = None,
        connection: DBConnection | None = None,
    ) -> None:
        self.config = config or EmbeddingConfig()
        self.config.lancedb_directory.mkdir(parents=True, exist_ok=True)
        self._connection = connection or lancedb.connect(
            str(self.config.lancedb_directory)
        )
        self._table_name = "knowledge_documents"
        self._table = self._get_or_create_table()

    def _get_or_create_table(self) -> Any:
        """Vrátí existující tabulku nebo vytvoří novou."""
        if self._table_name in self._connection.list_tables().tables:
            return self._connection.open_table(self._table_name)
        schema = pa.schema([
            pa.field("id", pa.string()),
            pa.field("vector", pa.list_(pa.float32(), list_size=self.config.vector_dim)),
            pa.field("text", pa.string()),
            pa.field("source_id", pa.string()),
            pa.field("path", pa.string()),
            pa.field("mtime", pa.float64()),
            pa.field("sha256", pa.string()),
            pa.field("chunk_index", pa.int64()),
            pa.field("metadata", pa.string()),
        ])
        return self._connection.create_table(
            self._table_name,
            schema=schema,
            mode="overwrite",
        )

    @staticmethod
    def _chunk_id(path: str, chunk_index: int) -> str:
        """Stabilní ID pro chunk."""
        raw = f"{path}:{chunk_index}"
        return hashlib.sha256(raw.encode()).hexdigest()

    def add_documents(self, documents: list[VectorDocument]) -> None:
        """Přidá dokumenty do vector store."""
        if not documents:
            return
        rows: list[dict[str, Any]] = []
        for doc in documents:
            rows.append(
                {
                    "id": doc.id,
                    "vector": doc.vector,
                    "text": doc.text,
                    "source_id": doc.source_id,
                    "path": doc.path,
                    "mtime": doc.mtime,
                    "sha256": doc.sha256,
                    "chunk_index": doc.chunk_index,
                    "metadata": str(doc.metadata),
                }
            )
        self._table.add(rows)
        logger.info("Přidáno %d vektorů do %s", len(rows), self._table_name)

    def search(self, query_vector: list[float], limit: int = 10) -> list[dict[str, Any]]:
        """Sémantické vyhledání podle vektoru."""
        if len(query_vector) != self.config.vector_dim:
            raise ValueError(
                f"Query vektor má {len(query_vector)} dimenzí, "
                f"očekávám {self.config.vector_dim}"
            )
        results = (
            self._table.search(query_vector)
            .limit(limit)
            .to_list()
        )
        return results

    def count(self) -> int:
        """Počet vektorů v tabulce."""
        return self._table.count_rows()

    def delete_by_path(self, path: str) -> None:
        """Smaže všechny chunky pro danou cestu."""
        self._table.delete(f'path = "{path}"')

    def close(self) -> None:
        """Uzavře připojení k LanceDB."""
        try:
            close = getattr(self._connection, "close", None)
            if callable(close):
                close()
        except Exception as e:  # noqa: BLE001
            logger.debug("Chyba při uzavírání připojení k LanceDB: %s", e)
