import asyncio
import shutil
import uuid
from datetime import datetime, timezone

from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse

from config import FORMAT_META, PROJECTS_DIR
from database import (
    create_guide, delete_guide, get_guide, list_guides,
    create_baustein as db_create_baustein, list_bausteine, get_baustein, delete_baustein as db_delete_baustein,
    list_suggestions, get_suggestion, update_suggestion, delete_suggestion,
    list_progress, set_progress, delete_progress,
)
from generator import generate_guide, rework_guide, cancel_guide, generate_suggestions, generate_baustein_detail, rework_baustein, sort_bausteine, suggest_topics, chat_with_guide, is_suggestions_generating, is_sorting
from models import (
    GuideCreateRequest, GuideReworkRequest, GuideResponse,
    BausteinCreateRequest, BausteinReworkRequest, BausteinSortRequest, BausteinResponse, SuggestionResponse,
    TopicSuggestRequest, TopicSuggestion,
    GuideChatRequest, GuideChatResponse,
    ProgressUpdate, ProgressResponse, ProjectResponse,
)
from paths import final_paths, project_dir, project_cache_path

router = APIRouter(prefix="/api")


@router.get("/formats")
async def get_formats():
    return FORMAT_META


def _safe_project_name(name: str) -> str:
    if not name or "/" in name or "\\" in name or ".." in name or "\x00" in name:
        raise HTTPException(400, "Ungültiger Projektname")
    return name


@router.get("/projects", response_model=list[ProjectResponse])
async def list_projects():
    if not PROJECTS_DIR.is_dir():
        return []
    result = []
    for entry in sorted(PROJECTS_DIR.iterdir()):
        if entry.is_dir():
            result.append({"name": entry.name, "cached": project_cache_path(entry.name).exists()})
    return result


@router.delete("/projects/{name}")
async def remove_project(name: str):
    _safe_project_name(name)
    pdir = project_dir(name)
    if not pdir.is_dir():
        raise HTTPException(404, "Projekt nicht gefunden")
    shutil.rmtree(pdir)
    project_cache_path(name).unlink(missing_ok=True)
    return {"ok": True}


@router.post("/topic-suggestions", response_model=list[TopicSuggestion])
async def topic_suggestions(req: TopicSuggestRequest):
    guides = await list_guides()
    existing_topics = sorted({g["topic"] for g in guides})
    return await suggest_topics(req.problem.strip(), existing_topics)


@router.post("/guides", response_model=GuideResponse)
async def create(req: GuideCreateRequest):
    now = datetime.now(timezone.utc).isoformat()
    guide = {
        "id": str(uuid.uuid4()),
        "topic": req.topic.strip(),
        "format": req.format,
        "instructions": req.instructions.strip(),
        "status": "queued",
        "progress": None,
        "created_at": now,
        "updated_at": now,
    }
    await create_guide(guide)
    asyncio.create_task(generate_guide(guide["id"], guide["topic"], guide["format"], guide["instructions"], req.reindex))
    return guide


@router.get("/guides", response_model=list[GuideResponse])
async def list_all():
    return await list_guides()


@router.get("/guides/{guide_id}", response_model=GuideResponse)
async def get_one(guide_id: str):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    return guide


@router.get("/guides/{guide_id}/html")
async def download_html(guide_id: str):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    if guide["status"] != "done":
        raise HTTPException(404, "HTML nicht verfügbar")
    html_path, _ = final_paths(guide["topic"], guide["format"])
    if not html_path.exists():
        raise HTTPException(404, "Datei nicht gefunden")
    return FileResponse(html_path, media_type="text/html", content_disposition_type="inline")


@router.post("/guides/{guide_id}/rework")
async def rework(guide_id: str, req: GuideReworkRequest):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    if guide["status"] != "done":
        raise HTTPException(400, "Guide muss fertig sein")
    asyncio.create_task(rework_guide(guide_id, guide["topic"], guide["format"], req.instructions.strip()))
    return {"ok": True}


@router.post("/guides/{guide_id}/chat", response_model=GuideChatResponse)
async def guide_chat(guide_id: str, req: GuideChatRequest):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    reply = await chat_with_guide(
        guide["topic"], guide["format"], req.section, req.outline,
        [m.model_dump() for m in req.messages],
    )
    return {"reply": reply}


@router.post("/guides/{guide_id}/cancel")
async def cancel(guide_id: str):
    cancelled = await cancel_guide(guide_id)
    if not cancelled:
        raise HTTPException(404, "Kein aktiver Prozess gefunden")
    return {"ok": True}


@router.get("/guides/{guide_id}/pdf")
async def download_pdf(guide_id: str):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    if guide["status"] != "done":
        raise HTTPException(404, "PDF nicht verfügbar")
    _, pdf_path = final_paths(guide["topic"], guide["format"])
    if not pdf_path.exists():
        raise HTTPException(404, "Datei nicht gefunden")
    return FileResponse(pdf_path, filename=pdf_path.name, media_type="application/pdf")


@router.delete("/guides/{guide_id}")
async def remove(guide_id: str):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    html_path, pdf_path = final_paths(guide["topic"], guide["format"])
    html_path.unlink(missing_ok=True)
    pdf_path.unlink(missing_ok=True)
    await delete_progress(guide_id)
    await delete_guide(guide_id)
    return {"ok": True}


@router.get("/guides/{guide_id}/progress", response_model=ProgressResponse)
async def get_progress(guide_id: str):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    return {"chapters": await list_progress(guide_id)}


@router.post("/guides/{guide_id}/progress", response_model=ProgressResponse)
async def update_progress(guide_id: str, req: ProgressUpdate):
    guide = await get_guide(guide_id)
    if guide is None:
        raise HTTPException(404, "Guide nicht gefunden")
    await set_progress(guide_id, req.chapter, req.done)
    return {"chapters": await list_progress(guide_id)}


# --- Bausteine ---

@router.get("/bausteine", response_model=list[BausteinResponse])
async def get_bausteine(topic: str):
    return await list_bausteine(topic)


@router.post("/bausteine", response_model=BausteinResponse)
async def add_baustein(req: BausteinCreateRequest):
    now = datetime.now(timezone.utc).isoformat()
    baustein = {
        "id": str(uuid.uuid4()),
        "topic": req.topic.strip(),
        "title": req.title.strip(),
        "description": "",
        "purpose": "",
        "example": "",
        "created_at": now,
        "updated_at": now,
    }
    await db_create_baustein(baustein)
    asyncio.create_task(generate_baustein_detail(baustein["id"], baustein["topic"], baustein["title"], req.instructions.strip()))
    return baustein


@router.delete("/bausteine/{baustein_id}")
async def remove_baustein(baustein_id: str):
    b = await get_baustein(baustein_id)
    if b is None:
        raise HTTPException(404, "Baustein nicht gefunden")
    await db_delete_baustein(baustein_id)
    return {"ok": True}


@router.post("/bausteine/{baustein_id}/rework")
async def rework_baustein_route(baustein_id: str, req: BausteinReworkRequest):
    b = await get_baustein(baustein_id)
    if b is None:
        raise HTTPException(404, "Baustein nicht gefunden")
    import json
    try:
        examples = json.loads(b.get("example") or "[]")
    except Exception:
        examples = []
    current = {
        "description": b.get("description", ""),
        "purpose": b.get("purpose", ""),
        "examples": examples,
    }
    asyncio.create_task(rework_baustein(baustein_id, b["topic"], b["title"], current, req.instructions.strip()))
    return {"ok": True}


@router.post("/bausteine/sort")
async def sort_bausteine_route(topic: str, req: BausteinSortRequest):
    if is_sorting(topic):
        return {"ok": True, "status": "already_sorting"}
    bausteine = await list_bausteine(topic)
    if not bausteine:
        return {"ok": True}
    asyncio.create_task(sort_bausteine(topic, bausteine, req.instructions.strip()))
    return {"ok": True}


@router.get("/bausteine/sort/status")
async def sort_status(topic: str):
    return {"sorting": is_sorting(topic)}


# --- Baustein Suggestions ---

@router.get("/bausteine/suggestions", response_model=list[SuggestionResponse])
async def get_suggestions(topic: str):
    return await list_suggestions(topic)


@router.post("/bausteine/suggestions/generate")
async def trigger_suggestions(topic: str):
    if is_suggestions_generating(topic):
        return {"ok": True, "status": "already_generating"}
    guides = await list_guides()
    html_paths = []
    for g in guides:
        if g["topic"] == topic and g["status"] == "done":
            html_path, _ = final_paths(g["topic"], g["format"])
            if html_path.exists():
                html_paths.append(html_path)
    asyncio.create_task(generate_suggestions(topic, html_paths))
    return {"ok": True}


@router.get("/bausteine/suggestions/status")
async def suggestions_status(topic: str):
    return {"generating": is_suggestions_generating(topic)}


@router.post("/bausteine/suggestions/{suggestion_id}/add")
async def accept_suggestion(suggestion_id: str):
    s = await get_suggestion(suggestion_id)
    if s is None:
        raise HTTPException(404, "Vorschlag nicht gefunden")
    now = datetime.now(timezone.utc).isoformat()
    baustein = {
        "id": str(uuid.uuid4()),
        "topic": s["topic"],
        "title": s["title"],
        "description": s["description"],
        "purpose": s["purpose"],
        "example": s["example"],
        "created_at": now,
        "updated_at": now,
    }
    await db_create_baustein(baustein)
    await delete_suggestion(suggestion_id)
    return baustein


@router.post("/bausteine/suggestions/{suggestion_id}/ignore")
async def ignore_suggestion(suggestion_id: str):
    s = await get_suggestion(suggestion_id)
    if s is None:
        raise HTTPException(404, "Vorschlag nicht gefunden")
    await update_suggestion(suggestion_id, status="ignored")
    return {"ok": True}
