import { useState, useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient, getApiErrorMessage } from '../lib/apiClient';
import { onDualMonitorEvent, getPersistedTaskId } from '../lib/dualMonitor';
import { ContextMenu } from '../components/common/ContextMenu';
import { FeedbackModal, type FeedbackFormData } from '../components/detail/FeedbackModal';
import { ResultPreview } from '../components/detail/ResultPreview';
import {
  StageModal,
  parseMilestoneLinks,
  type StageFileSavePayload,
  type StageFormData,
} from '../components/detail/StageModal';
import { sortFilesByOrder } from '../lib/fileDisplay';
import { useAuth } from '../contexts/AuthContext';
import type { Task, Milestone, FileRecord, TaskDetail } from '../types';

const STATUS_CONFIG: Record<string, { label: string }> = {
  IN_PROGRESS: { label: '진행중' },
  REVIEW:      { label: '보류' },
  TODO:        { label: '대기' },
  DONE:        { label: '완료' },
  CANCELLED:   { label: '취소' },
};

type TaskWithRelations = Task & {
  files: FileRecord[];
  details: TaskDetail[];
  milestones: Milestone[];
};

function fmtDate(iso: string | null | undefined) {
  if (!iso) return '';
  const d = new Date(iso);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

function fmtShort(iso: string | null | undefined) {
  if (!iso) return '';
  const d = new Date(iso);
  return `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;
}

function fmtStageRange(m: Milestone) {
  if (m.startDate && m.dueDate) return `${fmtShort(m.startDate)} ~ ${fmtShort(m.dueDate)}`;
  if (m.dueDate) return fmtShort(m.dueDate);
  return fmtShort(m.createdAt);
}

function fmtTimelineLabel(iso: string | null | undefined) {
  if (!iso) return '';
  const d = new Date(iso);
  return `${d.getMonth() + 1}.${String(d.getDate()).padStart(2, '0')}`;
}

function sortByIsoDesc<T>(items: T[], pick: (item: T) => string) {
  return [...items].sort((a, b) => new Date(pick(b)).getTime() - new Date(pick(a)).getTime());
}

function milestoneProgress(m: Milestone) {
  if (m.completedAt) return 100;
  const p = m.progress ?? 0;
  return Math.min(100, Math.max(0, p));
}

function parseContentLines(text: string | null | undefined) {
  if (!text) return [];
  return text.split('\n').map((l) => l.replace(/^[•·\-]\s*/, '').trim()).filter(Boolean);
}

interface TimelineBar {
  id: string;
  label: string;
  left: number;
  width: number;
  progress: number;
  title: string;
}

function buildTimeline(task: Task, milestones: Milestone[]): {
  start: string;
  end: string;
  today: string;
  todayLeft: number;
  bars: TimelineBar[];
} | null {
  if (!task.startDate || !task.dueDate) return null;

  const startMs = new Date(task.startDate).getTime();
  const endMs = new Date(task.dueDate).getTime();
  const range = Math.max(endMs - startMs, 86400000);
  const now = Date.now();
  const todayLeft = Math.min(100, Math.max(0, ((now - startMs) / range) * 100));
  const ordered = [...milestones].sort((a, b) => a.order - b.order);

  const bars: TimelineBar[] = ordered.map((m, i) => {
    const barStart = m.startDate
      ? new Date(m.startDate).getTime()
      : i > 0 && ordered[i - 1].dueDate
        ? new Date(ordered[i - 1].dueDate!).getTime()
        : startMs;
    const barEnd = m.dueDate ? new Date(m.dueDate).getTime() : endMs;
    const left = Math.max(0, ((barStart - startMs) / range) * 100);
    const width = Math.max(4, ((barEnd - barStart) / range) * 100);
    return {
      id: m.id,
      label: `${i + 1}`,
      left,
      width,
      progress: milestoneProgress(m),
      title: m.title,
    };
  });

  const today = new Date();
  return {
    start: fmtTimelineLabel(task.startDate),
    end: fmtTimelineLabel(task.dueDate),
    today: `${today.getMonth() + 1}.${String(today.getDate()).padStart(2, '0')}`,
    todayLeft,
    bars,
  };
}

function Badge({ children, className = '' }: { children: React.ReactNode; className?: string }) {
  return (
    <span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-bold ${className}`}>
      {children}
    </span>
  );
}

function PanelLabel({ children, sub }: { children: React.ReactNode; sub?: string }) {
  return (
    <div className="mb-2 flex shrink-0 items-baseline justify-between gap-2 border-b border-slate-200 pb-2">
      <h3 className="text-sm font-black uppercase tracking-widest text-slate-500">{children}</h3>
      {sub && <span className="truncate text-sm font-bold text-emerald-600">{sub}</span>}
    </div>
  );
}

function LeftSection({ children }: { children: React.ReactNode }) {
  return (
    <section className="flex min-h-0 flex-col overflow-hidden border-b border-slate-200 px-4 py-3 last:border-b-0">
      {children}
    </section>
  );
}

function WaitingScreen() {
  return (
    <div className="flex h-full flex-col items-center justify-center gap-6 bg-[#eef2f5]">
      <div className="flex h-20 w-20 animate-pulse items-center justify-center rounded-full bg-emerald-100 text-4xl">←</div>
      <div className="text-center">
        <p className="text-2xl font-black text-slate-700">카드를 선택하세요</p>
        <p className="mt-2 text-lg font-medium text-slate-400">
          대시보드에서 업무 카드를 클릭하면<br />이곳에 상세 내용이 표시됩니다.
        </p>
      </div>
    </div>
  );
}

function DetailHeader({ task }: { task: Task }) {
  const status = STATUS_CONFIG[task.status] ?? STATUS_CONFIG.TODO;
  const period =
    task.startDate || task.dueDate
      ? `${fmtDate(task.startDate)} ~ ${fmtDate(task.dueDate)}`
      : '—';

  return (
    <header className="relative flex h-12 shrink-0 items-center overflow-hidden bg-[linear-gradient(180deg,#37a184_0%,#29724f_20%,#07412e_100%)] px-5 text-white shadow-[0_2px_10px_rgba(0,0,0,0.20)]">
      <div className="pointer-events-none absolute inset-x-0 top-0 h-[45%] bg-white/10" />
      <div className="pointer-events-none absolute inset-x-0 bottom-0 h-px bg-emerald-200/50" />

      <h1 className="relative z-10 min-w-0 truncate text-[20px] font-bold leading-normal text-[#bad8ca]">
        {task.title}
      </h1>

      <div className="relative z-10 ml-auto flex shrink-0 items-center gap-4 text-sm">
        <span className="whitespace-nowrap">
          <span className="font-semibold text-white/55">담당</span>{' '}
          <span className="font-bold text-white/90">{task.assignee?.name ?? '—'}</span>
        </span>
        <span className="h-4 w-px bg-white/25" />
        <span className="whitespace-nowrap">
          <span className="font-semibold text-white/55">수행기간</span>{' '}
          <span className="font-bold text-white/90">{period}</span>
        </span>
        <span className="h-4 w-px bg-white/25" />
        <span className="whitespace-nowrap">
          <span className="font-semibold text-white/55">부서</span>{' '}
          <span className="font-bold text-white/90">{task.section ?? '—'}</span>
        </span>
        <Badge className="border border-white/20 bg-white/10 text-white/90">{status.label}</Badge>
      </div>
    </header>
  );
}

function DetailView({ task }: { task: TaskWithRelations }) {
  const qc = useQueryClient();
  const { user } = useAuth();
  const [stageSaving, setStageSaving] = useState(false);
  const [feedbackSaving, setFeedbackSaving] = useState(false);
  const [stageModal, setStageModal] = useState<{ mode: 'add' | 'edit'; milestone?: Milestone } | null>(null);
  const [feedbackModal, setFeedbackModal] = useState<{ mode: 'add' | 'edit'; detail?: TaskDetail } | null>(null);
  const [ctxMenu, setCtxMenu] = useState<{ x: number; y: number; stageId: string } | null>(null);
  const [contentCtx, setContentCtx] = useState<{ x: number; y: number } | null>(null);
  const [feedbackCtx, setFeedbackCtx] = useState<{ x: number; y: number; detailId?: string } | null>(null);

  const milestones = task.milestones ?? [];
  const files = task.files ?? [];
  const details = task.details ?? [];

  const sortedStages = useMemo(
    () => sortByIsoDesc(milestones, (m) => m.updatedAt),
    [milestones],
  );

  const [selectedId, setSelectedId] = useState<string | null>(sortedStages[0]?.id ?? null);

  useEffect(() => {
    if (!selectedId || !sortedStages.some((s) => s.id === selectedId)) {
      setSelectedId(sortedStages[0]?.id ?? null);
    }
  }, [task.id, sortedStages, selectedId]);

  const selected = sortedStages.find((m) => m.id === selectedId) ?? sortedStages[0] ?? null;

  const stageContents = useMemo(() => {
    if (!selected?.description) return [];
    return parseContentLines(selected.description);
  }, [selected]);

  const stageDetails = useMemo(
    () => (selectedId ? details.filter((d) => d.milestoneId === selectedId) : []),
    [details, selectedId],
  );

  const sortedFeedbacks = useMemo(
    () => sortByIsoDesc(stageDetails, (d) => d.createdAt),
    [stageDetails],
  );

  const stageFiles = useMemo(
    () =>
      sortFilesByOrder(
        selectedId ? files.filter((f) => f.milestoneId === selectedId) : [],
      ),
    [files, selectedId],
  );

  const stageLinks = useMemo(
    () => (selected ? parseMilestoneLinks(selected.links) : []),
    [selected],
  );

  const timeline = useMemo(() => buildTimeline(task, milestones), [task, milestones]);

  const deleteMs = useMutation({
    mutationFn: (id: string) => apiClient.delete(`/milestones/item/${id}`),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['task', task.id] }),
  });

  const uploadFiles = async (milestoneId: string, filePayload: StageFileSavePayload['uploads']) => {
    for (const item of filePayload) {
      const form = new FormData();
      form.append('file', item.file);
      form.append('milestoneId', milestoneId);
      form.append('sortOrder', String(item.sortOrder));
      if (item.displayName.trim()) {
        form.append('displayName', item.displayName.trim());
      }
      await apiClient.post(`/files/upload/${task.id}`, form);
    }
  };

  const handleStageSave = async (data: StageFormData, filePayload: StageFileSavePayload) => {
    setStageSaving(true);
    try {
      const payload = {
        title: data.title.trim(),
        description: data.description.trim() || undefined,
        startDate: data.startDate || undefined,
        dueDate: data.dueDate || undefined,
        progress: data.progress,
        links: data.links.length > 0 ? JSON.stringify(data.links) : undefined,
      };

      let milestoneId: string;

      if (stageModal?.mode === 'add') {
        const { data: created } = await apiClient.post<Milestone>(`/milestones/${task.id}`, payload);
        milestoneId = created.id;
        setSelectedId(created.id);
      } else if (stageModal?.milestone) {
        const { data: updated } = await apiClient.patch<Milestone>(
          `/milestones/item/${stageModal.milestone.id}`,
          payload,
        );
        milestoneId = updated.id;
      } else {
        return;
      }

      try {
        for (const id of filePayload.deletedFileIds) {
          await apiClient.delete(`/files/${id}`);
        }
        for (const rep of filePayload.replacements) {
          const form = new FormData();
          form.append('file', rep.file);
          await apiClient.post(`/files/${rep.id}/replace`, form);
        }
        for (const edit of filePayload.existingEdits) {
          const original = files.find((f) => f.id === edit.id);
          if (!original) continue;
          const prevName = (original.displayName ?? '').trim();
          const nextName = edit.displayName.trim();
          const prevOrder = original.sortOrder ?? 0;
          if (nextName !== prevName || edit.sortOrder !== prevOrder) {
            await apiClient.patch(`/files/${edit.id}`, {
              displayName: nextName || null,
              sortOrder: edit.sortOrder,
            });
          }
        }
        if (filePayload.uploads.length > 0) {
          await uploadFiles(milestoneId, filePayload.uploads);
        }
      } catch (err: unknown) {
        alert(`단계는 저장됐지만 ${getApiErrorMessage(err, '파일 처리에 실패했습니다.')}`);
      }

      await qc.invalidateQueries({ queryKey: ['task', task.id] });
      setStageModal(null);
    } catch (err: unknown) {
      alert(getApiErrorMessage(err, '단계 저장에 실패했습니다.'));
    } finally {
      setStageSaving(false);
    }
  };

  const handleFeedbackSave = async (data: FeedbackFormData) => {
    if (!selectedId && feedbackModal?.mode === 'add') {
      alert('피드백을 추가할 업무 단계를 먼저 선택하세요.');
      return;
    }

    setFeedbackSaving(true);
    try {
      if (feedbackModal?.mode === 'add') {
        await apiClient.post(`/details/${task.id}`, {
          content: data.content.trim(),
          authorName: data.authorName.trim(),
          milestoneId: selectedId,
        });
      } else if (feedbackModal?.detail) {
        await apiClient.patch(`/details/item/${feedbackModal.detail.id}`, {
          content: data.content.trim(),
        });
      }
      await qc.invalidateQueries({ queryKey: ['task', task.id] });
      setFeedbackModal(null);
    } catch (err: unknown) {
      alert(getApiErrorMessage(err, '피드백 저장에 실패했습니다.'));
    } finally {
      setFeedbackSaving(false);
    }
  };

  const deleteFeedback = useMutation({
    mutationFn: (id: string) => apiClient.delete(`/details/item/${id}`),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['task', task.id] }),
  });

  const overview = task.description?.split('\n')[0]?.trim() || '등록된 개요가 없습니다.';

  return (
    <div className="grid h-full min-h-0 grid-cols-[1fr_3fr] grid-rows-1">
      {/* 좌 1/4 — 1:2:2:1 세로 비율 */}
      <aside className="grid h-full min-h-0 grid-rows-[1fr_2fr_2fr_1fr] overflow-hidden border-r border-slate-300 bg-white">
        <LeftSection>
          <PanelLabel>개요</PanelLabel>
          <p className="line-clamp-4 min-h-0 flex-1 overflow-hidden text-xl leading-snug text-slate-600">
            {overview}
          </p>
          {task.issueNote && (
            <p className="mt-1 truncate text-sm font-bold text-red-500">▶ {task.issueNote}</p>
          )}
        </LeftSection>

        <LeftSection>
          <div className="mb-2 flex shrink-0 items-center justify-between gap-2 border-b border-slate-200 pb-2">
            <h3 className="text-sm font-black uppercase tracking-widest text-slate-500">업무 단계</h3>
            <button
              type="button"
              title="업무 단계 추가"
              onClick={() => setStageModal({ mode: 'add' })}
              className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-emerald-500 text-lg font-bold leading-none text-white hover:bg-emerald-600"
            >
              +
            </button>
          </div>
          <div className="flex min-h-0 flex-1 flex-col gap-2 overflow-hidden">
            {sortedStages.length === 0 && (
              <p className="text-lg text-slate-400">+ 버튼으로 단계를 추가하세요.</p>
            )}
            {sortedStages.map((stage) => {
              const isSelected = stage.id === selectedId;
              const progress = milestoneProgress(stage);
              return (
                <button
                  key={stage.id}
                  type="button"
                  onClick={() => setSelectedId(stage.id)}
                  onContextMenu={(e) => {
                    e.preventDefault();
                    setCtxMenu({ x: e.clientX, y: e.clientY, stageId: stage.id });
                  }}
                  className={`shrink-0 rounded-lg border px-3 py-2 text-left transition-colors ${
                    isSelected
                      ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-300'
                      : 'border-slate-200 bg-slate-50 hover:border-slate-300 hover:bg-white'
                  }`}
                >
                  <div className="flex items-start justify-between gap-2">
                    <div className="min-w-0 flex-1">
                      <p className={`truncate text-2xl font-black leading-snug ${isSelected ? 'text-emerald-800' : 'text-slate-800'}`}>
                        {stage.title}
                      </p>
                      <p className="mt-0.5 text-sm font-semibold text-slate-400">{fmtStageRange(stage)}</p>
                    </div>
                    <span
                      className={`shrink-0 text-lg font-black ${
                        progress >= 100 ? 'text-emerald-600' : progress > 0 ? 'text-blue-500' : 'text-slate-300'
                      }`}
                    >
                      {progress}%
                    </span>
                  </div>
                </button>
              );
            })}
          </div>
        </LeftSection>

        <LeftSection>
          <PanelLabel>업무내용</PanelLabel>
          <ul
            className="min-h-0 flex-1 space-y-2 overflow-hidden"
            onContextMenu={(e) => {
              if (!selected) return;
              e.preventDefault();
              setContentCtx({ x: e.clientX, y: e.clientY });
            }}
          >
            {stageContents.length === 0 ? (
              <li className="text-lg text-slate-400">
                {selected ? '우클릭으로 업무내용을 수정하세요.' : '단계를 선택하세요.'}
              </li>
            ) : (
              stageContents.map((text) => (
                <li
                  key={text}
                  className="flex gap-2"
                  onContextMenu={(e) => {
                    if (!selected) return;
                    e.preventDefault();
                    e.stopPropagation();
                    setContentCtx({ x: e.clientX, y: e.clientY });
                  }}
                >
                  <span className="shrink-0 text-lg text-blue-400">•</span>
                  <p className="min-w-0 flex-1 truncate text-2xl font-black leading-snug text-slate-800">{text}</p>
                </li>
              ))
            )}
          </ul>
        </LeftSection>

        <LeftSection>
          <div className="mb-2 flex shrink-0 items-center justify-between gap-2 border-b border-slate-200 pb-2">
            <h3 className="text-sm font-black uppercase tracking-widest text-slate-500">피드백</h3>
            <button
              type="button"
              title="피드백 추가"
              disabled={!selectedId}
              onClick={() => setFeedbackModal({ mode: 'add' })}
              className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-emerald-500 text-lg font-bold leading-none text-white hover:bg-emerald-600 disabled:opacity-40"
            >
              +
            </button>
          </div>
          <div
            className="flex min-h-0 flex-1 flex-col overflow-hidden"
            onContextMenu={(e) => {
              if (!selectedId) return;
              e.preventDefault();
              setFeedbackCtx({ x: e.clientX, y: e.clientY });
            }}
          >
            {sortedFeedbacks.length === 0 ? (
              <p className="text-lg text-slate-400">우클릭 또는 + 버튼으로 피드백을 추가하세요.</p>
            ) : (
              <div className="mb-2 min-h-0 flex-1 space-y-2 overflow-hidden">
                {sortedFeedbacks.map((f) => (
                  <div
                    key={f.id}
                    className="rounded-lg bg-slate-50 px-3 py-2"
                    onContextMenu={(e) => {
                      e.preventDefault();
                      e.stopPropagation();
                      setFeedbackCtx({ x: e.clientX, y: e.clientY, detailId: f.id });
                    }}
                  >
                    <p className="truncate text-2xl font-black leading-snug text-slate-700">
                      {f.content}
                      <span className="font-bold text-slate-400"> — {f.author?.name ?? '—'}</span>
                    </p>
                  </div>
                ))}
              </div>
            )}
          </div>
        </LeftSection>
      </aside>

      {/* 우 3/4 */}
      <div className="flex h-full min-h-0 min-w-0 flex-col">
        <ResultPreview
          files={stageFiles}
          links={stageLinks}
          hasSelectedStage={!!selectedId}
        />

        <footer className="shrink-0 border-t border-slate-300 bg-white px-5 py-4" style={{ height: '132px' }}>
          <div className="mb-2 flex items-center justify-between">
            <span className="text-lg font-black text-slate-700">업무별 타임라인</span>
            <span className="truncate text-base font-bold text-emerald-600">{selected?.title ?? task.title}</span>
          </div>

          {timeline ? (
            <>
              <div className="mb-1.5 flex justify-between text-sm font-semibold text-slate-400">
                <span>{timeline.start}</span>
                <span>{timeline.end}</span>
              </div>
              <div className="relative h-10 bg-slate-100">
                <div
                  className="pointer-events-none absolute top-0 z-10 flex h-full flex-col items-center"
                  style={{ left: `${timeline.todayLeft}%` }}
                >
                  <span className="mb-0.5 bg-emerald-500 px-1.5 py-0.5 text-[10px] font-black text-white">
                    TODAY ({timeline.today})
                  </span>
                  <div className="h-full w-0.5 bg-emerald-500" />
                </div>
                {timeline.bars.map((bar) => {
                  const isSelected = bar.id === selectedId;
                  return (
                    <button
                      key={bar.id}
                      type="button"
                      onClick={() => setSelectedId(bar.id)}
                      className={`absolute top-1/2 h-5 -translate-y-1/2 overflow-hidden transition-all ${
                        isSelected ? 'z-20 ring-2 ring-emerald-400' : 'z-0 opacity-85 hover:opacity-100'
                      }`}
                      style={{ left: `${bar.left}%`, width: `${bar.width}%` }}
                      title={bar.title}
                    >
                      <div className="absolute inset-0 bg-slate-300" />
                      <div
                        className="absolute inset-y-0 left-0 bg-emerald-500"
                        style={{ width: `${bar.progress}%` }}
                      />
                      <span className="relative flex h-full items-center justify-center truncate px-1 text-[10px] font-bold text-white">
                        {bar.label}
                      </span>
                    </button>
                  );
                })}
              </div>
            </>
          ) : (
            <p className="text-sm text-slate-400">수행기간이 설정되면 타임라인이 표시됩니다.</p>
          )}
        </footer>
      </div>

      {stageModal && (
        <StageModal
          mode={stageModal.mode}
          milestone={stageModal.milestone}
          existingFiles={
            stageModal.milestone
              ? sortFilesByOrder(files.filter((f) => f.milestoneId === stageModal.milestone!.id))
              : []
          }
          saving={stageSaving}
          onClose={() => setStageModal(null)}
          onSave={handleStageSave}
        />
      )}

      {feedbackModal && (
        <FeedbackModal
          mode={feedbackModal.mode}
          detail={feedbackModal.detail}
          defaultAuthorName={user?.name ?? ''}
          saving={feedbackSaving}
          onClose={() => setFeedbackModal(null)}
          onSave={handleFeedbackSave}
        />
      )}

      {ctxMenu && (
        <ContextMenu
          x={ctxMenu.x}
          y={ctxMenu.y}
          onClose={() => setCtxMenu(null)}
          items={[
            {
              label: '단계 수정',
              icon: '✏️',
              onClick: () => {
                const ms = milestones.find((m) => m.id === ctxMenu.stageId);
                if (ms) setStageModal({ mode: 'edit', milestone: ms });
              },
            },
            {
              label: '단계 삭제',
              icon: '🗑',
              danger: true,
              onClick: () => {
                if (window.confirm('이 단계를 삭제하시겠습니까?')) {
                  deleteMs.mutate(ctxMenu.stageId);
                  if (selectedId === ctxMenu.stageId) setSelectedId(null);
                }
              },
            },
          ]}
        />
      )}

      {contentCtx && selected && (
        <ContextMenu
          x={contentCtx.x}
          y={contentCtx.y}
          onClose={() => setContentCtx(null)}
          items={[
            {
              label: '수정',
              icon: '✏️',
              onClick: () => setStageModal({ mode: 'edit', milestone: selected }),
            },
          ]}
        />
      )}

      {feedbackCtx && (
        <ContextMenu
          x={feedbackCtx.x}
          y={feedbackCtx.y}
          onClose={() => setFeedbackCtx(null)}
          items={
            feedbackCtx.detailId
              ? [
                  {
                    label: '피드백 수정',
                    icon: '✏️',
                    onClick: () => {
                      const d = details.find((item) => item.id === feedbackCtx.detailId);
                      if (d) setFeedbackModal({ mode: 'edit', detail: d });
                    },
                  },
                  {
                    label: '피드백 삭제',
                    icon: '🗑',
                    danger: true,
                    onClick: () => {
                      if (window.confirm('이 피드백을 삭제하시겠습니까?')) {
                        deleteFeedback.mutate(feedbackCtx.detailId!);
                      }
                    },
                  },
                ]
              : [
                  {
                    label: '피드백 추가',
                    icon: '➕',
                    onClick: () => setFeedbackModal({ mode: 'add' }),
                  },
                ]
          }
        />
      )}
    </div>
  );
}

export default function DetailPage() {
  const { taskId: routeTaskId } = useParams<{ taskId?: string }>();
  const [taskId, setTaskId] = useState<string | null>(
    () => routeTaskId ?? getPersistedTaskId(),
  );

  useEffect(() => {
    if (routeTaskId) setTaskId(routeTaskId);
  }, [routeTaskId]);

  useEffect(() => {
    const unsub = onDualMonitorEvent((evt) => {
      if (evt.type === 'TASK_SELECTED') setTaskId(evt.taskId);
      if (evt.type === 'TASK_DESELECTED') setTaskId(null);
    });
    return unsub;
  }, []);

  const { data: task, isLoading, isError, error } = useQuery({
    queryKey: ['task', taskId],
    queryFn: async () => {
      const { data } = await apiClient.get<TaskWithRelations>(`/tasks/${taskId}`);
      return data;
    },
    enabled: !!taskId,
    staleTime: 10_000,
    retry: 2,
  });

  return (
    <div className="flex h-screen flex-col overflow-hidden bg-[#eef2f5] text-slate-800" style={{ fontSize: '18px' }}>
      {task && <DetailHeader task={task} />}

      <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
        {isLoading ? (
          <div className="flex h-full items-center justify-center text-xl text-slate-400">불러오는 중...</div>
        ) : isError ? (
          <div className="flex h-full flex-col items-center justify-center gap-3 text-center">
            <p className="text-xl font-bold text-red-500">상세 정보를 불러오지 못했습니다.</p>
            <p className="text-base text-slate-500">{getApiErrorMessage(error, '서버 연결을 확인해 주세요.')}</p>
          </div>
        ) : !task ? (
          <WaitingScreen />
        ) : (
          <div className="h-full min-h-0 flex-1">
            <DetailView task={task} />
          </div>
        )}
      </div>
    </div>
  );
}
