"""Async job API for long transcriptions (file conversion, meeting recordings).

POST /v1/jobs        -> {id, status:"queued"}
GET  /v1/jobs/{id}   -> {id, status, progress, result?|error?}
DELETE /v1/jobs/{id} -> cancel/delete
"""
from __future__ import annotations

from fastapi import APIRouter, Depends, Form, HTTPException, UploadFile

from ..auth import Principal
from ..deps import AuthDep, get_config, get_engine, get_runner, get_store
from ..engine import EngineHost
from ..formats import VALID_FORMATS
from ..jobs import JobRunner, JobStore
from ..upload import spool_upload

router = APIRouter()


@router.post("/v1/jobs")
async def create_job(
    file: UploadFile,
    model: str = Form("whisper-1"),
    language: str = Form(""),
    response_format: str = Form("verbose_json"),
    multilingual: bool = Form(True),
    principal: Principal = AuthDep,
    engine: EngineHost = Depends(get_engine),
    store: JobStore = Depends(get_store),
    runner: JobRunner = Depends(get_runner),
    config=Depends(get_config),
) -> dict:
    if response_format not in VALID_FORMATS:
        raise HTTPException(status_code=400,
                            detail=f"Unsupported response_format '{response_format}'")
    # Cap pending jobs so a client can't spool unbounded uploads to disk faster
    # than the single worker drains them (disk-exhaustion guard).
    if store.pending_count() >= config.queue_size:
        raise HTTPException(status_code=503, detail="Too many pending jobs",
                            headers={"Retry-After": "10"})

    from paths import data_dir
    tmp = await spool_upload(file, config.max_upload_mb * 1024 * 1024, data_dir())

    lang = language.strip() or None
    # An explicit language wins; otherwise per-segment auto-detect (multilingual).
    job = store.create(
        input_path=tmp,
        language=lang,
        with_timestamps=True,
        multilingual=(multilingual if lang is None else False),
        response_format=response_format,
        principal_id=principal.id,
    )
    runner.submit(job)
    return {"id": job.id, "status": job.status}


@router.get("/v1/jobs/{job_id}")
async def get_job(
    job_id: str,
    principal: Principal = AuthDep,
    store: JobStore = Depends(get_store),
) -> dict:
    job = store.get(job_id)
    # 404 also when the job belongs to another principal (no cross-tenant peek
    # once the hosted offering maps tokens to accounts).
    if job is None or job.principal_id != principal.id:
        raise HTTPException(status_code=404, detail="No such job")
    return job.public()


@router.delete("/v1/jobs/{job_id}")
async def delete_job(
    job_id: str,
    principal: Principal = AuthDep,
    store: JobStore = Depends(get_store),
) -> dict:
    job = store.get(job_id)
    if job is None or job.principal_id != principal.id:
        raise HTTPException(status_code=404, detail="No such job")
    store.delete(job_id)
    return {"id": job_id, "status": "deleted"}
