"use client";
import useSWR from "swr";
import { useMemo, useState } from "react";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

export default function AdminBoardsPage() {
  const { data: boardsResp, mutate: mutateBoards } = useSWR<{ boards: any[] }>("/api/admin/boards", fetcher);
  const { data: catsResp, mutate: mutateCats } = useSWR<{ categories: any[] }>("/api/admin/categories", fetcher);
  const boards = boardsResp?.boards ?? [];
  const categories = (catsResp?.categories ?? []).sort((a: any, b: any) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
  const groups = useMemo(() => {
    const map: Record<string, any[]> = {};
    for (const b of boards) {
      const cid = b.categoryId ?? "uncat";
      if (!map[cid]) map[cid] = [];
      map[cid].push(b);
    }
    return categories.map((c: any) => ({ ...c, items: (map[c.id] ?? []).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)) }));
  }, [boards, categories]);

  const [savingId, setSavingId] = useState<string | null>(null);
  const [dirtyBoards, setDirtyBoards] = useState<Record<string, any>>({});
  const [dirtyCats, setDirtyCats] = useState<Record<string, any>>({});
  const [savingAll, setSavingAll] = useState(false);
  async function save(b: any) {
    setSavingId(b.id);
    await fetch(`/api/admin/boards/${b.id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(b) });
    setSavingId(null);
    mutateBoards();
  }

  // DnD: 카테고리 순서 변경
  async function reorderCategories(next: any[]) {
    // optimistic update
    await Promise.all(next.map((c, idx) => fetch(`/api/admin/categories/${c.id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ sortOrder: idx + 1 }) })));
    mutateCats();
  }

  // DnD: 보드 순서 변경 (카테고리 내부)
  async function reorderBoards(categoryId: string, nextItems: any[]) {
    await Promise.all(nextItems.map((b, idx) => fetch(`/api/admin/boards/${b.id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ sortOrder: idx + 1, categoryId }) })));
    mutateBoards();
  }

  function markBoardDirty(id: string, draft: any) {
    setDirtyBoards((prev) => ({ ...prev, [id]: draft }));
  }
  function markCatDirty(id: string, draft: any) {
    setDirtyCats((prev) => ({ ...prev, [id]: draft }));
  }
  async function saveAll() {
    try {
      setSavingAll(true);
      const boardEntries = Object.entries(dirtyBoards);
      const catEntries = Object.entries(dirtyCats);
      await Promise.all([
        ...boardEntries.map(([id, payload]) => fetch(`/api/admin/boards/${id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(payload) })),
        ...catEntries.map(([id, payload]) => fetch(`/api/admin/categories/${id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(payload) })),
      ]);
      setDirtyBoards({});
      setDirtyCats({});
      mutateBoards();
      mutateCats();
    } finally {
      setSavingAll(false);
    }
  }

  return (
    <div className="space-y-6">
      <h1 className="text-xl md:text-2xl font-bold text-neutral-900">게시판 관리</h1>
      {/* 변경사항 저장 바 */}
      {(Object.keys(dirtyBoards).length + Object.keys(dirtyCats).length) > 0 && (
        <div className="sticky top-2 z-10 flex justify-end">
          <button
            onClick={saveAll}
            disabled={savingAll}
            className="h-9 px-4 rounded-md bg-neutral-900 text-white text-sm shadow disabled:opacity-60"
          >{savingAll ? "저장 중..." : `변경사항 저장 (${Object.keys(dirtyBoards).length + Object.keys(dirtyCats).length})`}</button>
        </div>
      )}

      {/* 대분류 리스트 (드래그로 순서 변경) */}
      <div className="rounded-xl border border-neutral-200 overflow-hidden bg-white">
        <div className="px-4 py-2 border-b border-neutral-200 text-sm font-semibold">대분류</div>
        <ul className="divide-y divide-neutral-100">
          {groups.map((g, idx) => (
            <CategoryRow key={g.id} idx={idx} g={g} onMove={(from, to) => {
              const arr = [...groups];
              const [moved] = arr.splice(from, 1);
              arr.splice(to, 0, moved);
              reorderCategories(arr);
            }} onDirty={(payload) => markCatDirty(g.id, { ...payload })} />
          ))}
        </ul>
      </div>

      {groups.map((g) => (
        <section key={g.id} className="rounded-xl border border-neutral-200 overflow-hidden bg-white">
          <div className="px-4 py-2 border-b border-neutral-200 flex items-center justify-between">
            <div className="text-sm font-semibold">대분류: {g.name}</div>
            <div className="text-xs text-neutral-500">slug: {g.slug}</div>
          </div>
          <div className="overflow-x-auto">
            <table className="min-w-full text-sm">
              <thead className="text-xs text-neutral-500 border-b border-neutral-200">
                <tr>
                  <th className="px-3 py-2 text-left">이름</th>
                  <th className="px-3 py-2 text-left">slug</th>
                  <th className="px-3 py-2">읽기</th>
                  <th className="px-3 py-2">쓰기</th>
                  <th className="px-3 py-2">익명</th>
                  <th className="px-3 py-2">비밀댓</th>
                  <th className="px-3 py-2">승인</th>
                  <th className="px-3 py-2">유형</th>
                  <th className="px-3 py-2">성인</th>
                  <th className="px-3 py-2">정렬</th>
                  <th className="px-3 py-2"></th>
                </tr>
              </thead>
              <tbody className="divide-y divide-neutral-100">
                {g.items.map((b, i) => (
                  <DraggableRow
                    key={b.id}
                    index={i}
                    onMove={(from, to) => {
                      const list = [...g.items];
                      const [mv] = list.splice(from, 1);
                      list.splice(to, 0, mv);
                      reorderBoards(g.id, list);
                    }}
                  >
                    <BoardRowCells b={b} onDirty={(id, draft) => markBoardDirty(id, draft)} />
                  </DraggableRow>
                ))}
              </tbody>
            </table>
          </div>
        </section>
      ))}
    </div>
  );
}

function BoardRowCells({ b, onDirty }: { b: any; onDirty: (id: string, draft: any) => void }) {
  const [edit, setEdit] = useState(b);
  return (
    <>
      <td className="px-3 py-2"><input className="h-9 w-full rounded-md border border-neutral-300 px-2 text-sm" value={edit.name} onChange={(e) => { const v = { ...edit, name: e.target.value }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2"><input className="h-9 w-full rounded-md border border-neutral-300 px-2 text-sm" value={edit.slug} onChange={(e) => { const v = { ...edit, slug: e.target.value }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2 text-center">
        <select className="h-9 rounded-md border border-neutral-300 px-2 text-sm" value={edit.readLevel} onChange={(e) => { const v = { ...edit, readLevel: e.target.value }; setEdit(v); onDirty(b.id, v); }}>
          <option value="public">public</option>
          <option value="member">member</option>
          <option value="moderator">moderator</option>
          <option value="admin">admin</option>
        </select>
      </td>
      <td className="px-3 py-2 text-center">
        <select className="h-9 rounded-md border border-neutral-300 px-2 text-sm" value={edit.writeLevel} onChange={(e) => { const v = { ...edit, writeLevel: e.target.value }; setEdit(v); onDirty(b.id, v); }}>
          <option value="public">public</option>
          <option value="member">member</option>
          <option value="moderator">moderator</option>
          <option value="admin">admin</option>
        </select>
      </td>
      <td className="px-3 py-2 text-center"><input type="checkbox" checked={edit.allowAnonymousPost} onChange={(e) => { const v = { ...edit, allowAnonymousPost: e.target.checked }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2 text-center"><input type="checkbox" checked={edit.allowSecretComment} onChange={(e) => { const v = { ...edit, allowSecretComment: e.target.checked }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2 text-center"><input type="checkbox" checked={edit.requiresApproval} onChange={(e) => { const v = { ...edit, requiresApproval: e.target.checked }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2 text-center">
        <select className="h-9 rounded-md border border-neutral-300 px-2 text-sm" value={edit.type} onChange={(e) => { const v = { ...edit, type: e.target.value }; setEdit(v); onDirty(b.id, v); }}>
          <option value="general">general</option>
          <option value="special">special</option>
        </select>
      </td>
      <td className="px-3 py-2 text-center"><input type="checkbox" checked={!!edit.isAdultOnly} onChange={(e) => { const v = { ...edit, isAdultOnly: e.target.checked }; setEdit(v); onDirty(b.id, v); }} /></td>
      <td className="px-3 py-2 text-center"><input className="h-9 w-20 rounded-md border border-neutral-300 px-2 text-sm" type="number" value={edit.sortOrder} onChange={(e) => { const v = { ...edit, sortOrder: Number(e.target.value) }; setEdit(v); onDirty(b.id, v); }} /></td>
    </>
  );
}

function DraggableRow({ index, onMove, children }: { index: number; onMove: (from: number, to: number) => void; children: React.ReactNode }) {
  return (
    <tr
      draggable
      onDragStart={(e) => {
        e.dataTransfer.setData("text/plain", String(index));
        e.dataTransfer.effectAllowed = "move";
        // 수직만 보이게: 실제 행을 고정 포지션으로 띄워 따라오게 함 + 전역 placeholder 등록
        const row = e.currentTarget as HTMLTableRowElement;
        const rect = row.getBoundingClientRect();
        const table = row.closest('table') as HTMLElement | null;
        const tableRect = table?.getBoundingClientRect();
        // placeholder로 자리를 유지 (전역으로 참조)
        const placeholder = document.createElement('tr');
        placeholder.style.height = `${rect.height}px`;
        (row.parentNode as HTMLElement).insertBefore(placeholder, row);
        (window as any).__adminDnd = { placeholder, dragging: row, target: null, before: false, rAF: 0 };
        // 행을 고정 배치
        const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));
        const offsetY = e.clientY - rect.top;
        row.style.position = 'fixed';
        row.style.left = `${tableRect ? tableRect.left : rect.left}px`;
        row.style.width = `${tableRect ? tableRect.width : rect.width}px`;
        row.style.zIndex = '9999';
        row.classList.add('bg-white');
        const updatePos = (clientY: number) => {
          const top = clamp(clientY - offsetY, (tableRect?.top ?? 0), (tableRect?.bottom ?? (rect.top + rect.height)) - rect.height);
          row.style.top = `${top}px`;
        };
        updatePos(e.clientY);
        // 기본 드래그 이미지는 투명 1x1로 숨김
        const img = document.createElement('canvas');
        img.width = 1; img.height = 1; const ctx = img.getContext('2d'); ctx?.clearRect(0,0,1,1);
        e.dataTransfer.setDragImage(img, 0, 0);
        const onDragOver = (ev: DragEvent) => {
          if (typeof ev.clientY === 'number') updatePos(ev.clientY);
        };
        const cleanup = () => {
          row.style.position = '';
          row.style.left = '';
          row.style.top = '';
          row.style.width = '';
          row.style.zIndex = '';
          row.classList.remove('bg-white');
          placeholder.remove();
          window.removeEventListener('dragover', onDragOver, true);
          window.removeEventListener('dragend', cleanup, true);
          const st = (window as any).__adminDnd; if (st?.rAF) cancelAnimationFrame(st.rAF);
          (window as any).__adminDnd = undefined;
        };
        window.addEventListener('dragover', onDragOver, true);
        window.addEventListener('dragend', cleanup, true);
      }}
      onDragOver={(e) => {
        // 수직만 허용: 기본 동작만 유지하고 수평 제스처는 무시
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
        const current = e.currentTarget as HTMLTableRowElement;
        const state = (window as any).__adminDnd || {};
        const ph: HTMLElement | undefined = state.placeholder;
        if (!ph || !current.parentElement) return;
        const r = current.getBoundingClientRect();
        const before = e.clientY < r.top + r.height / 2;
        // 목표만 저장하고 DOM 조작은 프레임당 1회 수행
        state.target = current;
        state.before = before;
        if (!state.rAF) {
          state.rAF = requestAnimationFrame(() => {
            const st = (window as any).__adminDnd || {};
            if (!st.placeholder || !st.target || !st.target.parentElement) { st.rAF = 0; return; }
            const parent = st.target.parentElement as HTMLElement;
            const desiredNode = st.before ? st.target : (st.target.nextSibling as any);
            if (desiredNode !== st.placeholder) {
              parent.insertBefore(st.placeholder, desiredNode || null);
              if (st.dragging) {
                const pr = st.placeholder.getBoundingClientRect();
                (st.dragging as HTMLElement).style.top = `${pr.top}px`;
              }
            }
            st.rAF = 0;
          });
        }
      }}
      onDrop={(e) => {
        e.preventDefault();
        const from = Number(e.dataTransfer.getData("text/plain"));
        const state = (window as any).__adminDnd || {};
        const ph: HTMLElement | undefined = state.placeholder;
        let to = index;
        if (ph && ph.parentElement) {
          to = Array.from(ph.parentElement.children).indexOf(ph);
          ph.remove();
        }
        if (!Number.isNaN(from) && from !== to) onMove(from, to);
      }}
      className="align-middle cursor-ns-resize select-none"
    >
      {children}
    </tr>
  );
}

function CategoryRow({ idx, g, onMove, onDirty }: { idx: number; g: any; onMove: (from: number, to: number) => void; onDirty: (payload: any) => void }) {
  const [edit, setEdit] = useState({ name: g.name, slug: g.slug });
  return (
    <li
      className="px-4 py-3 flex items-center gap-3 cursor-ns-resize select-none"
      draggable
      onDragStart={(e) => {
        e.dataTransfer.setData("text/plain", String(idx));
        e.dataTransfer.effectAllowed = "move";
        const item = e.currentTarget as HTMLLIElement;
        const rect = item.getBoundingClientRect();
        const listRect = item.parentElement?.getBoundingClientRect();
        // placeholder (전역 등록)
        const placeholder = document.createElement('div');
        placeholder.style.height = `${rect.height}px`;
        placeholder.style.border = '1px dashed rgba(0,0,0,0.1)';
        item.parentElement?.insertBefore(placeholder, item);
        (window as any).__adminDnd = { placeholder, dragging: item, target: null, before: false, rAF: 0 };
        // fix item position
        const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));
        const offsetY = e.clientY - rect.top;
        item.style.position = 'fixed';
        item.style.left = `${listRect ? listRect.left : rect.left}px`;
        item.style.width = `${listRect ? listRect.width : rect.width}px`;
        item.style.zIndex = '9999';
        const updatePos = (y: number) => {
          const top = clamp(y - offsetY, (listRect?.top ?? 0), (listRect?.bottom ?? (rect.top + rect.height)) - rect.height);
          item.style.top = `${top}px`;
        };
        updatePos(e.clientY);
        // hide default drag image
        const img = document.createElement('canvas'); img.width = 1; img.height = 1; e.dataTransfer.setDragImage(img, 0, 0);
        const onDragOver = (ev: DragEvent) => { if (typeof ev.clientY === 'number') updatePos(ev.clientY); };
        const cleanup = () => { const st = (window as any).__adminDnd; if (st?.rAF) cancelAnimationFrame(st.rAF); item.style.position=''; item.style.left=''; item.style.top=''; item.style.width=''; item.style.zIndex=''; placeholder.remove(); window.removeEventListener('dragover', onDragOver, true); window.removeEventListener('dragend', cleanup, true); (window as any).__adminDnd = undefined; };
        window.addEventListener('dragover', onDragOver, true);
        window.addEventListener('dragend', cleanup, true);
      }}
      onDragOver={(e) => { 
        e.preventDefault(); 
        e.dataTransfer.dropEffect = 'move';
        const state = (window as any).__adminDnd || {};
        const current = e.currentTarget as HTMLElement;
        const r = current.getBoundingClientRect();
        state.target = current;
        state.before = e.clientY < r.top + r.height / 2;
        if (!state.rAF) {
          state.rAF = requestAnimationFrame(() => {
            const st = (window as any).__adminDnd || {};
            if (!st.placeholder || !st.target || !st.target.parentElement) { st.rAF = 0; return; }
            const parent = st.target.parentElement as HTMLElement;
            const desiredNode = st.before ? st.target : (st.target.nextSibling as any);
            if (desiredNode !== st.placeholder) {
              parent.insertBefore(st.placeholder, desiredNode || null);
              if (st.dragging) {
                const pr = st.placeholder.getBoundingClientRect();
                (st.dragging as HTMLElement).style.top = `${pr.top}px`;
              }
            }
            st.rAF = 0;
          });
        }
      }}
      onDrop={(e) => {
        e.preventDefault();
        const from = Number(e.dataTransfer.getData("text/plain"));
        const state = (window as any).__adminDnd || {};
        const ph: HTMLElement | undefined = state.placeholder;
        let to = idx;
        if (ph && ph.parentElement) {
          to = Array.from(ph.parentElement.children).indexOf(ph);
          ph.remove();
        }
        if (!Number.isNaN(from) && from !== to) onMove(from, to);
      }}
    >
      <div className="w-6 text-xs text-neutral-500">≡</div>
      <input className="h-8 rounded-md border border-neutral-300 px-2 text-sm w-48" value={edit.name} onChange={(e) => { const v = { ...edit, name: e.target.value }; setEdit(v); onDirty(v); }} />
      <input className="h-8 rounded-md border border-neutral-300 px-2 text-sm w-48" value={edit.slug} onChange={(e) => { const v = { ...edit, slug: e.target.value }; setEdit(v); onDirty(v); }} />
      <div className="flex-1" />
    </li>
  );
}


