from __future__ import annotations

import json
import shutil
import uuid
from pathlib import Path
from typing import Annotated

from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles

from .excel_writer import DEFAULT_TEMPLATE, fill_template, parse_values_json, values_to_csv
from .extractor import extract_pdf

BASE_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = BASE_DIR / "data"
JOBS_DIR = DATA_DIR / "jobs"
JOBS_DIR.mkdir(parents=True, exist_ok=True)

app = FastAPI(title="Smileys Evaluation Webapp", version="1.0.0")
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "app" / "static")), name="static")


def job_dir(job_id: str) -> Path:
    safe = "".join(ch for ch in job_id if ch.isalnum() or ch in "-_")
    if not safe:
        raise HTTPException(status_code=400, detail="Ungültige job_id")
    path = JOBS_DIR / safe
    path.mkdir(parents=True, exist_ok=True)
    return path


@app.get("/", response_class=HTMLResponse)
def index() -> str:
    return (BASE_DIR / "app" / "static" / "index.html").read_text(encoding="utf-8")


@app.get("/health")
def health() -> dict[str, str | bool]:
    return {"ok": True, "default_template": str(DEFAULT_TEMPLATE), "template_exists": DEFAULT_TEMPLATE.exists()}


@app.post("/api/extract")
async def api_extract(pdf: Annotated[UploadFile, File(description="Eingescannter Bewertungs-PDF")]) -> JSONResponse:
    if not pdf.filename.lower().endswith(".pdf"):
        raise HTTPException(status_code=400, detail="Bitte eine PDF-Datei hochladen.")
    jid = uuid.uuid4().hex
    jd = job_dir(jid)
    pdf_path = jd / "upload.pdf"
    with pdf_path.open("wb") as f:
        shutil.copyfileobj(pdf.file, f)
    try:
        data = extract_pdf(pdf_path, render_dir=jd / "pages")
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"PDF-Auswertung fehlgeschlagen: {exc}") from exc
    data["job_id"] = jid
    (jd / "extraction.json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
    return JSONResponse(data)


@app.post("/api/generate")
async def api_generate(
    values_json: Annotated[str, Form(description="JSON array [questionnaire][15]")],
    output_format: Annotated[str, Form()] = "xls",
    template: Annotated[UploadFile | None, File()] = None,
    job_id: Annotated[str | None, Form()] = None,
) -> FileResponse:
    try:
        values = parse_values_json(values_json)
    except Exception as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc

    jid = job_id or uuid.uuid4().hex
    jd = job_dir(jid)
    template_path: Path | None = None
    if template and template.filename:
        if not template.filename.lower().endswith((".xls", ".xlsx")):
            raise HTTPException(status_code=400, detail="Template muss .xls oder .xlsx sein.")
        template_path = jd / f"template{Path(template.filename).suffix.lower()}"
        with template_path.open("wb") as f:
            shutil.copyfileobj(template.file, f)

    try:
        out = fill_template(values, jd, template_path=template_path, output_format=output_format)
        values_to_csv(values, jd / "korrigierte_werte.csv")
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"Excel-Erstellung fehlgeschlagen: {exc}") from exc

    media = "application/vnd.ms-excel" if out.suffix.lower() == ".xls" else "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    return FileResponse(out, media_type=media, filename=out.name)


@app.post("/api/export-csv")
async def api_export_csv(values_json: Annotated[str, Form()], job_id: Annotated[str | None, Form()] = None) -> FileResponse:
    try:
        values = parse_values_json(values_json)
    except Exception as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    jd = job_dir(job_id or uuid.uuid4().hex)
    out = values_to_csv(values, jd / "korrigierte_werte.csv")
    return FileResponse(out, media_type="text/csv", filename=out.name)
