import os
import uuid
from pathlib import Path

from fastapi import APIRouter, Depends, HTTPException, UploadFile
from sqlalchemy.orm import Session

from app.api.deps import get_current_principal, get_project_or_404, require_role
from app.core.config import get_settings
from app.db.session import get_db
from app.models.knowledge import ExtractedText, FileType, KnowledgeSource
from app.models.org import Project, User, UserRole
from app.schemas.knowledge import ConfirmCleanRequest, ExtractedTextOut, KnowledgeSourceOut, KnowledgeSourceUpdate
from app.services.audit import record_audit
from app.services.storage import save_upload
from app.services.upload_guard import verify_file_signature
from app.workers.document_tasks import extract_and_clean

router = APIRouter(tags=["knowledge"])
settings = get_settings()

_EXT_TO_TYPE = {
    ".pdf": FileType.pdf,
    ".docx": FileType.docx,
    ".txt": FileType.txt,
    ".md": FileType.markdown,
    ".markdown": FileType.markdown,
    ".html": FileType.html,
    ".htm": FileType.html,
    ".csv": FileType.csv,
    ".json": FileType.json,
    ".xml": FileType.xml,
}


@router.post("/projects/{project_id}/knowledge", response_model=KnowledgeSourceOut, status_code=201)
async def upload_knowledge_source(
    project_id: uuid.UUID,
    file: UploadFile,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role(UserRole.editor)),
):
    get_project_or_404(project_id, db, current_user)

    suffix = Path(file.filename).suffix.lower()
    file_type = _EXT_TO_TYPE.get(suffix)
    if file_type is None:
        raise HTTPException(status_code=400, detail=f"Unsupported file extension: {suffix}")

    content = await file.read()
    if len(content) > settings.max_upload_mb * 1024 * 1024:
        raise HTTPException(status_code=400, detail="File too large")

    ok, reason = verify_file_signature(content, file_type)
    if not ok:
        raise HTTPException(status_code=400, detail=f"File content doesn't match its extension: {reason}")

    storage_path, stored_filename = save_upload(project_id, file.filename, content)

    source = KnowledgeSource(
        project_id=project_id,
        uploaded_by=current_user.id,
        filename=stored_filename,
        original_filename=file.filename,
        file_type=file_type,
        storage_path=storage_path,
        file_size_bytes=len(content),
    )
    db.add(source)
    db.commit()
    db.refresh(source)
    record_audit(db, current_user, "knowledge.upload", "knowledge_source", source.id, {"filename": file.filename})

    extract_and_clean.delay(str(source.id))

    return source


@router.get("/projects/{project_id}/knowledge", response_model=list[KnowledgeSourceOut])
def list_knowledge_sources(
    project_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_principal)
):
    get_project_or_404(project_id, db, current_user)
    return (
        db.query(KnowledgeSource)
        .filter(KnowledgeSource.project_id == project_id)
        .order_by(KnowledgeSource.created_at.desc())
        .all()
    )


def _get_source_or_404(source_id: uuid.UUID, db: Session, current_user: User) -> KnowledgeSource:
    source = db.get(KnowledgeSource, source_id)
    if source is None:
        raise HTTPException(status_code=404, detail="Knowledge source not found")
    project = db.get(Project, source.project_id)
    if project is None or project.org_id != current_user.org_id:
        raise HTTPException(status_code=404, detail="Knowledge source not found")
    return source


@router.get("/knowledge/{source_id}", response_model=KnowledgeSourceOut)
def get_knowledge_source(
    source_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_principal)
):
    return _get_source_or_404(source_id, db, current_user)


@router.patch("/knowledge/{source_id}", response_model=KnowledgeSourceOut)
def update_knowledge_source(
    source_id: uuid.UUID,
    payload: KnowledgeSourceUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role(UserRole.editor)),
):
    source = _get_source_or_404(source_id, db, current_user)
    if payload.tags is not None:
        source.tags = payload.tags
    if payload.category is not None:
        source.category = payload.category
    if payload.source is not None:
        source.source = payload.source
    db.commit()
    db.refresh(source)
    record_audit(db, current_user, "knowledge.classify", "knowledge_source", source.id, {"category": source.category})
    return source


@router.delete("/knowledge/{source_id}", status_code=204)
def delete_knowledge_source(
    source_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.editor))
):
    source = _get_source_or_404(source_id, db, current_user)
    if os.path.exists(source.storage_path):
        os.remove(source.storage_path)
    db.delete(source)
    db.commit()
    record_audit(db, current_user, "knowledge.delete", "knowledge_source", source_id, {"filename": source.original_filename})


@router.get("/knowledge/{source_id}/text", response_model=ExtractedTextOut)
def get_extracted_text(
    source_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_principal)
):
    source = _get_source_or_404(source_id, db, current_user)
    extracted = db.query(ExtractedText).filter_by(knowledge_source_id=source.id).one_or_none()
    if extracted is None:
        raise HTTPException(status_code=404, detail="Text not yet extracted")
    return extracted


@router.post("/knowledge/{source_id}/confirm-clean", response_model=ExtractedTextOut)
def confirm_cleaned_text(
    source_id: uuid.UUID,
    payload: ConfirmCleanRequest,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role(UserRole.editor)),
):
    source = _get_source_or_404(source_id, db, current_user)
    extracted = db.query(ExtractedText).filter_by(knowledge_source_id=source.id).one_or_none()
    if extracted is None:
        raise HTTPException(status_code=404, detail="Text not yet extracted")
    extracted.cleaned_text = payload.cleaned_text
    extracted.is_confirmed = True
    db.commit()
    db.refresh(extracted)
    record_audit(db, current_user, "knowledge.confirm_clean", "knowledge_source", source_id)
    return extracted


@router.post("/knowledge/{source_id}/reprocess", response_model=KnowledgeSourceOut)
def reprocess_knowledge_source(
    source_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.editor))
):
    source = _get_source_or_404(source_id, db, current_user)
    extract_and_clean.delay(str(source.id))
    return source
