from sqlalchemy import select
from sqlalchemy.orm import Session, sessionmaker

from zbt.domains.knowledge.models import KnowledgeDocument, KnowledgeSearchTest
from zbt.domains.knowledge.repository import KnowledgeRepository
from zbt.infrastructure.mysql.core_models import (
    KnowledgeDocumentRecord,
    KnowledgeSearchTestRecord,
)

SessionFactory = sessionmaker[Session]


class SqlAlchemyKnowledgeRepository(KnowledgeRepository):
    def __init__(self, session_factory: SessionFactory) -> None:
        self._session_factory = session_factory

    def save_document(self, document: KnowledgeDocument) -> None:
        with self._session_factory() as session:
            session.merge(
                KnowledgeDocumentRecord(
                    id=document.id,
                    title=document.title,
                    document_type=document.document_type,
                    source_name=document.source_name,
                    product_code=document.product_code,
                    content=document.content,
                    status=document.status,
                    version_no=document.version_no,
                    chunk_count=document.chunk_count,
                    created_by=document.created_by,
                    created_at=document.created_at,
                    updated_at=document.updated_at,
                    indexed_at=document.indexed_at,
                    published_at=document.published_at,
                )
            )
            session.commit()

    def get_document(self, document_id: str) -> KnowledgeDocument | None:
        with self._session_factory() as session:
            record = session.get(KnowledgeDocumentRecord, document_id)
            return self._to_domain(record) if record is not None else None

    def list_documents(self) -> list[KnowledgeDocument]:
        with self._session_factory() as session:
            records = session.scalars(
                select(KnowledgeDocumentRecord).order_by(
                    KnowledgeDocumentRecord.created_at.desc()
                )
            ).all()
            return [self._to_domain(record) for record in records]

    def save_search_test(self, search_test: KnowledgeSearchTest) -> None:
        with self._session_factory() as session:
            session.merge(
                KnowledgeSearchTestRecord(
                    id=search_test.id,
                    document_id=search_test.document_id,
                    query=search_test.query,
                    hit_count=search_test.hit_count,
                    top_score=search_test.top_score,
                    passed=search_test.passed,
                    tested_by=search_test.tested_by,
                    created_at=search_test.created_at,
                )
            )
            session.commit()

    def list_search_tests(self, document_id: str) -> list[KnowledgeSearchTest]:
        with self._session_factory() as session:
            records = session.scalars(
                select(KnowledgeSearchTestRecord)
                .where(KnowledgeSearchTestRecord.document_id == document_id)
                .order_by(KnowledgeSearchTestRecord.created_at.desc())
            ).all()
            return [
                KnowledgeSearchTest(
                    id=record.id,
                    document_id=record.document_id,
                    query=record.query,
                    hit_count=record.hit_count,
                    top_score=record.top_score,
                    passed=record.passed,
                    tested_by=record.tested_by,
                    created_at=record.created_at,
                )
                for record in records
            ]

    @staticmethod
    def _to_domain(record: KnowledgeDocumentRecord) -> KnowledgeDocument:
        return KnowledgeDocument(
            id=record.id,
            title=record.title,
            document_type=record.document_type,
            source_name=record.source_name,
            product_code=record.product_code,
            content=record.content,
            status=record.status,
            version_no=record.version_no,
            chunk_count=record.chunk_count,
            created_by=record.created_by,
            created_at=record.created_at,
            updated_at=record.updated_at,
            indexed_at=record.indexed_at,
            published_at=record.published_at,
        )
