from datetime import datetime, timezone
from pathlib import Path

from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import FileResponse, PlainTextResponse
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from sqlalchemy.orm import Session

from app.api.deps import get_current_user
from app.core.config import get_settings
from app.core.database import get_db
from app.models import CV, CVVersion, Job, User
from app.schemas.cv_apply import (
    CVAdaptIn,
    CVImproveIn,
    CVIn,
    CVOut,
    CVReviewIn,
    CVReviewOut,
    CVVersionOut,
)
from app.services import ai_cv
from app.services.cv_review import merge_sections, split_markdown_sections

router = APIRouter(prefix="/cvs", tags=["cvs"])


@router.get("", response_model=list[CVOut])
def list_cvs(user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    return db.query(CV).filter(CV.user_id == user.id).order_by(CV.updated_at.desc()).all()


@router.post("", response_model=CVOut)
def create_cv(payload: CVIn, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    from app.services.entitlements import check_cv_create_allowed

    count = db.query(CV).filter(CV.user_id == user.id).count()
    check_cv_create_allowed(db, user, count)
    if payload.is_base:
        for cv in db.query(CV).filter(CV.user_id == user.id, CV.is_base.is_(True)):
            cv.is_base = False
    structured = payload.structured or ai_cv.parse_cv_to_structure(payload.content_md)
    cv = CV(
        user_id=user.id,
        title=payload.title,
        content_md=payload.content_md,
        structured=structured,
        is_base=payload.is_base,
    )
    db.add(cv)
    db.commit()
    db.refresh(cv)
    return cv


@router.put("/{cv_id}", response_model=CVOut)
def update_cv(
    cv_id: int,
    payload: CVIn,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    cv = db.query(CV).filter(CV.id == cv_id, CV.user_id == user.id).first()
    if not cv:
        raise HTTPException(404, "CV no encontrado")
    cv.title = payload.title
    cv.content_md = payload.content_md
    cv.structured = payload.structured or ai_cv.parse_cv_to_structure(payload.content_md)
    cv.is_base = payload.is_base
    cv.updated_at = datetime.now(timezone.utc)
    if payload.is_base:
        for other in db.query(CV).filter(CV.user_id == user.id, CV.id != cv.id, CV.is_base.is_(True)):
            other.is_base = False
    db.commit()
    db.refresh(cv)
    return cv


@router.post("/upload", response_model=CVOut)
async def upload_cv(
    file: UploadFile = File(...),
    replace_cv_id: int | None = Query(None),
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    from app.services.entitlements import check_cv_create_allowed

    settings = get_settings()
    dest_dir = settings.data_dir / "cvs" / str(user.id)
    dest_dir.mkdir(parents=True, exist_ok=True)
    dest = dest_dir / (file.filename or "curriculum.pdf")
    content = await file.read()
    dest.write_bytes(content)

    text = ""
    if dest.suffix.lower() == ".pdf":
        text = ai_cv.extract_text_from_pdf(dest)
    else:
        text = content.decode("utf-8", errors="ignore")

    existing = None
    if replace_cv_id:
        existing = db.query(CV).filter(CV.id == replace_cv_id, CV.user_id == user.id).first()
        if not existing:
            raise HTTPException(404, "Currículum no encontrado")
    if existing:
        existing.title = file.filename or existing.title
        existing.content_md = text
        existing.structured = ai_cv.parse_cv_to_structure(text)
        existing.file_path = str(dest)
        existing.updated_at = datetime.now(timezone.utc)
        db.commit()
        db.refresh(existing)
        return existing

    count = db.query(CV).filter(CV.user_id == user.id).count()
    check_cv_create_allowed(db, user, count)

    for cv in db.query(CV).filter(CV.user_id == user.id, CV.is_base.is_(True)):
        cv.is_base = False

    cv = CV(
        user_id=user.id,
        title=file.filename or "Currículum subido",
        content_md=text,
        structured=ai_cv.parse_cv_to_structure(text),
        is_base=True,
        file_path=str(dest),
    )
    db.add(cv)
    db.commit()
    db.refresh(cv)
    return cv


@router.post("/improve", response_model=CVReviewOut)
def improve(payload: CVImproveIn, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    from app.services.entitlements import check_ai_adapt_allowed, increment_usage

    check_ai_adapt_allowed(db, user)
    cv = db.query(CV).filter(CV.id == payload.cv_id, CV.user_id == user.id).first()
    if not cv:
        raise HTTPException(404, "CV no encontrado")
    improved = ai_cv.improve_cv(cv.content_md, payload.language, payload.instructions)
    sections = {"proposed": split_markdown_sections(improved), "base": split_markdown_sections(cv.content_md)}
    ver = CVVersion(
        cv_id=cv.id,
        kind="improved",
        language=payload.language,
        content_md=improved,
        notes=payload.instructions or "Borrador: revisa antes de usar",
        review_status="draft",
        base_snapshot_md=cv.content_md,
        sections_json=sections,
    )
    db.add(ver)
    db.commit()
    db.refresh(ver)
    increment_usage(db, user, "ai_adapts_month")
    return CVReviewOut(
        version=CVVersionOut.model_validate(ver),
        base_sections=sections["base"],
        proposed_sections=sections["proposed"],
        ready_to_send=False,
    )


@router.post("/adapt", response_model=CVReviewOut)
def adapt(payload: CVAdaptIn, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    from app.services.entitlements import check_ai_adapt_allowed, increment_usage

    check_ai_adapt_allowed(db, user)
    cv = db.query(CV).filter(CV.id == payload.cv_id, CV.user_id == user.id).first()
    job = db.get(Job, payload.job_id)
    if not cv or not job:
        raise HTTPException(404, "CV u oferta no encontrados")
    content, cover, notes = ai_cv.adapt_cv_to_job(
        cv.content_md,
        job.title,
        job.company,
        job.description,
        language=payload.language,
        include_cover_letter=payload.include_cover_letter,
    )
    sections = {"proposed": split_markdown_sections(content), "base": split_markdown_sections(cv.content_md)}
    ver = CVVersion(
        cv_id=cv.id,
        job_id=job.id,
        kind="adapted",
        language=payload.language,
        content_md=content,
        cover_letter_md=cover,
        notes=(notes or "") + " | Borrador: aprueba antes de aplicar",
        review_status="draft",
        base_snapshot_md=cv.content_md,
        sections_json=sections,
    )
    db.add(ver)
    db.commit()
    db.refresh(ver)
    increment_usage(db, user, "ai_adapts_month")
    return CVReviewOut(
        version=CVVersionOut.model_validate(ver),
        base_sections=sections["base"],
        proposed_sections=sections["proposed"],
        ready_to_send=False,
    )


@router.get("/versions/{version_id}/review", response_model=CVReviewOut)
def get_review(version_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    ver = (
        db.query(CVVersion)
        .join(CV)
        .filter(CVVersion.id == version_id, CV.user_id == user.id)
        .first()
    )
    if not ver:
        raise HTTPException(404, "Versión no encontrada")
    base = ver.base_snapshot_md or ""
    return CVReviewOut(
        version=CVVersionOut.model_validate(ver),
        base_sections=split_markdown_sections(base),
        proposed_sections=split_markdown_sections(ver.content_md),
        ready_to_send=ver.review_status == "approved",
    )


@router.post("/versions/{version_id}/review", response_model=CVReviewOut)
def apply_review(
    version_id: int,
    payload: CVReviewIn,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    ver = (
        db.query(CVVersion)
        .join(CV)
        .filter(CVVersion.id == version_id, CV.user_id == user.id)
        .first()
    )
    if not ver:
        raise HTTPException(404, "Versión no encontrada")

    if payload.content_md is not None:
        ver.content_md = payload.content_md
    elif payload.accept_all or payload.accepted_section_ids:
        ver.content_md = merge_sections(
            ver.base_snapshot_md or "",
            ver.content_md,
            payload.accepted_section_ids,
            accept_all=payload.accept_all,
        )
    if payload.cover_letter_md is not None:
        ver.cover_letter_md = payload.cover_letter_md

    ver.sections_json = {
        "proposed": split_markdown_sections(ver.content_md),
        "base": split_markdown_sections(ver.base_snapshot_md or ""),
    }
    if payload.approve:
        ver.review_status = "approved"
        ver.notes = (ver.notes or "") + " | Aprobado para envío"
    else:
        ver.review_status = "draft"

    db.commit()
    db.refresh(ver)
    return CVReviewOut(
        version=CVVersionOut.model_validate(ver),
        base_sections=ver.sections_json.get("base") or [],
        proposed_sections=ver.sections_json.get("proposed") or [],
        ready_to_send=ver.review_status == "approved",
    )


@router.get("/versions", response_model=list[CVVersionOut])
def list_versions(
    cv_id: int | None = None,
    job_id: int | None = None,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    q = (
        db.query(CVVersion)
        .join(CV)
        .filter(CV.user_id == user.id)
        .order_by(CVVersion.created_at.desc())
    )
    if cv_id:
        q = q.filter(CVVersion.cv_id == cv_id)
    if job_id:
        q = q.filter(CVVersion.job_id == job_id)
    rows = q.limit(100).all()
    out: list[CVVersionOut] = []
    for ver in rows:
        data = CVVersionOut.model_validate(ver)
        if data.sections_json is None:
            data.sections_json = {}
        if not data.review_status:
            data.review_status = "draft"
        if data.base_snapshot_md is None:
            data.base_snapshot_md = ""
        out.append(data)
    return out


@router.post("/versions/{version_id}/use", response_model=CVOut)
def use_version_as_cv(
    version_id: int,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    """Copia una versión generada por IA al currículum (sin gastar otra mejora)."""
    ver = (
        db.query(CVVersion)
        .join(CV)
        .filter(CVVersion.id == version_id, CV.user_id == user.id)
        .first()
    )
    if not ver:
        raise HTTPException(404, "Versión no encontrada")
    cv = db.query(CV).filter(CV.id == ver.cv_id, CV.user_id == user.id).first()
    if not cv:
        raise HTTPException(404, "Currículum no encontrado")
    cv.content_md = ver.content_md
    cv.structured = ai_cv.parse_cv_to_structure(ver.content_md)
    cv.updated_at = datetime.now(timezone.utc)
    ver.review_status = "approved"
    db.commit()
    db.refresh(cv)
    return cv


@router.get("/versions/{version_id}/export.md")
def export_md(version_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    ver = (
        db.query(CVVersion)
        .join(CV)
        .filter(CVVersion.id == version_id, CV.user_id == user.id)
        .first()
    )
    if not ver:
        raise HTTPException(404, "Versión no encontrada")
    body = ver.content_md
    if ver.cover_letter_md:
        body += "\n\n## Carta\n\n" + ver.cover_letter_md
    return PlainTextResponse(body, media_type="text/markdown")


@router.get("/versions/{version_id}/export.pdf")
def export_pdf(version_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    ver = (
        db.query(CVVersion)
        .join(CV)
        .filter(CVVersion.id == version_id, CV.user_id == user.id)
        .first()
    )
    if not ver:
        raise HTTPException(404, "Versión no encontrada")
    settings = get_settings()
    out = settings.data_dir / "exports" / f"cv_{user.id}_{version_id}.pdf"
    out.parent.mkdir(parents=True, exist_ok=True)
    c = canvas.Canvas(str(out), pagesize=A4)
    width, height = A4
    y = height - 40
    text = ver.content_md
    if ver.cover_letter_md:
        text += "\n\n--- CARTA ---\n\n" + ver.cover_letter_md
    for line in text.splitlines():
        if y < 40:
            c.showPage()
            y = height - 40
        c.setFont("Helvetica", 10)
        c.drawString(40, y, line[:110])
        y -= 14
    c.save()
    return FileResponse(out, filename=out.name, media_type="application/pdf")
