import { useState, useEffect, useRef, useMemo } from 'react';
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
import {
  DndContext,
  closestCenter,
  PointerSensor,
  useSensor,
  useSensors,
  DragOverlay,
  type DragStartEvent,
  type DragEndEvent,
} from '@dnd-kit/core';
import { arrayMove } from '@dnd-kit/sortable';
import { apiClient } from '../lib/apiClient';
import { useTasks } from '../hooks/useTasks';
import { useBoardReferenceDate } from '../hooks/useBoardReferenceDate';
import { useTeamMembers } from '../hooks/useTeamMembers';
import { DashboardHeader } from '../components/dashboard/DashboardHeader';
import { TeamStatusPanel } from '../components/dashboard/TeamStatusPanel';
import { DepartmentColumn } from '../components/dashboard/DepartmentColumn';
import { HubColumn } from '../components/dashboard/HubColumn';
import { BoardConnectors } from '../components/dashboard/BoardConnectors';
import '../styles/dummy-board.css';
import { DonutGauge } from '../components/dashboard/DonutGauge';
import { TaskManager } from '../components/dashboard/TaskManager';
import { useSocket } from '../contexts/SocketContext';
import { sendTaskSelected, openDetailWindow, registerSyncProvider, isDetailWindowOpen, getWindowPlacementHint } from '../lib/dualMonitor';
import { resolveTaskFocusMilestoneId } from '../lib/milestoneWeekFocus';
import { startOfWeekMonday } from '../lib/boardCalendar';
import { TaskDetailShell } from '../pages/DetailPage';
import { isRoutineTask, isProjectTask, displayFlagsForTaskType } from '../lib/taskType';
import { hasVisibleIssue } from '../lib/taskIssues';
import {
  DEFAULT_STATUS_FILTERS,
  taskMatchesStatusFilters,
  toggleAllFilter,
  toggleCoreFilter,
  type CoreStatusFilter,
} from '../lib/statusFilters';
import {
  SECTIONS,
  LEGACY_COLUMN_KEYS,
  taskBelongsToSection,
  type SectionKey,
} from '../lib/sections';
import {
  BOARD_SLOTS,
  BOARD_SLOT_ORDER,
  getBoardSlot,
  slotSectionLabel,
  columnDisplayTitle,
  taskBelongsToBoardSlot,
} from '../lib/boardLayout';
import { invalidateTaskCaches } from '../lib/taskQueryCache';

function mergeCardOrders(primary: string | null | undefined, extras: (string | null | undefined)[]): string[] {
  const merged: string[] = [];
  const push = (raw: string | null | undefined) => {
    if (!raw) return;
    try {
      const ids = JSON.parse(raw) as string[];
      for (const id of ids) {
        if (!merged.includes(id)) merged.push(id);
      }
    } catch {
      /* ignore */
    }
  };
  push(primary);
  extras.forEach(push);
  return merged;
}

function sectionKeyForLabel(label: string): SectionKey | null {
  for (const s of SECTIONS) {
    if (label === s || taskBelongsToSection(label, s)) return s;
  }
  return null;
}

export default function DashboardPage() {
  const [activeFilters, setActiveFilters] = useState<string[]>([...DEFAULT_STATUS_FILTERS]);
  const [filtersBeforeIssue, setFiltersBeforeIssue] = useState<string[]>([...DEFAULT_STATUS_FILTERS]);
  const [issueFilterActive, setIssueFilterActive] = useState(false);
  const [showTaskManager, setShowTaskManager] = useState(false);
  const [teamPanelOpen, setTeamPanelOpen] = useState(false);
  const [showAllTeamTasks, setShowAllTeamTasks] = useState(false);
  const [activeTeamProjectId, setActiveTeamProjectId] = useState<string | null>(null);
  const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
  const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
  const [detailStageId, setDetailStageId] = useState<string | null>(null);
  const [detailPopupOpen, setDetailPopupOpen] = useState(false);
  const [placementBanner, setPlacementBanner] = useState<string | null>(() => getWindowPlacementHint());
  const [viewportWidth, setViewportWidth] = useState(() =>
    typeof window !== 'undefined' ? window.innerWidth : 1920,
  );
  const [columnOrders, setColumnOrders] = useState<Record<string, string[]>>({});
  const saveTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>({});

  const queryClient = useQueryClient();
  const socket = useSocket();
  const {
    referenceDate,
    referenceWeekMonday,
    quarter,
    weekLensActive,
    selectWeek,
    selectQuarter,
    returnToCurrentQuarter,
  } = useBoardReferenceDate();

  const issueVisibility = useMemo(
    () => (weekLensActive ? { weekMonday: referenceWeekMonday } : undefined),
    [weekLensActive, referenceWeekMonday],
  );

  const { data: tasks = [], isLoading } = useTasks({ quarter });
  const { data: teamMembers = [] } = useTeamMembers();

  const { data: colConfigs } = useQuery({
    queryKey: ['columns', 'all', ...SECTIONS],
    queryFn: async () => {
      const results = await Promise.all(
        SECTIONS.map(async (s) => {
          const main = await apiClient.get(`/columns/${encodeURIComponent(s)}`).then((r) => r.data);
          const legacy = await Promise.all(
            LEGACY_COLUMN_KEYS[s].map((key) =>
              apiClient.get(`/columns/${encodeURIComponent(key)}`).then((r) => r.data).catch(() => null),
            ),
          );
          const cardOrder = JSON.stringify(
            mergeCardOrders(main.cardOrder, legacy.map((l) => l?.cardOrder)),
          );
          return { key: s, ...main, cardOrder };
        }),
      );
      return results;
    },
    staleTime: 0,
  });

  useEffect(() => {
    const onResize = () => setViewportWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  useEffect(() => registerSyncProvider(() => selectedTaskId), [selectedTaskId]);

  useEffect(() => {
    const timer = setInterval(() => {
      setDetailPopupOpen(isDetailWindowOpen());
    }, 300);
    return () => clearInterval(timer);
  }, []);

  useEffect(() => {
    if (!colConfigs) return;
    setColumnOrders((prev) => {
      const next = { ...prev };
      BOARD_SLOTS.forEach((slot) => {
        const label = slotSectionLabel(slot);
        if (next[label]) return;
        if (!slot.sectionKey) return;
        const cfg = colConfigs.find((c) => c.key === slot.sectionKey);
        if (cfg?.cardOrder) {
          try {
            next[label] = JSON.parse(cfg.cardOrder);
          } catch {
            /* ignore */
          }
        }
      });
      return next;
    });
  }, [colConfigs]);

  useEffect(() => {
    BOARD_SLOTS.forEach((slot) => {
      const label = slotSectionLabel(slot);
      const ids = tasks.filter((t) => taskBelongsToBoardSlot(t, slot)).map((t) => t.id);
      setColumnOrders((prev) => {
        const existing = prev[label] ?? [];
        const merged = [
          ...existing.filter((id) => ids.includes(id)),
          ...ids.filter((id) => !existing.includes(id)),
        ];
        if (JSON.stringify(merged) === JSON.stringify(existing)) return prev;
        return { ...prev, [label]: merged };
      });
    });
  }, [tasks]);

  const patchColumn = useMutation({
    mutationFn: ({ section, data }: { section: string; data: Record<string, string> }) =>
      apiClient.patch(`/columns/${encodeURIComponent(section)}`, data),
  });

  const patchTask = useMutation({
    mutationFn: ({ id, data }: { id: string; data: Record<string, unknown> }) =>
      apiClient.patch(`/tasks/${id}`, data),
    onSuccess: (_data, { id }) => invalidateTaskCaches(queryClient, id),
  });

  useEffect(() => {
    if (!socket) return;
    const refreshList = () => queryClient.invalidateQueries({ queryKey: ['tasks'] });
    const refreshTask = () => queryClient.invalidateQueries({ queryKey: ['task'] });
    socket.on('tasks:refresh', refreshList);
    socket.on('task:updated', refreshTask);
    return () => {
      socket.off('tasks:refresh', refreshList);
      socket.off('task:updated', refreshTask);
    };
  }, [socket, queryClient]);

  const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }));

  const handleDragStart = (event: DragStartEvent) => {
    setActiveTaskId(String(event.active.id));
  };

  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event;
    setActiveTaskId(null);
    if (!over) return;

    const activeId = String(active.id);
    const overId = String(over.id);

    const draggedTask = tasks.find((t) => t.id === activeId);
    if (!draggedTask) return;

    const srcSlot = BOARD_SLOTS.find((s) => taskBelongsToBoardSlot(draggedTask, s));
    const srcLabel = srcSlot ? slotSectionLabel(srcSlot) : (draggedTask.section ?? '인사관리');

    if (overId.startsWith('drop::')) {
      const parts = overId.split('::');
      const areaType = parts[1];
      const targetSection = parts[2];

      const updateData: Record<string, unknown> = {};

      if (areaType === 'routine' && targetSection === 'hub') {
        if (!isRoutineTask(draggedTask.taskType)) {
          updateData.taskType = '기반업무';
          Object.assign(updateData, displayFlagsForTaskType('기반업무'));
        }
        if (Object.keys(updateData).length > 0) {
          patchTask.mutate({ id: activeId, data: updateData });
        }
        return;
      }

      if (areaType === 'project') {
        if (targetSection !== srcLabel) updateData.section = targetSection;
        if (isRoutineTask(draggedTask.taskType)) {
          updateData.taskType = '실행과제';
          Object.assign(updateData, displayFlagsForTaskType('실행과제'));
        }
        if (Object.keys(updateData).length > 0) {
          patchTask.mutate({ id: activeId, data: updateData });
        }
        return;
      }
    }

    const overTask = tasks.find((t) => t.id === overId);
    if (!overTask) return;

    const dstSlot = BOARD_SLOTS.find((s) => taskBelongsToBoardSlot(overTask, s));
    const dstLabel = dstSlot ? slotSectionLabel(dstSlot) : (overTask.section ?? '인사관리');
    const typeChanged = isRoutineTask(draggedTask.taskType) !== isRoutineTask(overTask.taskType);

    if (dstLabel !== srcLabel || typeChanged) {
      const updateData: Record<string, unknown> = {};
      if (dstLabel !== srcLabel) updateData.section = dstLabel;
      if (typeChanged) {
        const newType = isRoutineTask(overTask.taskType) ? '기반업무' : '실행과제';
        updateData.taskType = newType;
        Object.assign(updateData, displayFlagsForTaskType(newType));
      }
      patchTask.mutate({ id: activeId, data: updateData });
      if (dstLabel !== srcLabel) return;
    }

    const current = columnOrders[srcLabel]
      ?? tasks.filter((t) => t.section === srcLabel).map((t) => t.id);
    const oldIdx = current.indexOf(activeId);
    const newIdx = current.indexOf(overId);
    if (oldIdx === -1 || newIdx === -1 || oldIdx === newIdx) return;

    const newOrder = arrayMove(current, oldIdx, newIdx);
    setColumnOrders((prev) => ({ ...prev, [srcLabel]: newOrder }));

    const apiKey = sectionKeyForLabel(srcLabel);
    if (!apiKey) return;

    if (saveTimers.current[srcLabel]) clearTimeout(saveTimers.current[srcLabel]);
    saveTimers.current[srcLabel] = setTimeout(() => {
      patchColumn.mutate({ section: apiKey, data: { cardOrder: JSON.stringify(newOrder) } });
    }, 300);
  };

  const filtered = tasks.filter((t) => {
    if (issueFilterActive) return hasVisibleIssue(t, issueVisibility);
    return taskMatchesStatusFilters(t, activeFilters);
  });

  /** 4분면 부서 카드에 표시되는 실행과제만 (상단 현황판과 건수 일치) */
  const boardProjectTasks = useMemo(
    () =>
      filtered.filter(
        (t) =>
          isProjectTask(t.taskType) &&
          BOARD_SLOTS.some((slot) => taskBelongsToBoardSlot(t, slot)),
      ),
    [filtered],
  );

  const stats = useMemo(
    () => ({
      total: boardProjectTasks.length,
      inProgress: boardProjectTasks.filter((t) => t.status === 'IN_PROGRESS').length,
      review: boardProjectTasks.filter(
        (t) => t.status === 'REVIEW' || t.status === 'CANCELLED' || t.status === 'TODO',
      ).length,
      done: boardProjectTasks.filter((t) => t.status === 'DONE').length,
      issues: boardProjectTasks.filter((t) => hasVisibleIssue(t, issueVisibility)).length,
    }),
    [boardProjectTasks, issueVisibility],
  );

  const routineTasks = useMemo(
    () => filtered.filter((t) => isRoutineTask(t.taskType)),
    [filtered],
  );

  const handleToggleAll = () => {
    setIssueFilterActive(false);
    setActiveFilters((prev) => toggleAllFilter(prev));
  };

  const handleToggleStatus = (key: CoreStatusFilter) => {
    setIssueFilterActive(false);
    setActiveFilters((prev) => toggleCoreFilter(prev, key));
  };

  const handleToggleIssue = () => {
    if (issueFilterActive) {
      setIssueFilterActive(false);
      setActiveFilters([...filtersBeforeIssue]);
      return;
    }
    setFiltersBeforeIssue([...activeFilters]);
    setIssueFilterActive(true);
  };

  const sectionOptions = BOARD_SLOTS.map((slot) => ({
    value: slotSectionLabel(slot),
    label: columnDisplayTitle(
      slot,
      colConfigs?.find((c) => c.key === slot.sectionKey),
    ),
  }));

  const activeTask = tasks.find((t) => t.id === activeTaskId) ?? null;

  /** 초광역(단일 화면)에서만 인라인 우측 패널 — 일반은 팝업(듀얼 모니터) 전용 */
  const ultraWideLayout = viewportWidth >= 3800;
  const showDockedDetail = ultraWideLayout && !!selectedTaskId && !detailPopupOpen;
  const detailDocked = showDockedDetail;

  const handleSelectTask = (taskId: string, stageId?: string | null) => {
    setSelectedTaskId(taskId);
    setDetailStageId(stageId ?? null);
    if (teamPanelOpen) setActiveTeamProjectId(taskId);
    void sendTaskSelected(taskId, stageId ?? null, () => setDetailPopupOpen(false)).then((popupOpened) => {
      if (popupOpened) {
        setDetailPopupOpen(true);
      } else if (!ultraWideLayout) {
        window.alert(
          '상세 창을 열 수 없습니다.\n\n' +
            '• 팝업 허용 (주소창 아이콘)\n' +
            '• 「모든 디스플레이에 대한 정보 보기」 권한 허용\n' +
            '  (IP 주소 접속 시 localhost와 별도로 한 번 더 필요)',
        );
      }
    });
  };

  const handleOpenDetailWindow = () => {
    void openDetailWindow(() => setDetailPopupOpen(false)).then((win) => {
      setDetailPopupOpen(!!win);
    });
  };

  const resolveProjectStageId = (task: (typeof filtered)[number]) => {
    const weekMonday = weekLensActive ? referenceWeekMonday : startOfWeekMonday(new Date());
    return resolveTaskFocusMilestoneId(task, {
      weekMonday,
      weekLensActive,
      quarter,
    });
  };

  const renderDeptSlot = (slotId: typeof BOARD_SLOT_ORDER[number]) => {
    const slot = getBoardSlot(slotId);
    const label = slotSectionLabel(slot);
    return (
      <DepartmentColumn
        key={slot.id}
        slot={slot}
        tasks={filtered.filter((t) => taskBelongsToBoardSlot(t, slot))}
        orderedIds={columnOrders[label] ?? []}
        quarter={quarter}
        referenceWeekMonday={weekLensActive ? referenceWeekMonday : undefined}
        onSelectTask={(t) => handleSelectTask(t.id, resolveProjectStageId(t))}
        sectionOptions={sectionOptions}
        teamMembers={teamMembers}
      />
    );
  };

  if (isLoading) {
    return (
      <div className="flex h-screen items-center justify-center bg-white">
        <div className="text-lg text-gray-400">데이터를 불러오는 중...</div>
      </div>
    );
  }

  return (
    <div
      className={`app-shell dummy-board-page dummy-board-page--2slots ${detailDocked ? 'detail-docked' : ''} ${detailPopupOpen ? 'dual-mode-parent' : ''}`}
    >
      <div className="app-main relative flex h-screen min-w-0 flex-col overflow-hidden">
        {placementBanner && (
          <div className="flex shrink-0 items-start justify-between gap-3 border-b border-amber-200 bg-amber-50 px-4 py-2.5 text-sm text-amber-950">
            <p className="whitespace-pre-line leading-relaxed">{placementBanner}</p>
            <button
              type="button"
              className="shrink-0 rounded-lg px-2 py-1 text-amber-800 hover:bg-amber-100"
              onClick={() => setPlacementBanner(null)}
              aria-label="닫기"
            >
              ✕
            </button>
          </div>
        )}
        <DashboardHeader
        quarter={quarter}
        referenceWeekMonday={referenceWeekMonday}
        weekLensActive={weekLensActive}
        onSelectWeek={selectWeek}
        onSelectQuarter={selectQuarter}
        onReturnToCurrentQuarter={returnToCurrentQuarter}
        stats={stats}
          activeFilters={activeFilters}
          issueFilterActive={issueFilterActive}
          onToggleAll={handleToggleAll}
          onToggleStatus={handleToggleStatus}
          onToggleIssue={handleToggleIssue}
          onOpenDetailWindow={handleOpenDetailWindow}
          onOpenTaskManager={() => setShowTaskManager(true)}
          teamPanelOpen={teamPanelOpen}
          onToggleTeamPanel={() => {
            setTeamPanelOpen((open) => {
              if (open) {
                setActiveTeamProjectId(null);
                setShowAllTeamTasks(false);
              } else if (selectedTaskId) {
                setActiveTeamProjectId(selectedTaskId);
              }
              return !open;
            });
          }}
        />

        {teamPanelOpen && (
          <TeamStatusPanel
            members={teamMembers}
            tasks={tasks}
            showAllTasks={showAllTeamTasks}
            activeProjectId={activeTeamProjectId}
            onToggleShowAll={() => setShowAllTeamTasks((v) => !v)}
            onProjectClick={setActiveTeamProjectId}
            onClose={() => {
              setTeamPanelOpen(false);
              setActiveTeamProjectId(null);
              setShowAllTeamTasks(false);
            }}
          />
        )}

        <main className={`board-main dummy-board-main ${teamPanelOpen ? 'hidden' : ''}`}>
          <div className="page-content">
            <DndContext
              sensors={sensors}
              collisionDetection={closestCenter}
              onDragStart={handleDragStart}
              onDragEnd={handleDragEnd}
            >
              <div className="board-layout">
                {renderDeptSlot('hrm')}
                {renderDeptSlot('hrd')}
                <HubColumn
                  routineTasks={routineTasks}
                  quarter={quarter}
                  referenceDate={referenceDate}
                  onSelectRoutine={(t) => handleSelectTask(t.id)}
                  onSelectRoutineMilestone={(taskId, milestoneId) => handleSelectTask(taskId, milestoneId)}
                />
                {renderDeptSlot('ex')}
                {renderDeptSlot('ga')}
                <BoardConnectors enabled={!teamPanelOpen} style="reference" />
              </div>

              <DragOverlay dropAnimation={{ duration: 180, easing: 'ease' }}>
                {activeTask && isProjectTask(activeTask.taskType) ? (
                  <article className="project-sub-card board-project-card--overlay">
                    <div className="project-sub-body">
                      <div className="project-fields">
                        <div className="project-sub-title">{activeTask.title}</div>
                      </div>
                      {activeTask.showProgress !== false && (
                        <div className="progress-col">
                          <DonutGauge task={activeTask} />
                        </div>
                      )}
                    </div>
                  </article>
                ) : activeTask ? (
                  <div className="board-routine-item board-project-card--overlay">
                    <span className="board-project-title">{activeTask.title}</span>
                  </div>
                ) : null}
              </DragOverlay>
            </DndContext>
          </div>
        </main>

        {showTaskManager && (
          <TaskManager
            tasks={tasks}
            sectionOptions={sectionOptions}
            quarter={quarter}
            teamMembers={teamMembers}
            onClose={() => setShowTaskManager(false)}
          />
        )}
      </div>

      {!detailPopupOpen && ultraWideLayout && (
        <aside className={`app-right ${showDockedDetail ? 'detail-open' : ''}`}>
          <TaskDetailShell taskId={selectedTaskId} initialStageId={detailStageId} />
        </aside>
      )}
    </div>
  );
}
