"""Admin production endpoints: failure dashboard, bulk retry, queue stats, VTT override."""
from datetime import datetime

import redis.asyncio as aioredis
from fastapi import (
    APIRouter,
    Depends,
    File,
    Form,
    HTTPException,
    Query,
    UploadFile,
    status,
)
from motor.motor_asyncio import AsyncIOMotorDatabase
from pydantic import BaseModel

from ...core.database import get_database
from ...core.dependencies import require_roles
from ...core.logging import get_logger
from ...core.redis import get_redis
from ...models.audit_log import AuditAction
from ...models.job import JobStatus, RequestedOutputs
from ...models.user import User, UserRole
from ...schemas.job import JobResponse
from ...services.audit_logger import audit_logger
from ...services.cloud_run_dispatch import dispatch as _cr_dispatch
from ...services.gcs import upload_vtt_to_gcs

logger = get_logger(__name__)
router = APIRouter(prefix="/admin/production", tags=["admin-production"])

_FAILURE_STATUSES = [
    JobStatus.PROCESSING_FAILED.value,
    JobStatus.TTS_FAILED.value,
    JobStatus.RENDER_FAILED.value,
]

_RETRY_CAP = 50


class BulkRetryRequest(BaseModel):
    job_ids: list[str]
    strategy: str = "auto"  # "auto" | "from_scratch"


class BulkRetryResponse(BaseModel):
    retried: list[str]
    skipped: list[str]
    errors: list[dict]


@router.get("/failures", response_model=list[JobResponse])
async def list_failures(
    step: str | None = Query(None, description="Filter by failure.step"),
    org_id: str | None = Query(None, description="Filter by organization_id"),
    limit: int = Query(50, ge=1, le=200),
    skip: int = Query(0, ge=0),
    current_user: User = Depends(require_roles(UserRole.PRODUCTION, UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """List all jobs in a failed status, optionally filtered by step and org."""
    query: dict = {"status": {"$in": _FAILURE_STATUSES}}
    if step:
        query["failure.step"] = step
    if org_id:
        query["organization_id"] = org_id

    cursor = db.jobs.find(query).sort("updated_at", -1).skip(skip).limit(limit)
    jobs = await cursor.to_list(length=limit)

    return [
        JobResponse(
            id=str(j["_id"]),
            title=j["title"],
            status=j["status"],
            source=j["source"],
            requested_outputs=RequestedOutputs(**j["requested_outputs"]),
            review=j.get("review", {"notes": "", "history": []}),
            outputs=j.get("outputs"),
            created_at=j["created_at"].isoformat(),
            updated_at=j["updated_at"].isoformat(),
        )
        for j in jobs
    ]


@router.post("/bulk-retry", response_model=BulkRetryResponse)
async def bulk_retry(
    payload: BulkRetryRequest,
    current_user: User = Depends(require_roles(UserRole.PRODUCTION, UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Retry up to 50 failed jobs in one call."""
    if len(payload.job_ids) > _RETRY_CAP:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Cannot retry more than {_RETRY_CAP} jobs at once",
        )

    retried: list[str] = []
    skipped: list[str] = []
    errors: list[dict] = []
    now = datetime.utcnow()

    for job_id in payload.job_ids:
        try:
            job_doc = await db.jobs.find_one({"_id": job_id})
            if not job_doc:
                skipped.append(job_id)
                continue
            if job_doc["status"] not in _FAILURE_STATUSES:
                skipped.append(job_id)
                continue

            failure = job_doc.get("failure") or {}
            if payload.strategy == "from_scratch":
                step = "ingestion"
            else:
                step = failure.get("step")
                if not step:
                    step = "tts" if job_doc["status"] == JobStatus.TTS_FAILED.value else "render"

            if step in ("ingestion", "ai_processing"):
                reset_status = JobStatus.CREATED.value
            elif step == "translation":
                reset_status = JobStatus.AI_PROCESSING.value
            elif step == "tts":
                src = job_doc["source"].get("language", "en")
                reset_status = (
                    JobStatus.APPROVED_ENGLISH.value if src == "en" else JobStatus.APPROVED_SOURCE.value
                )
            elif step == "render":
                reset_status = JobStatus.PENDING_QC.value
            else:
                skipped.append(job_id)
                continue

            await db.jobs.update_one(
                {"_id": job_id},
                {
                    "$set": {"status": reset_status, "error": None, "updated_at": now},
                    "$inc": {"retry_count": 1},
                    "$push": {
                        "review.history": {
                            "at": now,
                            "status": f"bulk_retry_{step}",
                            "by": str(current_user.id),
                        }
                    },
                },
            )

            if step in ("ingestion", "ai_processing"):
                await _cr_dispatch("ingest", job_id)
            elif step in ("translation", "tts"):
                await _cr_dispatch("translate", job_id)
            elif step == "render":
                lang = job_doc.get("last_render_language", "en")
                await _cr_dispatch("rerender", job_id, language=lang)

            retried.append(job_id)
        except Exception as e:
            logger.error(f"bulk-retry failed for job {job_id}: {e}")
            errors.append({"job_id": job_id, "error": str(e)})

    try:
        await audit_logger.log(
            action=AuditAction.JOB_BULK_RETRY,
            user_id=str(current_user.id),
            user_email=current_user.email,
            user_role=current_user.role.value if current_user.role else None,
            resource_type="job",
            description=f"Bulk retry {len(retried)} jobs (strategy={payload.strategy})",
            details={"retried": retried, "skipped": skipped, "error_count": len(errors)},
        )
    except Exception as e:
        logger.warning(f"Failed to write bulk-retry audit log: {e}")

    return BulkRetryResponse(retried=retried, skipped=skipped, errors=errors)


# ---------------------------------------------------------------------------
# PR-7: Queue depth stats
# ---------------------------------------------------------------------------

_CELERY_QUEUES = ["default", "ingest", "tts", "render", "ffmpeg", "whisper", "notify", "embed"]


class QueueStats(BaseModel):
    queues: dict[str, int]  # queue_name → pending task count
    total_pending: int


@router.get("/queue-stats", response_model=QueueStats)
async def get_queue_stats(
    current_user: User = Depends(require_roles(UserRole.PRODUCTION, UserRole.ADMIN)),
    redis: aioredis.Redis = Depends(get_redis),
):
    """Return pending task counts per Celery queue (via Redis LLEN)."""
    counts: dict[str, int] = {}
    for q in _CELERY_QUEUES:
        try:
            n = await redis.llen(q)
            counts[q] = n
        except Exception:
            counts[q] = 0
    return QueueStats(queues=counts, total_pending=sum(counts.values()))


# ---------------------------------------------------------------------------
# PR-8: Upload final VTT override — bypass AI, jump to PENDING_QC
# ---------------------------------------------------------------------------

_BYPASSABLE_STATUSES = {
    JobStatus.CREATED.value,
    JobStatus.INGESTING.value,
    JobStatus.AI_PROCESSING.value,
    JobStatus.PROCESSING_FAILED.value,
    JobStatus.TTS_FAILED.value,
    JobStatus.RENDER_FAILED.value,
}


@router.post("/jobs/{job_id}/upload-final-vtt")
async def upload_final_vtt(
    job_id: str,
    language: str = Form(..., description="BCP-47 language code, e.g. 'en' or 'fr'"),
    vtt_file: UploadFile = File(..., description="WebVTT (.vtt) file"),
    vtt_type: str = Form("captions", description="'captions' or 'ad'"),
    current_user: User = Depends(require_roles(UserRole.PRODUCTION, UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Upload a hand-crafted VTT to override AI output and advance job to PENDING_QC."""
    job_doc = await db.jobs.find_one({"_id": job_id})
    if not job_doc:
        raise HTTPException(status_code=404, detail="Job not found")

    if job_doc["status"] not in _BYPASSABLE_STATUSES:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Cannot override VTT when job is in status '{job_doc['status']}'. "
                   f"Only allowed in: {sorted(_BYPASSABLE_STATUSES)}",
        )

    if not vtt_file.filename or not vtt_file.filename.endswith(".vtt"):
        raise HTTPException(status_code=400, detail="File must be a .vtt file")

    vtt_content = (await vtt_file.read()).decode("utf-8")
    if not vtt_content.strip().startswith("WEBVTT"):
        raise HTTPException(status_code=400, detail="File does not start with WEBVTT header")

    if vtt_type not in ("captions", "ad"):
        raise HTTPException(status_code=400, detail="vtt_type must be 'captions' or 'ad'")

    lang_key = language.replace("-", "_")
    field = "captions_vtt_gcs" if vtt_type == "captions" else "ad_vtt_gcs"
    gcs_path = f"{job_id}/{lang_key}/{vtt_type}.vtt"

    gcs_uri = await upload_vtt_to_gcs(vtt_content, gcs_path)

    now = datetime.utcnow()
    await db.jobs.update_one(
        {"_id": job_id},
        {
            "$set": {
                f"outputs.{lang_key}.{field}": gcs_uri,
                "status": JobStatus.PENDING_QC.value,
                "updated_at": now,
            },
            "$push": {
                "review.history": {
                    "at": now,
                    "status": "manual_vtt_upload",
                    "by": str(current_user.id),
                    "note": f"Manual {vtt_type} VTT upload for {language} by {current_user.email}",
                }
            },
        },
    )

    try:
        await audit_logger.log(
            action=AuditAction.VTT_EDIT,
            user_id=str(current_user.id),
            user_email=current_user.email,
            user_role=current_user.role.value if current_user.role else None,
            resource_type="job",
            resource_id=job_id,
            description=f"Manual {vtt_type} VTT upload for {language} — job advanced to PENDING_QC",
        )
    except Exception as e:
        logger.warning(f"Failed to write upload-final-vtt audit log: {e}")

    return {"status": "ok", "gcs_uri": gcs_uri, "job_status": JobStatus.PENDING_QC.value}
