"""SQLite metadata repository for the first mySecondBrain26 ingestion slice."""

from __future__ import annotations

import hashlib
import json
import logging
import time
from typing import Any
from uuid import uuid4

from cyberbrain26.data_plane.sqlite_store import SQLiteDatabase
from cyberbrain26.knowledge_plane.chunker import TextChunker
from cyberbrain26.knowledge_plane.contracts import (
    DocumentRegistration,
    DocumentStatus,
    DocumentTrust,
    KnowledgeDocument,
    Sensitivity,
)
from cyberbrain26.knowledge_plane.embedding_service import EmbeddingService
from cyberbrain26.knowledge_plane.vector_store import KnowledgeVectorStore, VectorDocument
from cyberbrain26.security_plane.audit import AuditEventType, AuditLog

logger = logging.getLogger(__name__)


class SQLiteKnowledgeRepository:
    """Persist document metadata without persisting source bytes.

    The unique source-and-content hash is the idempotency boundary: re-scanning
    an unchanged source returns the existing document instead of creating a
    duplicate.  A changed source has another digest and becomes a separate,
    traceable version.
    """

    def __init__(self, database: SQLiteDatabase, *, audit_log: AuditLog | None = None) -> None:
        self.database = database
        self.audit_log = audit_log

    def register(
        self,
        registration: DocumentRegistration,
        *,
        vector_store: KnowledgeVectorStore | None = None,
        embedding_service: EmbeddingService | None = None,
    ) -> tuple[KnowledgeDocument, bool]:
        self._validate_registration(registration)
        source = registration.source
        digest = hashlib.sha256(registration.content).hexdigest()
        with self.database.transaction() as connection:
            row = connection.execute(
                "SELECT * FROM knowledge_documents WHERE source_id = ? AND content_sha256 = ?",
                (source.source_id, digest),
            ).fetchone()
            if row is not None:
                return self._row_to_document(row), False

            now = time.time()
            document = KnowledgeDocument(
                document_id=f"document_{uuid4().hex}",
                source_id=source.source_id,
                source_uri=source.source_uri,
                content_sha256=digest,
                media_type=registration.media_type,
                trust=source.trust,
                sensitivity=source.sensitivity,
                status=DocumentStatus.REGISTERED,
                metadata=dict(registration.metadata),
                created_at=now,
                updated_at=now,
            )
            connection.execute(
                """
                INSERT INTO knowledge_documents(
                    document_id, source_id, source_uri, content_sha256, media_type,
                    trust_level, sensitivity, status, metadata_json, created_at, updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    document.document_id,
                    document.source_id,
                    document.source_uri,
                    document.content_sha256,
                    document.media_type,
                    document.trust.value,
                    document.sensitivity.value,
                    document.status.value,
                    json.dumps(document.metadata, separators=(",", ":"), allow_nan=False),
                    document.created_at,
                    document.updated_at,
                ),
            )
        if self.audit_log is not None:
            self.audit_log.record(
                AuditEventType.MEMORY_UPDATED,
                action="knowledge_document_registered",
                resource=document.document_id,
                memory_write=document.document_id,
                metadata={
                    "source_id": document.source_id,
                    "content_sha256": document.content_sha256,
                    "trust_level": document.trust.value,
                    "sensitivity": document.sensitivity.value,
                },
            )

        if vector_store is not None and embedding_service is not None:
            try:
                chunks = TextChunker().chunk_text(
                    registration.content.decode("utf-8", errors="replace"),
                    source_path=document.source_uri,
                )
                vectors = embedding_service.embed_texts([c.text for c in chunks])
                vector_docs = [
                    VectorDocument(
                        id=KnowledgeVectorStore._chunk_id(c.source_path, c.chunk_index),
                        vector=v,
                        text=c.text,
                        source_id=document.source_id,
                        path=c.source_path,
                        mtime=c.mtime,
                        sha256=c.sha256 or document.content_sha256,
                        chunk_index=c.chunk_index,
                    )
                    for c, v in zip(chunks, vectors)
                ]
                vector_store.add_documents(vector_docs)
                # Dokument je po úspěšném upsertu do vektorového úložiště indexován
                with self.database.transaction() as connection:
                    connection.execute(
                        "UPDATE knowledge_documents SET status = ?, updated_at = ? WHERE document_id = ?",
                        (DocumentStatus.INDEXED.value, time.time(), document.document_id),
                    )
            except Exception:
                logger.exception("Embedding/vector store selhal, registrace zůstává v SQLite.")

        return document, True

    def get(self, document_id: str) -> KnowledgeDocument | None:
        with self.database.connection() as connection:
            row = connection.execute(
                "SELECT * FROM knowledge_documents WHERE document_id = ?", (document_id,)
            ).fetchone()
        return self._row_to_document(row) if row is not None else None

    @staticmethod
    def _validate_registration(registration: DocumentRegistration) -> None:
        source = registration.source
        if not source.approved:
            raise PermissionError(f"knowledge source '{source.source_id}' is not approved")
        if not source.source_id.strip() or not source.source_uri.strip():
            raise ValueError("knowledge source_id and source_uri must not be empty")
        if not registration.content:
            raise ValueError("document content must not be empty")
        if not registration.media_type.strip():
            raise ValueError("media_type must not be empty")

    @staticmethod
    def _row_to_document(row: Any) -> KnowledgeDocument:
        return KnowledgeDocument(
            document_id=row["document_id"],
            source_id=row["source_id"],
            source_uri=row["source_uri"],
            content_sha256=row["content_sha256"],
            media_type=row["media_type"],
            trust=DocumentTrust(row["trust_level"]),
            sensitivity=Sensitivity(row["sensitivity"]),
            status=DocumentStatus(row["status"]),
            metadata=json.loads(row["metadata_json"]),
            created_at=row["created_at"],
            updated_at=row["updated_at"],
        )


__all__ = ["SQLiteKnowledgeRepository"]
