from datetime import datetime, timezone

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

from app.api.deps import get_current_user
from app.apply import prepare_apply, run_semi_auto_fill
from app.core.config import get_settings
from app.core.database import get_db
from app.models import Application, CV, CVVersion, Job, User
from app.schemas import ApplicationIn, ApplicationOut, ApplicationUpdate, JobOut
from app.schemas.cv_apply import ApplyPrepareOut, ApplyRunIn, ApplyRunOut

router = APIRouter(tags=["applications"])


def _app_out(app: Application, db: Session, user: User) -> ApplicationOut:
    job_data = JobOut.model_validate(app.job) if app.job else None
    from app.services.wow_tier_d import signal_gates, silence_protocol

    return ApplicationOut(
        id=app.id,
        job_id=app.job_id,
        status=app.status,
        notes=app.notes,
        adapter=app.adapter,
        apply_log=app.apply_log or [],
        cv_version_id=app.cv_version_id,
        applied_at=app.applied_at,
        created_at=app.created_at,
        job=job_data,
        silence=silence_protocol(app, gates=signal_gates(db, user)),
    )


@router.get("/applications", response_model=list[ApplicationOut])
def list_applications(user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    apps = (
        db.query(Application)
        .options(joinedload(Application.job))
        .filter(Application.user_id == user.id)
        .order_by(Application.updated_at.desc())
        .all()
    )
    return [_app_out(a, db, user) for a in apps]


@router.post("/applications", response_model=ApplicationOut)
def create_application(
    payload: ApplicationIn,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    from app.services.entitlements import check_application_allowed

    job = db.get(Job, payload.job_id)
    if not job:
        raise HTTPException(404, "Oferta no encontrada")
    existing = (
        db.query(Application)
        .filter(Application.user_id == user.id, Application.job_id == payload.job_id)
        .first()
    )
    if existing:
        existing.status = payload.status
        existing.notes = payload.notes
        db.commit()
        db.refresh(existing)
        return _app_out(existing, db, user)
    count = db.query(Application).filter(Application.user_id == user.id).count()
    check_application_allowed(db, user, count)
    app = Application(
        user_id=user.id,
        job_id=payload.job_id,
        status=payload.status,
        notes=payload.notes,
    )
    db.add(app)
    db.commit()
    db.refresh(app)
    app = (
        db.query(Application)
        .options(joinedload(Application.job))
        .filter(Application.id == app.id)
        .first()
    )
    return _app_out(app, db, user)


@router.patch("/applications/{app_id}", response_model=ApplicationOut)
def update_application(
    app_id: int,
    payload: ApplicationUpdate,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    app = (
        db.query(Application)
        .options(joinedload(Application.job))
        .filter(Application.id == app_id, Application.user_id == user.id)
        .first()
    )
    if not app:
        raise HTTPException(404, "Candidatura no encontrada")
    data = payload.model_dump(exclude_unset=True)
    for k, v in data.items():
        setattr(app, k, v)
    if payload.status == "applied" and not app.applied_at:
        app.applied_at = datetime.now(timezone.utc)
    app.updated_at = datetime.now(timezone.utc)
    db.commit()
    db.refresh(app)
    return _app_out(app, db, user)


@router.get("/apply/prepare/{job_id}", response_model=ApplyPrepareOut)
def apply_prepare(
    job_id: int,
    cv_version_id: int | None = None,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    from app.services.entitlements import require_entitlement

    require_entitlement(db, user, "apply_prepare", message="Apply prepare requiere plan Profesional o superior.")
    job = db.get(Job, job_id)
    if not job:
        raise HTTPException(404, "Oferta no encontrada")
    cv_md = None
    cover = None
    if cv_version_id:
        ver = (
            db.query(CVVersion)
            .join(CV)
            .filter(CVVersion.id == cv_version_id, CV.user_id == user.id)
            .first()
        )
        if ver:
            cv_md = ver.content_md
            cover = ver.cover_letter_md
    else:
        base = db.query(CV).filter(CV.user_id == user.id, CV.is_base.is_(True)).first()
        if base:
            cv_md = base.content_md

    plan = prepare_apply(user, job, cv_md, cover)
    return ApplyPrepareOut(
        job_id=job.id,
        url=job.url,
        adapter=plan.adapter,
        mode=plan.mode,
        checklist=plan.checklist,
        cv_markdown=cv_md,
        cover_letter=cover,
        prefill=plan.prefill,
    )


@router.post("/apply/run", response_model=ApplyRunOut)
def apply_run(
    payload: ApplyRunIn,
    user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    from app.services.entitlements import require_entitlement

    require_entitlement(db, user, "apply_run", message="Apply run requiere plan Carrera Max.")
    job = db.get(Job, payload.job_id)
    if not job:
        raise HTTPException(404, "Oferta no encontrada")

    cv_md = None
    cover = None
    if payload.cv_version_id:
        ver = (
            db.query(CVVersion)
            .join(CV)
            .filter(CVVersion.id == payload.cv_version_id, CV.user_id == user.id)
            .first()
        )
        if not ver:
            raise HTTPException(404, "Versión de CV no encontrada")
        if getattr(ver, "review_status", "draft") != "approved":
            raise HTTPException(
                400,
                "El CV aún es borrador. Revisa secciones, aprueba la versión y luego aplica.",
            )
        cv_md = ver.content_md
        cover = ver.cover_letter_md
    else:
        raise HTTPException(
            400,
            "Selecciona una versión de CV aprobada (Adaptar → revisar → Aprobar para envío).",
        )

    plan = prepare_apply(user, job, cv_md, cover)
    app = (
        db.query(Application)
        .filter(Application.user_id == user.id, Application.job_id == job.id)
        .first()
    )
    if not app:
        app = Application(user_id=user.id, job_id=job.id, status="applying")
        db.add(app)

    settings = get_settings()
    confirm = payload.confirm_submit and (user.apply_auto_submit or settings.apply_auto_submit)

    if plan.mode == "semi_auto":
        result = run_semi_auto_fill(job, plan.prefill, confirm_submit=confirm)
    else:
        result = {
            "status": "assisted",
            "adapter": plan.adapter,
            "message": "Modo asistido: abre la URL y usa el checklist / CV copiable.",
            "log": plan.checklist,
            "screenshot_path": None,
        }

    app.status = "applied" if result.get("status") == "submitted" else "applying"
    app.adapter = result.get("adapter")
    app.apply_log = (app.apply_log or []) + [
        {"at": datetime.now(timezone.utc).isoformat(), **{k: result.get(k) for k in ("status", "message", "log")}}
    ]
    if payload.cv_version_id:
        app.cv_version_id = payload.cv_version_id
    if result.get("status") == "submitted":
        app.applied_at = datetime.now(timezone.utc)
    app.updated_at = datetime.now(timezone.utc)
    db.commit()

    return ApplyRunOut(
        status=result.get("status", "unknown"),
        adapter=result.get("adapter", plan.adapter),
        message=result.get("message", ""),
        log=result.get("log") or [],
        screenshot_path=result.get("screenshot_path"),
    )
