from __future__ import annotations

from pathlib import Path

from fastapi import APIRouter, Header, HTTPException, status

from app.api.schemas.lucos import (
    LucosChunksDeleteRequest,
    LucosChunksEmbedRequest,
    LucosChunksEmbedResponse,
)
from app.api.routes.internal_maintenance import INTERNAL_SECRET_HEADER, _require_internal_secret
from app.core.logger import logger
from app.models.embedding_record import EmbeddingRecord, EmbeddingRecordMetadata
from app.models.embedding_record_upsert import EmbeddingRecordUpsert
from app.models.enums import SourceType
from app.repositories import embedding_record_repo
from app.services.embedding_record_builder import (
    _should_skip_unchanged,
    chunk_hash_from_record,
)
from app.services.qdrant_index_service import delete_record_vectors
from app.services.record_embedding_service import embed_and_index_records

router = APIRouter(tags=["Internal Lucos"])


def _record_id(repo_id: str, chunk_id: str) -> str:
    return f"lucos:{repo_id}:{chunk_id}"


def _section_title(content: str, file_path: str) -> str:
    for line in content.splitlines():
        stripped = line.strip()
        if not stripped:
            continue
        if stripped.startswith("#"):
            return stripped.lstrip("#").strip() or Path(file_path).name
        return stripped[:120]
    return Path(file_path).name


def _with_lucos_metadata(
    record: EmbeddingRecord,
    *,
    chunk_id: str,
    chunk_hash: str,
    language: str | None = None,
    start_line: int | None = None,
    end_line: int | None = None,
) -> EmbeddingRecord:
    metadata = record.metadata
    if hasattr(metadata, "model_copy"):
        metadata = metadata.model_copy(
            update={
                "chunk_id": chunk_id,
                "chunk_hash": chunk_hash,
                **({"language": language} if language else {}),
            }
        )
    else:
        metadata = EmbeddingRecordMetadata(
            chunk_id=chunk_id,
            chunk_hash=chunk_hash,
            language=language,
        )

    updates: dict = {"metadata": metadata}
    if start_line is not None:
        updates["start_line"] = start_line
    if end_line is not None:
        updates["end_line"] = end_line
    return record.model_copy(update=updates)


def _to_embedding_record(item) -> EmbeddingRecord:
    record_id = _record_id(item.repo_id, item.chunk_id)
    if item.source_type == SourceType.DOCS.value:
        record = EmbeddingRecord.from_doc_chunk(
            record_id=record_id,
            repo_id=item.repo_id,
            snapshot_id=item.snapshot_id,
            commit_sha=item.commit_sha or item.snapshot_id,
            text=item.content,
            doc_path=item.file_path,
            section_title=_section_title(item.content, item.file_path),
            graph_node_id=f"lucos:{item.chunk_id}",
            chunk_hash=item.chunk_hash,
        )
        return _with_lucos_metadata(
            record,
            chunk_id=item.chunk_id,
            chunk_hash=item.chunk_hash,
            start_line=item.start_line,
            end_line=item.end_line,
        )

    symbol_name = item.symbol_name or Path(item.file_path).stem or item.chunk_id
    record = EmbeddingRecord.from_code_chunk(
        record_id=record_id,
        repo_id=item.repo_id,
        snapshot_id=item.snapshot_id,
        commit_sha=item.commit_sha or item.snapshot_id,
        text=item.content,
        file_path=item.file_path,
        symbol_name=symbol_name,
        start_line=item.start_line,
        end_line=item.end_line,
        graph_node_id=f"lucos:{item.chunk_id}",
        language=item.language,
        chunk_hash=item.chunk_hash,
    )
    return _with_lucos_metadata(
        record,
        chunk_id=item.chunk_id,
        chunk_hash=item.chunk_hash,
        language=item.language,
        start_line=item.start_line,
        end_line=item.end_line,
    )


@router.post(
    "/internal/lucos/chunks/embed",
    response_model=LucosChunksEmbedResponse,
    status_code=status.HTTP_200_OK,
)
async def embed_lucos_chunks(
    request: LucosChunksEmbedRequest,
    x_internal_secret: str | None = Header(default=None, alias=INTERNAL_SECRET_HEADER),
) -> LucosChunksEmbedResponse:
    _require_internal_secret(x_internal_secret)

    upserts: list[EmbeddingRecordUpsert] = []
    for item in request.chunks:
        try:
            record = _to_embedding_record(item)
            upserts.append(
                EmbeddingRecordUpsert(
                    record=record,
                    upstream_chunk_id=item.chunk_id,
                )
            )
        except Exception as exc:
            logger.warning(
                "Skipping invalid lucos chunk repo_id={repo_id} chunk_id={chunk_id} error={error}",
                repo_id=item.repo_id,
                chunk_id=item.chunk_id,
                error=str(exc),
            )

    if not upserts:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No valid lucos chunks to embed",
        )

    record_ids = [item.record.record_id for item in upserts]
    existing_by_id = await embedding_record_repo.get_records_by_ids(record_ids)

    pending_upserts: list[EmbeddingRecordUpsert] = []
    skipped_unchanged = 0
    for upsert in upserts:
        if _should_skip_unchanged(
            upsert.record.record_id,
            chunk_hash_from_record(upsert.record),
            existing_by_id,
        ):
            skipped_unchanged += 1
            continue
        pending_upserts.append(upsert)

    embedded = 0
    indexed = 0
    failed = 0

    if pending_upserts:
        await embedding_record_repo.upsert_record_items(pending_upserts)

        try:
            stats = await embed_and_index_records(pending_upserts)
            embedded = stats.get("embedded", 0)
            indexed = stats.get("indexed", 0)
            failed = stats.get("failed", 0)
        except Exception as exc:
            logger.error("Lucos chunk embedding failed: {error}", error=str(exc))
            raise HTTPException(
                status_code=status.HTTP_502_BAD_GATEWAY,
                detail=f"Embedding failed: {exc}",
            ) from exc

    embedded += skipped_unchanged
    indexed += skipped_unchanged

    return LucosChunksEmbedResponse(
        requested=len(request.chunks),
        embedded=embedded,
        indexed=indexed,
        failed=failed,
    )


@router.post("/internal/lucos/chunks/delete", status_code=status.HTTP_200_OK)
async def delete_lucos_chunk_vectors(
    request: LucosChunksDeleteRequest,
    x_internal_secret: str | None = Header(default=None, alias=INTERNAL_SECRET_HEADER),
) -> dict:
    _require_internal_secret(x_internal_secret)
    if not request.record_ids:
        return {"deleted": 0, "mongo_updated": 0}
    return await delete_record_vectors(request.record_ids)
