# retoor <retoor@molodetz.nl>

import logging
from pathlib import Path
from typing import Annotated

from fastapi import APIRouter, Form, Request
from fastapi.responses import FileResponse, JSONResponse

from devplacepy.config import PLANNING_REPORTS_DIR
from devplacepy.models import PlanningForm
from devplacepy.responses import json_error
from devplacepy.schemas import PlanningJobOut
from devplacepy.services.audit import record as audit
from devplacepy.services.gitea.config import gitea_config
from devplacepy.services.jobs import queue
from devplacepy.utils import not_found, require_admin

logger = logging.getLogger(__name__)
router = APIRouter()

PLANNING_KIND = "planning"
RETENTION_EXTEND_SECONDS = 7 * 24 * 60 * 60
PREFERRED_NAME = "open-tickets-plan"


def _status_payload(job: dict) -> dict:
    result = job.get("result", {})
    done = job.get("status") == queue.DONE
    return {
        "uid": job.get("uid", ""),
        "kind": job.get("kind", ""),
        "status": job.get("status", ""),
        "preferred_name": job.get("preferred_name"),
        "download_url": result.get("download_url") if done else None,
        "markdown": result.get("markdown") if done else None,
        "ai_used": bool(result.get("ai_used")) if done else False,
        "error": job.get("error") or None,
        "issue_count": int(result.get("item_count") or 0),
        "bytes_out": int(job.get("bytes_out") or result.get("bytes_out") or 0),
        "created_at": job.get("created_at"),
        "completed_at": job.get("completed_at") or None,
    }


def _parse_numbers(raw: str) -> list[int]:
    numbers: list[int] = []
    for token in (raw or "").replace(",", " ").split():
        try:
            number = int(token)
        except ValueError:
            continue
        if number > 0 and number not in numbers:
            numbers.append(number)
    return numbers


@router.post("/planning")
async def planning_generate(
    request: Request, data: Annotated[PlanningForm, Form()]
):
    admin = require_admin(request)
    if not gitea_config().is_configured:
        return json_error(503, "The issue tracker is not configured")
    numbers = _parse_numbers(data.numbers)
    uid = queue.enqueue(
        "planning", {"numbers": numbers}, "user", admin["uid"], PREFERRED_NAME
    )
    scope = f"{len(numbers)} selected tickets" if numbers else "all open tickets"
    audit.record(
        request,
        "issue.planning.request",
        user=admin,
        target_type="issue",
        summary=f"requested a planning report for {scope}",
        metadata={"selected": len(numbers)},
        links=[audit.job(uid)],
    )
    return JSONResponse({"uid": uid, "status_url": f"/issues/planning/{uid}"})


@router.get("/planning/{uid}")
async def planning_status(request: Request, uid: str):
    require_admin(request)
    job = queue.get_job(uid)
    if not job or job.get("kind") != PLANNING_KIND:
        raise not_found("Planning job not found")
    return JSONResponse(
        PlanningJobOut.model_validate(_status_payload(job)).model_dump(mode="json")
    )


@router.get("/planning/{uid}/download")
async def planning_download(request: Request, uid: str):
    require_admin(request)
    job = queue.get_job(uid)
    if (
        not job
        or job.get("kind") != PLANNING_KIND
        or job.get("status") != queue.DONE
    ):
        raise not_found("Planning report not available")
    result = job.get("result", {})
    local_path = result.get("local_path", "")
    resolved = Path(local_path).resolve()
    if (
        not local_path
        or not resolved.is_relative_to(PLANNING_REPORTS_DIR.resolve())
        or not resolved.is_file()
    ):
        raise not_found("Planning report not available")
    queue.touch_job(uid, RETENTION_EXTEND_SECONDS)
    return FileResponse(
        resolved,
        filename=result.get("final_name", "open-tickets-plan.md"),
        media_type="text/markdown",
    )
