# backend/app/routers/tasks.py
import uuid
from datetime import UTC, datetime

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

from ..auth.deps import current_user
from ..auth.scope import owned_or_404, scoped
from ..db import get_session
from ..models import Person, Project, Task, TaskComment, TaskStatus
from ..schemas import (
    CommentCreate,
    CommentOut,
    RiskOut,
    ScaffoldOut,
    ScaffoldRequest,
    TaskCreate,
    TaskNode,
    TaskPatch,
)
from ..services.risk import compute_risks
from ..services.scaffold import pick_scaffold, scaffold_create

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


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


def folder_of_project(s: Session, project_id: str) -> str | None:
    p = s.get(Project, project_id)
    return p.folder_id if p else None


def project_subtree_ids(s: Session, root_id: str) -> set[str]:
    """root_id 와 그 모든 하위 프로젝트 id 집합."""
    all_projects = s.exec(select(Project)).all()
    by_parent: dict[str | None, list[Project]] = {}
    for p in all_projects:
        by_parent.setdefault(p.parent_id, []).append(p)
    ids: set[str] = set()

    def walk(pid: str):
        ids.add(pid)
        for ch in by_parent.get(pid, []):
            walk(ch.id)

    walk(root_id)
    return ids


def to_node(s: Session, t: Task, all_tasks: list[Task]) -> TaskNode:
    kids = sorted([x for x in all_tasks if x.parent_id == t.id], key=lambda x: x.sort_order)
    comments = s.exec(
        select(TaskComment)
        .where(TaskComment.task_id == t.id)
        .order_by(TaskComment.created_at, TaskComment.id)
    ).all()
    return TaskNode(
        id=t.id,
        project_id=t.project_id,
        parent_id=t.parent_id,
        title=t.title,
        status=t.status,
        assignee_id=t.assignee_id,
        due=t.due,
        prio=t.prio,
        notes=t.notes,
        est=t.est,
        delegated=t.delegated,
        sort_order=t.sort_order,
        created_at=t.created_at,
        updated_at=t.updated_at,
        comments=[CommentOut.model_validate(c, from_attributes=True) for c in comments],
        children=[to_node(s, k, all_tasks) for k in kids],
    )


@router.get("/tasks", response_model=list[TaskNode])
def list_tasks(
    area: str | None = None,
    project_id: str | None = None,
    status: TaskStatus | None = None,
    assignee: str | None = None,
    s: Session = Depends(get_session),
    user: Person = Depends(current_user),
):
    all_tasks = s.exec(scoped(select(Task), Task, user.id)).all()  # phase-15 소유자 스코프
    roots = [t for t in all_tasks if t.parent_id is None]
    # 필터 우선순위: project_id(+하위 프로젝트) > area > 전체. status/assignee 는 추가 필터.
    if project_id:
        scope = project_subtree_ids(s, project_id)
        roots = [t for t in roots if t.project_id in scope]
    elif area:
        roots = [t for t in roots if folder_of_project(s, t.project_id) == area]
    if status:
        roots = [t for t in roots if t.status == status]
    if assignee:
        roots = [t for t in roots if t.assignee_id == assignee]
    roots.sort(key=lambda t: t.sort_order)
    return [to_node(s, t, all_tasks) for t in roots]


@router.get("/tasks/{tid}", response_model=TaskNode)
def get_task(tid: str, s: Session = Depends(get_session), user: Person = Depends(current_user)):
    t = owned_or_404(s.get(Task, tid), user.id)  # 타인 소유 → 404(IDOR 방어)
    return to_node(s, t, s.exec(scoped(select(Task), Task, user.id)).all())


@router.post("/tasks", response_model=TaskNode)
def create_task(
    body: TaskCreate, s: Session = Depends(get_session), user: Person = Depends(current_user)
):
    if not s.get(Project, body.project_id):
        raise HTTPException(404, "project not found")
    if body.parent_id and not s.get(Task, body.parent_id):
        raise HTTPException(404, "parent task not found")
    siblings = s.exec(
        select(Task).where(Task.parent_id == body.parent_id, Task.project_id == body.project_id)
    ).all()
    order = max([x.sort_order for x in siblings] + [-1]) + 1
    t = Task(
        id=_tid(),
        user_id=user.id,  # phase-15: 소유자는 항상 current_user(요청 바디 무시)
        title=body.title,
        project_id=body.project_id,
        parent_id=body.parent_id,
        status=body.status or TaskStatus.todo,
        assignee_id=body.assignee_id,
        due=body.due,
        prio=body.prio,
        notes=body.notes or "",
        est=body.est or "",
        sort_order=order,
    )
    s.add(t)
    s.commit()
    return to_node(s, t, s.exec(select(Task)).all())


@router.patch("/tasks/{tid}", response_model=TaskNode)
def patch_task(tid: str, body: TaskPatch, s: Session = Depends(get_session)):
    t = s.get(Task, tid)
    if not t:
        raise HTTPException(404, "task not found")
    data = body.model_dump(exclude_none=True)
    # parent_id 이동 시 순환(자기 자신/후손으로 이동) 금지 — to_node 무한재귀 방지
    if "parent_id" in data and data["parent_id"]:
        if data["parent_id"] == tid:
            raise HTTPException(400, "cannot parent task to itself")
        cur = s.get(Task, data["parent_id"])
        while cur:
            if cur.id == tid:
                raise HTTPException(400, "cannot move into own descendant")
            cur = s.get(Task, cur.parent_id) if cur.parent_id else None
    for k, v in data.items():
        setattr(t, k, v)
    t.updated_at = datetime.now(UTC)
    s.add(t)
    s.commit()
    return to_node(s, t, s.exec(select(Task)).all())


@router.delete("/tasks/{tid}")
def delete_task(tid: str, s: Session = Depends(get_session)):
    t = s.get(Task, tid)
    if not t:
        raise HTTPException(404, "task not found")
    # 하위작업 재귀 삭제
    all_tasks = s.exec(select(Task)).all()

    def collect(pid):
        ids = [pid]
        for c in [x for x in all_tasks if x.parent_id == pid]:
            ids += collect(c.id)
        return ids

    for did in collect(tid):
        for c in s.exec(select(TaskComment).where(TaskComment.task_id == did)).all():
            s.delete(c)
        d = s.get(Task, did)
        s.delete(d)
    s.commit()
    return {"deleted": tid}


@router.post("/tasks/{tid}/comments", response_model=CommentOut)
def add_comment(tid: str, body: CommentCreate, s: Session = Depends(get_session)):
    if not s.get(Task, tid):
        raise HTTPException(404, "task not found")
    c = TaskComment(
        id="c-" + uuid.uuid4().hex[:8], task_id=tid, person_id=body.person_id, text=body.text
    )
    s.add(c)
    s.commit()
    s.refresh(c)
    return CommentOut.model_validate(c, from_attributes=True)


@router.post("/tasks/{tid}/scaffold", response_model=ScaffoldOut)
def scaffold(tid: str, body: ScaffoldRequest, s: Session = Depends(get_session)):
    t = s.get(Task, tid)
    if not t:
        raise HTTPException(404, "task not found")
    tpl = pick_scaffold(t.title, use_llm=body.use_llm)
    if body.create:
        ids = scaffold_create(s, t, tpl)
        return ScaffoldOut(
            kind=tpl["kind"],
            icon=tpl["icon"],
            items=tpl["items"],
            created=True,
            created_task_ids=ids,
        )
    return ScaffoldOut(kind=tpl["kind"], icon=tpl["icon"], items=tpl["items"], created=False)


@router.get("/risks", response_model=list[RiskOut])
def risks(area: str = "work", s: Session = Depends(get_session)):
    # area 기본값 work. 주어진 area 로 리스크 계산.
    return compute_risks(s, area=area)
