from __future__ import annotations

from typing import TYPE_CHECKING

from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
from fastapi.responses import Response
from pydantic import BaseModel

from niannian.http.deps import require_export_auth
from niannian.skills.distiller import distill_draft
from niannian.skills.export import build_export_bundle, export_skills, write_export_mirror
from niannian.skills.import_bundle import ImportBundleError, import_bundle

if TYPE_CHECKING:
    from niannian.client import NianNian


class SkillPatch(BaseModel):
    enabled: bool


class SkillPut(BaseModel):
    content: str


def create_skills_router(nn: NianNian) -> APIRouter:
    router = APIRouter(tags=["skills"])
    loader = nn.skills

    def _export_auth(request: Request) -> None:
        require_export_auth(request, nn._config)

    @router.get("/v1/skills")
    def list_skills():
        return [s.to_dict() for s in loader.list_skills()]

    @router.get("/v1/skills/export")
    def export_skill_list(
        _: None = Depends(_export_auth),
        name: str | None = Query(default=None),
        project: str | None = Query(default=None),
        tags: str | None = Query(default=None),
    ):
        return export_skills(nn.home, loader, name=name, project=project, tags=tags)

    @router.get("/v1/skills/export/bundle")
    def export_bundle(_: None = Depends(_export_auth)):
        write_export_mirror(nn.home, loader)
        data, _manifest = build_export_bundle(nn.home, loader)
        return Response(
            content=data,
            media_type="application/zip",
            headers={"Content-Disposition": "attachment; filename=niannian-skills-bundle.zip"},
        )

    @router.post("/v1/skills/distill")
    def distill_skills(project: str | None = None, dry_run: bool = True):
        atoms = nn.list_atoms(project=project or nn.project, limit=30)
        texts = [a.raw_text for a in atoms]
        draft = distill_draft(texts, project=project or nn.project)
        if not dry_run:
            loader.write_custom(draft["name"], draft["content"])
        return draft

    @router.post("/v1/skills/import")
    async def import_skills_bundle(bundle: UploadFile = File(...)) -> dict[str, list[dict[str, str]]]:
        data = await bundle.read()
        try:
            return import_bundle(loader, data)
        except ImportBundleError as exc:
            raise HTTPException(
                status_code=400,
                detail={"code": "invalid_import_bundle", "message": str(exc)},
            ) from exc

    @router.delete("/v1/skills/custom/{name}", status_code=204)
    def delete_custom_skill(name: str) -> None:
        try:
            loader.delete_custom(name)
        except KeyError:
            raise HTTPException(status_code=404, detail={"code": "skill_not_found"}) from None
        except ValueError as exc:
            raise HTTPException(
                status_code=400,
                detail={"code": "invalid_skill_delete", "message": str(exc)},
            ) from exc

    @router.get("/v1/skills/{name}")
    def get_skill(name: str):
        skill = loader.get(name)
        if skill is None:
            raise HTTPException(status_code=404, detail={"code": "skill_not_found"})
        return skill.full_dict()

    @router.patch("/v1/skills/{name}")
    def patch_skill(name: str, payload: SkillPatch):
        try:
            skill = loader.set_enabled(name, payload.enabled)
        except KeyError:
            raise HTTPException(status_code=404, detail={"code": "skill_not_found"}) from None
        return skill.to_dict()

    @router.put("/v1/skills/{name}")
    def put_skill(name: str, payload: SkillPut):
        try:
            skill = loader.write_custom(name, payload.content)
        except ValueError as e:
            raise HTTPException(status_code=400, detail={"code": "invalid_skill", "message": str(e)}) from e
        return skill.full_dict()

    return router
