# backend/app/routers/tree.py
import uuid

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select

from ..db import get_session
from ..models import Folder, Project, Task
from ..schemas import (
    FolderCreate,
    FolderOut,
    FolderPatch,
    ProjectCreate,
    ProjectNode,
    ProjectPatch,
)

router = APIRouter()  # prefix 없음. main.py 에서 prefix="/api" 등록.


def _slug() -> str:
    return "p-" + uuid.uuid4().hex[:8]


def build_tree(s: Session) -> list[FolderOut]:
    folders = s.exec(select(Folder).order_by(Folder.sort_order)).all()
    projects = s.exec(select(Project).order_by(Project.sort_order)).all()
    # 작업 수: project_id 별 직접 작업 수
    direct: dict[str, int] = {}
    for t in s.exec(select(Task)).all():
        direct[t.project_id] = direct.get(t.project_id, 0) + 1
    by_parent: dict[str | None, list[Project]] = {}
    for p in projects:
        by_parent.setdefault(p.parent_id, []).append(p)

    def count_recursive(pid: str) -> int:
        total = direct.get(pid, 0)
        for ch in by_parent.get(pid, []):
            total += count_recursive(ch.id)
        return total

    def node(p: Project) -> ProjectNode:
        kids = [node(c) for c in by_parent.get(p.id, [])]
        return ProjectNode(
            id=p.id,
            folder_id=p.folder_id,
            parent_id=p.parent_id,
            name=p.name,
            tone=p.tone,
            sort_order=p.sort_order,
            pinned=p.pinned,
            task_count=count_recursive(p.id),
            children=kids,
        )

    out = []
    for f in folders:
        roots = [node(p) for p in by_parent.get(None, []) if p.folder_id == f.id]
        out.append(
            FolderOut(
                id=f.id,
                name=f.name,
                tone=f.tone,
                icon=f.icon,
                sort_order=f.sort_order,
                is_system=f.is_system,
                projects=roots,
            )
        )
    return out


@router.get("/tree", response_model=list[FolderOut])
def get_tree(s: Session = Depends(get_session)):
    return build_tree(s)


@router.post("/folders", response_model=FolderOut)
def create_folder(body: FolderCreate, s: Session = Depends(get_session)):
    fid = _slug()
    mx = max([f.sort_order for f in s.exec(select(Folder)).all()] + [-1]) + 1
    f = Folder(
        id=fid,
        name=body.name,
        tone=body.tone or "ink",
        icon=body.icon or "folder",
        sort_order=mx,
        is_system=False,
    )
    s.add(f)
    s.commit()
    return [x for x in build_tree(s) if x.id == fid][0]


@router.patch("/folders/{fid}", response_model=FolderOut)
def patch_folder(fid: str, body: FolderPatch, s: Session = Depends(get_session)):
    f = s.get(Folder, fid)
    if not f:
        raise HTTPException(404, "folder not found")
    for k, v in body.model_dump(exclude_none=True).items():
        setattr(f, k, v)
    s.add(f)
    s.commit()
    return [x for x in build_tree(s) if x.id == fid][0]


@router.delete("/folders/{fid}")
def delete_folder(fid: str, s: Session = Depends(get_session)):
    f = s.get(Folder, fid)
    if not f:
        raise HTTPException(404, "folder not found")
    if f.is_system:
        raise HTTPException(400, "system folder cannot be deleted")
    if s.exec(select(Project).where(Project.folder_id == fid)).first():
        raise HTTPException(400, "folder has projects")
    s.delete(f)
    s.commit()
    return {"deleted": fid}


@router.post("/projects", response_model=ProjectNode)
def create_project(body: ProjectCreate, s: Session = Depends(get_session)):
    if not s.get(Folder, body.folder_id):
        raise HTTPException(404, "folder not found")
    if body.parent_id and not s.get(Project, body.parent_id):
        raise HTTPException(404, "parent project not found")
    pid = _slug()
    siblings = s.exec(
        select(Project).where(
            Project.parent_id == body.parent_id, Project.folder_id == body.folder_id
        )
    ).all()
    order = max([p.sort_order for p in siblings] + [-1]) + 1
    p = Project(
        id=pid,
        folder_id=body.folder_id,
        parent_id=body.parent_id,
        name=body.name,
        tone=body.tone or "ink",
        sort_order=order,
    )
    s.add(p)
    s.commit()
    s.refresh(p)
    return ProjectNode(
        id=p.id,
        folder_id=p.folder_id,
        parent_id=p.parent_id,
        name=p.name,
        tone=p.tone,
        sort_order=p.sort_order,
        pinned=p.pinned,
        task_count=0,
        children=[],
    )


@router.patch("/projects/{pid}", response_model=ProjectNode)
def patch_project(pid: str, body: ProjectPatch, s: Session = Depends(get_session)):
    p = s.get(Project, pid)
    if not p:
        raise HTTPException(404, "project not found")
    data = body.model_dump(exclude_none=True)
    # parent_id 이동 시 순환(자기 자신/후손으로 이동) 금지
    if "parent_id" in data and data["parent_id"]:
        cur = s.get(Project, data["parent_id"])
        while cur:
            if cur.id == pid:
                raise HTTPException(400, "cannot move into own descendant")
            cur = s.get(Project, cur.parent_id) if cur.parent_id else None
    for k, v in data.items():
        setattr(p, k, v)
    s.add(p)
    s.commit()
    s.refresh(p)

    def find(nodes):
        for n in nodes:
            if n.id == pid:
                return n
            r = find(n.children)
            if r:
                return r

    for f in build_tree(s):
        r = find(f.projects)
        if r:
            return r
    raise HTTPException(500, "rebuild failed")


@router.delete("/projects/{pid}")
def delete_project(pid: str, s: Session = Depends(get_session)):
    p = s.get(Project, pid)
    if not p:
        raise HTTPException(404, "project not found")
    if s.exec(select(Project).where(Project.parent_id == pid)).first():
        raise HTTPException(400, "project has sub-projects")
    if s.exec(select(Task).where(Task.project_id == pid)).first():
        raise HTTPException(400, "project has tasks")
    s.delete(p)
    s.commit()
    return {"deleted": pid}


@router.post("/projects/{pid}/pin", response_model=ProjectNode)
def pin_project(pid: str, s: Session = Depends(get_session)):
    p = s.get(Project, pid)
    if not p:
        raise HTTPException(404, "project not found")
    p.pinned = not p.pinned
    s.add(p)
    s.commit()
    s.refresh(p)
    return ProjectNode(
        id=p.id,
        folder_id=p.folder_id,
        parent_id=p.parent_id,
        name=p.name,
        tone=p.tone,
        sort_order=p.sort_order,
        pinned=p.pinned,
        task_count=0,
        children=[],
    )
