// frontend/components/tasks/TasksClient.tsx — 작업 페이지 오케스트레이터
"use client";
import { useEffect, useMemo, useRef, useState } from "react";
import { useParams } from "next/navigation";
import useSWR, { useSWRConfig } from "swr";
import { Icon } from "@/components/Icon";
import { cx } from "@/lib/cx";
import { tasksApi } from "@/lib/tasks/api";
import { loadLS, saveLS } from "@/lib/tasks/store";
import { buildIndex, findPath } from "@/lib/tasks/tree";
import type { Folder, Person, Project, Risk, Status, Task } from "@/lib/types";
import { DetailPanel } from "./DetailPanel";
import { KanbanView } from "./KanbanView";
import { ListView } from "./ListView";
import { RiskRadar } from "./RiskRadar";
import { TaskSidebar } from "./TaskSidebar";

const SCOPES: Record<string, { label: string; icon: string }> = {
  work: { label: "업무", icon: "folder" },
  life: { label: "개인", icon: "heart" },
  fav: { label: "즐겨찾기", icon: "star" },
};

function flattenProjects(folders: Folder[]): Project[] {
  const out: Project[] = [];
  const walk = (ps: Project[]) => {
    for (const p of ps) {
      out.push(p);
      if (p.children?.length) walk(p.children);
    }
  };
  folders.forEach((f) => walk(f.projects));
  return out;
}

export function TasksClient() {
  const { mutate } = useSWRConfig();

  // UI 상태 (localStorage)
  const [view, setView] = useState<"kanban" | "list">("kanban");
  const [sel, setSel] = useState<string>("work");
  const [expanded, setExpanded] = useState<Record<string, boolean>>({ work: true, life: true, biz: true, onb: true });
  const [q, setQ] = useState("");
  const [hydrated, setHydrated] = useState(false);
  const [focusId, setFocusId] = useState<string | null>(null);
  const [rootId, setRootId] = useState<string | null>(null);
  const shellRef = useRef<HTMLDivElement>(null);
  const notesTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const didMount = useRef(false);

  // URL 로 열린 작업을 표현 — /tasks/<id> 의 <id> 세그먼트(옵셔널 캐치올).
  // 주소 갱신은 router 대신 history.replaceState(얕은 갱신) — router.replace 는 이 라우트를
  // 리마운트시켜 뷰/선택 상태가 날아가고 open 루프가 발생하기 때문.
  const params = useParams();
  const routeId = Array.isArray(params.slug) ? params.slug[0] : undefined;

  // 클라이언트에서만 localStorage 복원 (SSR 안정)
  useEffect(() => {
    setView(loadLS("view", "kanban"));
    setSel(loadLS("sel", "work"));
    setExpanded({
      work: true,
      life: true,
      biz: true,
      onb: true,
      ...loadLS<Record<string, boolean>>("expanded", {}),
    });
    setHydrated(true);
  }, []);
  useEffect(() => {
    if (hydrated) saveLS("view", view);
  }, [view, hydrated]);
  useEffect(() => {
    if (hydrated) saveLS("sel", sel);
  }, [sel, hydrated]);
  useEffect(() => {
    if (hydrated) saveLS("expanded", expanded);
  }, [expanded, hydrated]);

  useEffect(() => {
    const el = shellRef.current;
    const id = requestAnimationFrame(() => el && el.classList.add("entered"));
    return () => cancelAnimationFrame(id);
  }, []);

  // 서버 데이터
  const { data: folders, error: treeErr } = useSWR<Folder[]>("tasks/tree", tasksApi.tree);
  const { data: people } = useSWR<Person[]>("tasks/people", tasksApi.people);
  const {
    data: allTasks,
    error: tasksErr,
    isLoading: tasksLoading,
  } = useSWR<Task[]>("tasks/all", () => tasksApi.list());

  const ix = useMemo(() => (folders ? buildIndex(folders) : null), [folders]);
  const projRecord = useMemo<Record<string, Project>>(() => {
    const rec: Record<string, Project> = {};
    if (folders) flattenProjects(folders).forEach((p) => (rec[p.id] = p));
    return rec;
  }, [folders]);
  const peopleRecord = useMemo<Record<string, Person>>(() => {
    const rec: Record<string, Person> = {};
    (people || []).forEach((p) => (rec[p.id] = p));
    return rec;
  }, [people]);
  const me = useMemo<Person>(
    () =>
      (people || []).find((p) => p.is_me) || {
        id: "jiwoo",
        name: "지우",
        initial: "지",
        color: "var(--blue)",
        is_me: true,
      },
    [people],
  );
  const pinnedProjects = useMemo(
    () => (folders ? flattenProjects(folders).filter((p) => p.pinned) : []),
    [folders],
  );

  const tasks = useMemo(() => allTasks || [], [allTasks]);

  // 스코프 판정
  const sphereOf = (id: string) => {
    if (!ix) return null;
    const path = ix.pathOf(id);
    return path.length ? path[0].id : null;
  };
  const activeSphere =
    sel === "fav" ? null : sel === "work" || sel === "life" ? sel : sphereOf(sel);

  // 리스크 — 업무 스코프에서만
  const { data: risks } = useSWR<Risk[]>(
    activeSphere === "work" ? "risks/work" : null,
    () => tasksApi.risks("work"),
  );

  // counts (top-level 미완료 작업 기준)
  const counts = useMemo<Record<string, number>>(() => {
    const c: Record<string, number> = {};
    if (!ix) return c;
    for (const rec of ix.projects) {
      c[rec.id] = tasks.filter(
        (t) => t.status !== "done" && ix.isDescendant(t.project_id, rec.id),
      ).length;
    }
    return c;
  }, [ix, tasks]);

  // 스코프 필터
  const inScope = (t: Task): boolean => {
    if (!ix) return false;
    if (sel === "fav") {
      return pinnedProjects.some((pr) => ix.isDescendant(t.project_id, pr.id));
    }
    return ix.isDescendant(t.project_id, sel);
  };
  const scopeAll = tasks.filter(inScope);
  const doneCount = scopeAll.filter((t) => t.status === "done").length;
  const filtered = scopeAll.filter(
    (t) => !q.trim() || t.title.toLowerCase().includes(q.trim().toLowerCase()),
  );

  const isScope = !!SCOPES[sel];
  const rec = isScope || !ix ? null : ix.byId[sel];
  const path = isScope || !ix ? [] : ix.pathOf(sel);

  // ---- 핸들러 ----
  const open = (id: string) => {
    setRootId(id);
    setFocusId(id);
  };
  const close = () => {
    setRootId(null);
    setFocusId(null);
  };

  // URL → 작업: routeId(주소의 <id>)가 바뀌면 해당 작업 상세를 연다(직접 입력·뒤로가기·딥링크).
  useEffect(() => {
    if (!routeId) {
      close();
      return;
    }
    if (routeId === focusId) return; // 이미 열려 있음
    open(routeId);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [routeId]);

  // 작업 → URL: focusId 가 바뀌면 주소를 맞춘다(초기 마운트는 위 effect 가 처리하므로 skip).
  useEffect(() => {
    if (!didMount.current) {
      didMount.current = true;
      return;
    }
    const target = focusId ? `/tasks/${focusId}` : "/tasks";
    if (window.location.pathname !== target) window.history.replaceState(null, "", target);
  }, [focusId]);
  const refresh = () => {
    mutate("tasks/all");
    mutate("tasks/tree");
    if (activeSphere === "work") mutate("risks/work");
  };

  const onMove = async (id: string, status: Status) => {
    await tasksApi.patch(id, { status });
    refresh();
  };
  const onField = async (id: string, patch: Partial<Task>) => {
    await tasksApi.patch(id, patch);
    refresh();
  };
  const onCheck = async (id: string) => {
    const node = findPath(tasks, id)?.slice(-1)[0];
    if (!node) return;
    await tasksApi.patch(id, { status: node.status === "done" ? "todo" : "done" });
    refresh();
  };
  const repProject = (): string => {
    if (sel === "work") return "biz";
    if (sel === "life" || sel === "fav") return "me";
    return sel;
  };
  const onAdd = async (status: Status, title: string) => {
    const created = await tasksApi.create({
      title,
      project_id: repProject(),
      status,
      assignee_id: "jiwoo",
      prio: "보통",
    });
    refresh();
    return created.id;
  };
  const newTask = async () => {
    const id = await onAdd("todo", "새 작업");
    setView("kanban");
    setTimeout(() => open(id), 80);
  };
  const onAddChild = async (parentId: string, title: string) => {
    const parent = findPath(tasks, parentId)?.slice(-1)[0];
    await tasksApi.create({
      title,
      project_id: parent ? parent.project_id : "me",
      parent_id: parentId,
      status: "todo",
      assignee_id: parent?.assignee_id || "jiwoo",
      prio: "보통",
    });
    refresh();
  };
  const onDelegate = async (id: string, who: string) => {
    await tasksApi.patch(id, { assignee_id: who, status: "waiting", delegated: true });
    refresh();
  };
  const onDelete = async (id: string) => {
    const pth = findPath(tasks, id);
    const parent = pth && pth.length > 1 ? pth[pth.length - 2] : null;
    await tasksApi.remove(id);
    refresh();
    if (id === rootId) close();
    else if (parent) setFocusId(parent.id);
  };
  const onNotes = (id: string, html: string) => {
    if (notesTimer.current) clearTimeout(notesTimer.current);
    notesTimer.current = setTimeout(() => {
      tasksApi.patch(id, { notes: html }).then(() => mutate("tasks/all"));
    }, 800);
  };
  const onAddComment = async (id: string, text: string) => {
    await tasksApi.addComment(id, { person_id: "jiwoo", text });
    mutate("tasks/all");
  };
  const onEditComment = async (id: string, cid: string, text: string) => {
    await tasksApi.editComment(id, cid, text);
    mutate("tasks/all");
  };
  const onDeleteComment = async (id: string, cid: string) => {
    await tasksApi.deleteComment(id, cid);
    mutate("tasks/all");
  };
  // 같은 컬럼 내 드래그 정렬 → sort_order 재배치(낙관적 후 refresh).
  const onReorder = async (ids: string[]) => {
    await tasksApi.reorder(ids);
    refresh();
  };
  const onAddProject = async (folderId: string, name: string) => {
    await tasksApi.createProject({ folder_id: folderId, name });
    setExpanded((e) => ({ ...e, [folderId]: true }));
    mutate("tasks/tree");
  };
  const onPin = async (id: string) => {
    await tasksApi.pinProject(id);
    mutate("tasks/tree");
  };
  const onToggle = (id: string) => setExpanded((e) => ({ ...e, [id]: !e[id] }));

  const slimIcon = rec
    ? rec.hasChildren
      ? "folder"
      : "hash"
    : isScope
      ? SCOPES[sel].icon
      : "inbox";

  return (
    <div ref={shellRef}>
      <div className="twork">
        {folders && people ? (
          <TaskSidebar
            folders={folders}
            me={me}
            sel={sel}
            onSelect={setSel}
            expanded={expanded}
            onToggle={onToggle}
            onPin={onPin}
            counts={counts}
            pinnedProjects={pinnedProjects}
            onAddProject={onAddProject}
          />
        ) : (
          <aside className="subnav">
            <div className="sn-title">작업</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {[0, 1, 2].map((i) => (
                <div key={i} className="tsk-skel" style={{ height: 36 }} />
              ))}
            </div>
          </aside>
        )}

        <main className="tmain">
          {/* 브레드크럼 */}
          <div className="crumbs">
            <button onClick={() => setSel(activeSphere || "work")}>워크스페이스</button>
            {isScope ? (
              <>
                <span className="sep">
                  <Icon name="chev" />
                </span>
                <span className="cur">{SCOPES[sel].label}</span>
              </>
            ) : (
              path.map((pr, i) => (
                <span key={pr.id} style={{ display: "inline-flex", alignItems: "center", gap: 5 }}>
                  <span className="sep">
                    <Icon name="chev" />
                  </span>
                  {i === path.length - 1 ? (
                    <span className="cur">
                      <span className="c-dot" style={{ background: `var(--${pr.tone})` }} /> {pr.name}
                    </span>
                  ) : (
                    <button onClick={() => setSel(pr.id)}>{pr.name}</button>
                  )}
                </span>
              ))
            )}
          </div>

          {/* 슬림 헤더 */}
          <div className="thead-slim">
            <span
              className="ts-ic"
              style={rec ? { color: `var(--${rec.tone})` } : { color: "var(--muted)" }}
            >
              <Icon name={slimIcon as never} />
            </span>
            <span className="ts-meta">
              {scopeAll.length}개 작업 · {doneCount}개 완료
            </span>
            {rec && (
              <button
                className={cx("th-pin", projRecord[sel]?.pinned && "on")}
                onClick={() => onPin(sel)}
              >
                <Icon name="star" w={projRecord[sel]?.pinned ? 0 : 1.9} fill={projRecord[sel]?.pinned ? "currentColor" : "none"} />{" "}
                {projRecord[sel]?.pinned ? "즐겨찾기됨" : "즐겨찾기"}
              </button>
            )}
          </div>

          {/* 툴바 */}
          <div className="ttoolbar">
            <div className="view-seg">
              <button className={cx(view === "kanban" && "on")} onClick={() => setView("kanban")}>
                <Icon name="grid" /> 칸반
              </button>
              <button className={cx(view === "list" && "on")} onClick={() => setView("list")}>
                <Icon name="list" /> 리스트
              </button>
            </div>
            <div className="t-search">
              <Icon name="search" />
              <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="작업 검색…" />
            </div>
            <button className="t-new" onClick={newTask}>
              <Icon name="plus" /> 새 작업
            </button>
          </div>

          {/* 에러 */}
          {(treeErr || tasksErr) && (
            <div className="tsk-error">
              <Icon name="x" /> 작업을 불러오지 못했어요
              <button onClick={() => refresh()}>다시 시도</button>
            </div>
          )}

          {/* 리스크 레이더 — 업무 스코프에서만 */}
          {activeSphere === "work" && risks && <RiskRadar risks={risks} onOpen={open} />}

          {/* 본문 */}
          {tasksLoading && !allTasks ? (
            <div className="kboard">
              {[0, 1, 2, 3, 4].map((i) => (
                <div key={i} className="tsk-skel" style={{ height: 200 }} />
              ))}
            </div>
          ) : view === "kanban" ? (
            <KanbanView
              tasks={filtered}
              people={peopleRecord}
              projects={projRecord}
              onOpen={open}
              onMove={onMove}
              onAdd={onAdd}
              onReorder={onReorder}
            />
          ) : (
            <ListView
              tasks={filtered}
              people={peopleRecord}
              projects={projRecord}
              onOpen={open}
            />
          )}
        </main>
      </div>

      {focusId && (
        <DetailPanel
          tasks={tasks}
          focusId={focusId}
          rootId={rootId}
          people={peopleRecord}
          projects={projRecord}
          me={me}
          onFocus={setFocusId}
          onClose={close}
          onCheck={onCheck}
          onField={onField}
          onAddChild={onAddChild}
          onScaffolded={refresh}
          onDelegate={onDelegate}
          onDelete={onDelete}
          onNotes={onNotes}
          onAddComment={onAddComment}
          onEditComment={onEditComment}
          onDeleteComment={onDeleteComment}
        />
      )}
    </div>
  );
}
