/**
 * Home - 메인 페이지 (Zone-Centric 슬라이드 빌더)
 */

import { useState, useCallback, useMemo, useEffect } from "react";
import { toast } from "sonner";
import type { DesignAgentState, LayoutPresetId, Zone } from "../types/designAgent";
import {
  createInitialUserSelection,
  selectZone,
  selectRegion,
  applyLayout,
  applyFrame,
  getSelectedZone,
  getSelectedRegion,
  moveSectionToZone,
  saveZoneSizes,
} from "../utils/slidePlanUtils";
import {
  parseMdxFile,
  runPipeline,
  loadRun,
  computeZonePositions,
  type RunMeta,
  type PipelineOverrides,
} from "../services/designAgentApi";

import LeftMdxPanel from "../components/LeftMdxPanel";
import SlideCanvas from "../components/SlideCanvas";
import LayoutPanel from "../components/LayoutPanel";
import FramePanel from "../components/FramePanel";
import {
  Sparkles, Download, Link2, Loader2,
  CheckCircle2, HelpCircle,
} from "lucide-react";
import { Button } from "@/components/ui/button";


const INITIAL_STATE: DesignAgentState = {
  uploadedFile: null,
  normalizedContent: null,
  slidePlan: null,
  userSelection: createInitialUserSelection(),
  isLoading: false,
  error: null,
};

type RightPanelTab = "layout" | "frame";

export default function Home() {
  const [state, setState] = useState<DesignAgentState>(INITIAL_STATE);
  const [rightTab, setRightTab] = useState<RightPanelTab>("frame");
  // Phase Z run 산출물 메타 (loadRun 호출 시 set — Step 5 에서 연결).
  const [runMeta, setRunMeta] = useState<RunMeta | null>(null);
  // Phase 1 : 사용자가 frame / section / layout / zone resize 등 override 를
  // 변경했을 때 true. 좌측 하단 버튼이 "선택대로 재생성하기" 로 전환.
  // handleGenerate (재생성) 시 false 로 reset.
  const [hasPendingChanges, setHasPendingChanges] = useState(false);

  // Phase 2 : 사용자가 LayoutPanel 의 다른 layout 카드 → "적용하기" 누르면
  // 빈 layout 모드로 전환 (final.html iframe 숨기고 빈 zone 만 표시 → 사용자가
  // section drag drop + frame 선택). null 이면 평소 모드 (final.html 표시).
  const [pendingLayout, setPendingLayout] = useState<LayoutPresetId | null>(null);

  // pendingLayout 활성 시 effective slidePlan = pendingZones 가 swap 된 plan.
  // 그 외 = default state.slidePlan. 모든 zone / region lookup (handleFrameSelect /
  // getSelectedZone / SlideCanvas) 이 일관되게 이 effectiveSlidePlan 사용.

  // pendingLayout 활성 시 빈 layout 의 zones 계산 (정규화 좌표).
  // section_ids 는 userSelection.overrides.zone_sections 에서만 — default 무시
  // (사용자가 직접 채워야 함). frame_candidates 는 normal mode 의 union 사용.
  const pendingZones = useMemo<Zone[] | null>(() => {
    if (!pendingLayout) return null;
    const positions = computeZonePositions(pendingLayout);
    return positions.map((p, idx) => {
      const sectionIds =
        state.userSelection.overrides.zone_sections?.[p.name] ?? [];
      // 그 zone 에 할당된 section 의 frame_candidates 를 자동 결정 결과 (slidePlan)
      // 에서 lookup. section_id 매칭되는 zone 의 internal_regions[0].frame_candidates 사용.
      // 없으면 첫 번째 zone 의 후보 fallback (시각용 — 정확한 매칭은 Step D 의
      // backend forwarding 후에 의미 있음).
      const sourceZone =
        sectionIds.length > 0
          ? state.slidePlan?.zones.find((z) =>
              sectionIds.some((sid) => z.section_ids.includes(sid))
            )
          : undefined;
      const frameCandidates =
        sourceZone?.internal_regions[0]?.frame_candidates ??
        state.slidePlan?.zones[0]?.internal_regions[0]?.frame_candidates ??
        [];
      return {
        id: `pending-zone-${idx}`,
        zone_id: p.name,
        section_ids: sectionIds,
        position: p.geometry,
        internal_regions: [
          {
            id: `pending-region-${idx}`,
            region_id: "region-single",
            role: "primary" as const,
            content_type: "text_block" as const,
            ratio_estimate: 1,
            content_unit_ids: [],
            frame_match_strategy: {
              kind: "frame_match" as const,
              frame_id: null,
              display_strategy: "inline_full" as const,
            },
            frame_candidates: frameCandidates,
          },
        ],
        region_layout_type: "region-single",
      } as Zone;
    });
  }, [pendingLayout, state.userSelection.overrides.zone_sections, state.slidePlan]);

  // Layout 카드의 "적용하기" 클릭 → pendingLayout 모드 진입.
  // 기존 zone_sections 를 새 layout 의 positions 순서로 자동 carry over —
  // horizontal-2 (top, bottom) → vertical-2 (left, right) 같은 case 에서 사용자가
  // 매번 drag drop 다시 안 해도 되게. 새 layout zone count 가 적으면 마지막 zone 에 합침.
  const handleApplyPendingLayout = useCallback((layoutId: LayoutPresetId) => {
    setState((p) => {
      const newPositions = computeZonePositions(layoutId).map((pos) => pos.name);
      const oldZones = p.slidePlan?.zones ?? [];
      const carriedZoneSections: Record<string, string[]> = {};
      oldZones.forEach((zone, idx) => {
        const targetPos =
          idx < newPositions.length
            ? newPositions[idx]
            : newPositions[newPositions.length - 1]; // 적은 zone count: 마지막에 합침
        if (!targetPos) return;
        if (!carriedZoneSections[targetPos]) {
          carriedZoneSections[targetPos] = [];
        }
        carriedZoneSections[targetPos].push(...zone.section_ids);
      });
      return {
        ...p,
        userSelection: {
          ...p.userSelection,
          overrides: {
            ...p.userSelection.overrides,
            layout_preset: layoutId,
            zone_sections: carriedZoneSections,
          },
          selectedZoneId: null,
          selectedRegionId: null,
        },
      };
    });
    setPendingLayout(layoutId);
    setHasPendingChanges(true);
    setRightTab("frame");
  }, []);

  // pending 모드 취소 → 평소 (final.html iframe) 모드 복귀.
  const handleCancelPendingLayout = useCallback(() => {
    setPendingLayout(null);
    setState((p) => ({
      ...p,
      userSelection: createInitialUserSelection(p.slidePlan),
    }));
    setHasPendingChanges(false);
  }, []);

  // ── 파일 업로드 → MDX 분석만 (파이프라인 X) ──
  //   업로드 시점에는 좌측 패널의 섹션 트리만 채움. 실제 Phase Z 파이프라인은
  //   하단 "슬라이드 플랜 생성하기" 버튼 (handleGenerate) 에서 실행.
  const handleFileUpload = useCallback(async (file: File) => {
    setState((p) => ({
      ...p,
      uploadedFile: file,
      isLoading: true,
      normalizedContent: null,
      slidePlan: null,
    }));
    setRunMeta(null);

    try {
      const content = await parseMdxFile(file);
      setState((p) => ({ ...p, normalizedContent: content, isLoading: false }));
      toast.success(`"${file.name}" 분석 완료 — 하단 버튼으로 슬라이드 생성하세요.`);
    } catch (err) {
      console.error(err);
      toast.error(
        `MDX 분석 오류: ${err instanceof Error ? err.message : String(err)}`
      );
      setState((p) => ({ ...p, isLoading: false }));
    }
  }, []);

  // 2026-05-14 — 좌측 패널의 03/04/05 fix list 클릭 또는 URL `?mdx=04` 변경 시
  // 호출되는 단일 callback. handleFileUpload 가 자동 분석 trigger.
  const [selectedSample, setSelectedSample] = useState<"03" | "04" | "05" | null>(null);

  // 2026-05-14 — mdx 별 slide-level CSS override (catalog/template 무변, frontend layer only).
  // SlideCanvas 의 iframe onLoad 에서 동적 inject. 사용자 룰 : "보고용 슬라이드 결과물 단위"
  // 변경. mdx04 의 default (rank 1 = process_product_two_way) 일 때만 적용 — 사용자 frame
  // override 후 (rank 2 = bim_dx_comparison_table 등) 다른 frame 시 무적용.
  const MDX04_DEFAULT_OVERRIDE_CSS = `
    .slide-body {
      grid-template-rows: 0.38fr 0.60fr !important;
      gap: 1.5% !important;
    }
    .f29b__cell .text-line + .text-line { margin-top: 1px !important; }
    .f29b__cell:nth-child(n+3) {
      padding-top: 3px !important;
      margin-top: 2px !important;
    }
  `.trim();

  const handleSelectSample = useCallback(async (which: "03" | "04" | "05") => {
    try {
      const res = await fetch(`/api/sample-mdx?mdx=${encodeURIComponent(which)}`);
      if (!res.ok) return;
      const text = await res.text();
      const filenameHeader = res.headers.get("X-Mdx-Filename");
      const filename = filenameHeader
        ? decodeURIComponent(filenameHeader)
        : `${which}_demo.mdx`;
      const file = new File([text], filename, { type: "text/markdown" });
      setSelectedSample(which);
      handleFileUpload(file);
      // 슬라이드 생성은 사용자가 "슬라이드 플랜 생성하기" 클릭 시 handleGenerate 에서.
    } catch {
      // backend 미작동
    }
    // handleFileUpload 는 useCallback 으로 안정 — eslint dep 우회
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 페이지 첫 로드 시 데모용 mdx 자동 로드 — 상대방에게 mdx 파일 공유 안 해도 되게.
  // URL query `?mdx=04` / `?mdx=05` 로 다른 sample 선택 가능. default = 03.
  // 사용자가 다른 파일을 직접 업로드하면 그것이 override 됨.
  useEffect(() => {
    const which = (new URLSearchParams(window.location.search).get("mdx") as "03" | "04" | "05" | null) || "03";
    if (!["03", "04", "05"].includes(which)) return;
    handleSelectSample(which);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // ── 슬라이드 플랜 생성 → Phase Z 파이프라인 실행 → 산출물 로드 ──
  const handleGenerate = useCallback(async () => {
    if (!state.uploadedFile) {
      toast.error("MDX 파일을 먼저 업로드하세요.");
      return;
    }

    // mdx04 도 mdx03 와 동일하게 정상 backend pipeline path (demo mode 제거).

    // Step D : userSelection.overrides → backend 가 받는 schema 로 변환.
    // - layout : userSelection.overrides.layout_preset (default 와 다를 때만)
    // - frames : zone.section_ids → unit_id ("+".join). region.id 별 zone_frames lookup.
    //   pendingZones 의 region 도 동일 — 그 region 의 zone 의 sections 가 unit_id 결정.
    const overrides: PipelineOverrides = {};
    const sourcePlan = effectiveSlidePlan;
    if (sourcePlan && state.slidePlan) {
      const defaultLayout = state.slidePlan.layout_preset;
      const overrideLayout = state.userSelection.overrides.layout_preset;
      if (overrideLayout && overrideLayout !== defaultLayout) {
        overrides.layout = overrideLayout;
      }
      const frames: Record<string, string> = {};
      const skippedNoCatalog: string[] = [];
      sourcePlan.zones.forEach((zone) => {
        const region = zone.internal_regions[0];
        if (!region) return;
        const overrideFrameId =
          state.userSelection.overrides.zone_frames?.[region.id];
        const defaultFrameId = region.frame_match_strategy.frame_id;
        if (
          overrideFrameId &&
          overrideFrameId !== defaultFrameId &&
          zone.section_ids.length > 0
        ) {
          // catalog 미등록 frame 은 backend 가 어차피 skip — frontend 에서 미리 거름
          // (불필요한 round trip + 사용자 헷갈림 방지). 후보 list 에서 catalog flag lookup.
          const candidateMatch = region.frame_candidates?.find(
            (c) => c.id === overrideFrameId
          );
          if (candidateMatch?.catalogRegistered === false) {
            skippedNoCatalog.push(`${zone.zone_id}=${overrideFrameId}`);
            return;
          }
          const unitId = zone.section_ids.join("+");
          frames[unitId] = overrideFrameId;
        }
      });
      if (Object.keys(frames).length > 0) overrides.frames = frames;
      if (skippedNoCatalog.length > 0) {
        toast.error(
          `catalog 미등록 frame ${skippedNoCatalog.length} 개는 backend 가 적용 못 함 — render path 미연결: ${skippedNoCatalog.join(", ")}`
        );
      }

      // zone-geometry override — backend 의 build_layout_css 에 전달 (horizontal-2 /
      // vertical-2 만 적용). zone_id (top/bottom/...) → slide-body 내부 0~1 비율.
      const zoneGeometries = state.userSelection.overrides.zone_geometries;
      if (zoneGeometries && Object.keys(zoneGeometries).length > 0) {
        overrides.zoneGeometries = zoneGeometries;
      }

      // IMP-08 B-3 : zoneSections forward only when the user diverged from
      // the auto plan. Codex Stage 3 R3 B3 fix : `createInitialUserSelection`
      // seeds `zone_sections` with the default placement, so a literal copy
      // would pollute backend assignment-source provenance even on a fresh
      // re-render. Diff against `sourcePlan.zones[].section_ids` per zone and
      // only emit zones whose section list differs.
      const userZoneSections = state.userSelection.overrides.zone_sections;
      if (userZoneSections) {
        const defaultByZone = new Map<string, string[]>();
        sourcePlan.zones.forEach((z) => {
          defaultByZone.set(z.zone_id, z.section_ids);
        });
        const zoneSectionsDiff: Record<string, string[]> = {};
        for (const [zoneId, sids] of Object.entries(userZoneSections)) {
          if (!Array.isArray(sids)) continue;
          const cleaned = sids.filter((s) => typeof s === "string" && s.trim());
          const defaults = defaultByZone.get(zoneId) ?? [];
          const sameAsDefault =
            cleaned.length === defaults.length &&
            cleaned.every((sid, i) => sid === defaults[i]);
          if (!sameAsDefault) {
            zoneSectionsDiff[zoneId] = cleaned;
          }
        }
        if (Object.keys(zoneSectionsDiff).length > 0) {
          overrides.zoneSections = zoneSectionsDiff;
        }
      }
    }

    setState((p) => ({ ...p, isLoading: true }));
    setHasPendingChanges(false);  // 재생성 트리거 시 override pending flag reset
    setPendingLayout(null);       // pending layout 모드 종료
    const overrideSummary =
      Object.keys(overrides).length > 0
        ? `(overrides: ${[
            overrides.layout && `layout=${overrides.layout}`,
            overrides.frames && `frames=${Object.keys(overrides.frames).length}`,
            overrides.zoneSections &&
              `zoneSections=${Object.keys(overrides.zoneSections).length}`,
          ]
            .filter(Boolean)
            .join(", ")})`
        : "";
    toast.info(`Phase Z 파이프라인 실행 중... ${overrideSummary}`);

    try {
      const result = await runPipeline(state.uploadedFile, overrides);

      if (!result.success || !result.final_html_exists) {
        const detail =
          result.stderr?.trim().split("\n").slice(-3).join(" | ") ||
          result.error ||
          `exit_code=${result.exit_code}`;
        toast.error(`파이프라인 실패: ${detail}`);
        setState((p) => ({ ...p, isLoading: false }));
        return;
      }

      const { normalizedContent, slidePlan, runMeta } = await loadRun(result.run_id);
      setState((p) => ({
        ...p,
        normalizedContent,
        slidePlan,
        userSelection: createInitialUserSelection(slidePlan),
        isLoading: false,
      }));
      setRunMeta(runMeta);
      toast.success(`run "${result.run_id}" 완료 — ${runMeta.status}`);
    } catch (err) {
      console.error(err);
      toast.error(
        `파이프라인 실행 오류: ${err instanceof Error ? err.message : String(err)}`
      );
      setState((p) => ({ ...p, isLoading: false }));
    }
  }, [state.uploadedFile, state.slidePlan, state.userSelection, pendingZones, pendingLayout]);

  // ── 섹션 드래그 앤 드롭 (Zone으로 재배치) ──
  const handleSectionDrop = useCallback((sectionId: string, zoneId: string) => {
    setState((p) => {
      const newSelection = moveSectionToZone(p.userSelection, sectionId, zoneId);
      return {
        ...p,
        userSelection: selectZone(newSelection, zoneId) // 이동된 존 자동 선택
      };
    });
    setRightTab("frame");
    setHasPendingChanges(true);
  }, []);

  // ── Zone 클릭 ──
  const handleZoneClick = useCallback((zoneId: string) => {
    setState((p) => ({ 
      ...p, 
      userSelection: selectZone(p.userSelection, zoneId) 
    }));
    setRightTab("frame");
  }, []);

  // ── Region 클릭 ──
  const handleRegionClick = useCallback((regionId: string) => {
    setState((p) => ({ 
      ...p, 
      userSelection: selectRegion(p.userSelection, regionId) 
    }));
    setRightTab("frame");
  }, []);

  // ── Layout 선택 ──
  const handleLayoutSelect = useCallback((layoutId: string) => {
    setState((p) => ({
      ...p,
      userSelection: applyLayout(p.userSelection, layoutId as LayoutPresetId)
    }));
    setHasPendingChanges(true);
  }, []);

  const handleLayoutResize = useCallback((groupId: string, sizes: number[]) => {
    setState((p) => ({
      ...p,
      userSelection: saveZoneSizes(p.userSelection, groupId, sizes)
    }));
    setHasPendingChanges(true);
  }, []);

  const handleZoneResize = useCallback((geometries: Record<string, { x: number; y: number; w: number; h: number }>) => {
    setState((p) => ({
      ...p,
      userSelection: {
        ...p.userSelection,
        overrides: {
          ...p.userSelection.overrides,
          zone_geometries: {
            ...p.userSelection.overrides.zone_geometries,
            ...geometries
          }
        }
      }
    }));
    setHasPendingChanges(true);
  }, []);

  // 편집 모드 텍스트 변경 시 hasPendingChanges 활성. useCallback 으로 reference 안정화 —
  // SlideCanvas 의 useEffect 가 매번 rerun 안 하도록 (resize drag 매 mousemove 마다
  // re-render 시 useEffect retrigger → iframe contentEditable 재설정 = 매우 느림).
  const handleContentEdit = useCallback(() => {
    setHasPendingChanges(true);
  }, []);

  // pending mode 일 때 effectiveSlidePlan = pendingZones 가 swap 된 plan.
  // 그 외 = state.slidePlan. 모든 zone / region lookup 이 일관되게 이걸 사용 →
  // pending mode 의 region.id ("pending-region-N") 가 zone_frames key 로 들어가
  // SlideCanvas overlay 의 region.id 와 매칭됨.
  const effectiveSlidePlan = useMemo(() => {
    if (pendingZones && state.slidePlan) {
      return { ...state.slidePlan, zones: pendingZones, layout_preset: pendingLayout! };
    }
    return state.slidePlan;
  }, [pendingZones, state.slidePlan, pendingLayout]);

  // 2026-05-14 — slide-level CSS override 계산. mdx04 default (rank 1 = process_product_two_way)
  // 일 때만 적용 (catalog 무변, slide 결과물에만 inject). 사용자 frame override 후 다른
  // frame 시 무적용 (rank 2 의 frame visual 유지).
  const slideOverrideCss = useMemo<string | undefined>(() => {
    if (selectedSample !== "04") return undefined;
    const zone04_2 = state.slidePlan?.zones.find((z) => z.zone_id === "bottom");
    const frameId = zone04_2?.internal_regions[0]?.frame_match_strategy.frame_id;
    if (frameId !== "process_product_two_way") return undefined;
    return MDX04_DEFAULT_OVERRIDE_CSS;
  }, [selectedSample, state.slidePlan]);

  // ── Frame 선택 ──
  const handleFrameSelect = useCallback((frameId: string) => {
    const zone = getSelectedZone(effectiveSlidePlan, state.userSelection);
    const region = getSelectedRegion(zone, state.userSelection) || zone?.internal_regions?.[0];

    if (!region) {
      toast.error("디자인을 적용할 구역을 먼저 선택해주세요.");
      return;
    }

    setState((p) => ({
      ...p,
      userSelection: applyFrame(p.userSelection, region.id, frameId)
    }));
    setHasPendingChanges(true);
  }, [effectiveSlidePlan, state.userSelection]);

  const selectedZone = getSelectedZone(effectiveSlidePlan, state.userSelection);
  const selectedRegion = getSelectedRegion(selectedZone, state.userSelection);

  return (
    <div className="h-screen flex flex-col bg-white overflow-hidden font-sans">
      {/* Header */}
      <header className="flex-shrink-0 h-12 flex items-center px-4 border-b border-slate-200 bg-white shadow-sm z-30">
        <div className="flex items-center gap-2 w-56 flex-shrink-0">
          <div className="w-8 h-8 bg-slate-900 rounded-lg flex items-center justify-center">
            <Sparkles className="w-4 h-4 text-blue-400 fill-blue-400" />
          </div>
          <span className="text-sm font-black text-slate-800 tracking-tighter uppercase">Design Agent</span>
        </div>

        <div className="flex-1 flex items-center justify-center gap-2">
          <span className="text-xs font-bold text-slate-400 uppercase tracking-widest">Active Slide /</span>
          <span className="text-sm font-bold text-slate-700">{state.slidePlan?.title ?? "새 슬라이드"}</span>
          {runMeta && (
            <>
              <span className="text-slate-300">·</span>
              <span className="text-[10px] font-mono text-slate-500 px-1.5 py-0.5 bg-slate-100 rounded">
                run: {runMeta.run_id}
              </span>
              <span
                className={`text-[10px] font-bold px-1.5 py-0.5 rounded uppercase tracking-wider ${
                  runMeta.status === "PASS"
                    ? "bg-emerald-100 text-emerald-700"
                    : runMeta.status === "RENDERED_WITH_VISUAL_REGRESSION"
                    ? "bg-amber-100 text-amber-700"
                    : runMeta.status === "PARTIAL_COVERAGE"
                    ? "bg-orange-100 text-orange-700"
                    : "bg-red-100 text-red-700"
                }`}
                title={`visual_check_passed: ${runMeta.visual_check_passed} / full_mdx_coverage: ${runMeta.full_mdx_coverage}`}
              >
                {runMeta.status}
              </span>
              {runMeta.filtered_section_ids.length > 0 && (
                <details className="relative">
                  <summary className="text-[10px] font-bold px-1.5 py-0.5 bg-amber-100 text-amber-700 rounded uppercase tracking-wider cursor-pointer list-none">
                    Filtered: {runMeta.filtered_section_ids.length}
                  </summary>
                  <div className="absolute top-full mt-1 left-0 z-50 bg-white border border-slate-200 rounded shadow-lg p-3 w-96 max-h-96 overflow-y-auto">
                    {runMeta.filtered_section_reasons.map((r, i) => (
                      <div key={i} className="mb-2 pb-2 border-b border-slate-100 last:border-0 last:mb-0 last:pb-0 text-[11px]">
                        <div className="font-mono text-slate-700">{r.section_ids.join(", ")}</div>
                        <div className="text-slate-500">selection_state: <span className="font-mono">{r.selection_state}</span></div>
                        {r.merge_type && <div className="text-slate-500">merge_type: <span className="font-mono">{r.merge_type}</span></div>}
                        {r.template_id && <div className="text-slate-500">template_id: <span className="font-mono">{r.template_id}</span></div>}
                        {r.v4_label && <div className="text-slate-500">v4_label: <span className="font-mono">{r.v4_label}</span></div>}
                        {r.phase_z_status && <div className="text-slate-500">phase_z_status: <span className="font-mono">{r.phase_z_status}</span></div>}
                        {r.score !== null && <div className="text-slate-500">score: <span className="font-mono">{r.score}</span></div>}
                        {r.source && <div className="text-slate-500">source: <span className="font-mono">{r.source}</span></div>}
                        {r.position && <div className="text-slate-500">position: <span className="font-mono">{r.position}</span></div>}
                        <ul className="mt-1 list-disc list-inside text-slate-600">
                          {r.filter_reasons.map((reason, j) => <li key={j} className="font-mono">{reason}</li>)}
                        </ul>
                      </div>
                    ))}
                  </div>
                </details>
              )}
            </>
          )}
        </div>

        <div className="flex items-center gap-3 w-56 flex-shrink-0 justify-end">
          {state.isLoading ? (
            <div className="flex items-center gap-1.5 text-blue-500">
              <Loader2 className="w-3.5 h-3.5 animate-spin" />
              <span className="text-[11px] font-bold uppercase">Processing...</span>
            </div>
          ) : state.slidePlan ? (
            <div className="flex items-center gap-1 text-slate-400">
              <CheckCircle2 className="w-3.5 h-3.5 text-emerald-500" />
              <span className="text-[11px] font-bold uppercase tracking-tighter">Synced</span>
            </div>
          ) : null}
          <div className="h-4 w-px bg-slate-200" />
          <button className="text-slate-300 hover:text-slate-500 transition-colors"><HelpCircle className="w-4 h-4" /></button>
        </div>
      </header>

      {/* Main Content Area */}
      <div className="flex-1 flex overflow-hidden">
        {/* Left Panel: MDX Explorer (Drag Source) */}
        <aside className="w-64 flex-shrink-0 border-r border-slate-200 flex flex-col overflow-hidden shadow-sm z-20">
          <LeftMdxPanel
            normalizedContent={state.normalizedContent}
            uploadedFile={state.uploadedFile}
            isLoading={state.isLoading}
            selectedSectionId={state.userSelection.selectedSectionId}
            hasSlidePlan={!!state.slidePlan}
            hasPendingChanges={hasPendingChanges}
            onFileUpload={handleFileUpload}
            onGenerate={handleGenerate}
            onSectionClick={(sectionId) => {
              if (!state.slidePlan) return;
              const zone = state.slidePlan.zones.find((z) => z.section_ids.includes(sectionId));
              if (zone) handleZoneClick(zone.id);
            }}
            onSelectSample={handleSelectSample}
            selectedSample={selectedSample}
          />
        </aside>

        {/* Center: Slide Canvas (Drop Target & Resizable) */}
        <main className="flex-1 flex flex-col overflow-hidden bg-slate-100">
          <SlideCanvas
            slidePlan={effectiveSlidePlan}
            normalizedContent={state.normalizedContent}
            userSelection={state.userSelection}
            finalHtmlUrl={runMeta?.final_html_url}
            slideOverrideCss={slideOverrideCss}
            isPipelineRunning={state.isLoading}
            isPendingLayout={!!pendingLayout}
            pendingLayoutId={pendingLayout}
            onCancelPendingLayout={handleCancelPendingLayout}
            onContentEdit={handleContentEdit}
            onZoneClick={(zoneId) => {
              handleZoneClick(zoneId);
              setRightTab("frame");
            }}
            onSlideClick={() => setRightTab("layout")}
            onRegionClick={handleRegionClick}
            onFrameSelect={handleFrameSelect}
            onSectionDrop={handleSectionDrop}
            onLayoutResize={handleLayoutResize}
            onZoneResize={handleZoneResize}
          />
        </main>

        {/* Right Panel: Frame / Layout Inspector */}
        <aside className="w-72 flex-shrink-0 border-l border-slate-200 flex flex-col overflow-hidden bg-white shadow-sm z-20">
          <div className="flex-shrink-0 flex p-1 bg-slate-50 border-b border-slate-200 m-2 rounded-lg">
            <button className={`flex-1 py-1.5 text-[10px] font-black uppercase tracking-widest rounded-md transition-all ${rightTab === "frame" ? "bg-white text-blue-600 shadow-sm" : "text-slate-400"}`} onClick={() => setRightTab("frame")}>Frame</button>
            <button className={`flex-1 py-1.5 text-[10px] font-black uppercase tracking-widest rounded-md transition-all ${rightTab === "layout" ? "bg-white text-blue-600 shadow-sm" : "text-slate-400"}`} onClick={() => setRightTab("layout")}>Layout</button>
          </div>
          <div className="flex-1 overflow-hidden">
            {rightTab === "frame" ? (
              <FramePanel
                slidePlan={effectiveSlidePlan}
                selectedZone={selectedZone}
                selectedRegion={selectedRegion}
                userSelection={state.userSelection}
                onFrameSelect={handleFrameSelect}
                onNoDesignToggle={() => {}}
              />
            ) : (
              <LayoutPanel
                selectedZone={selectedZone}
                userSelection={state.userSelection}
                onLayoutSelect={handleLayoutSelect}
                onApplyLayout={handleApplyPendingLayout}
                availableLayoutIds={runMeta?.layout_candidates}
                pipelineSelectedLayoutId={state.slidePlan?.layout_preset}
                pendingLayoutId={pendingLayout}
              />
            )}
          </div>
        </aside>
      </div>

      {/* Footer Actions */}
      <footer className="flex-shrink-0 flex items-center justify-between px-6 py-3 bg-white border-t border-slate-200 z-30">
        <div className="flex items-center gap-4">
          <div className="flex items-center gap-1.5">
            <div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
            <span className="text-[10px] font-bold text-slate-500 uppercase tracking-tighter">Phase Z Engine Active</span>
          </div>
        </div>
        <div className="flex items-center gap-3">
          <Button variant="outline" onClick={() => toast.info("연동하기 기능은 준비 중입니다.")} className="gap-2 h-9 text-[11px] font-bold uppercase tracking-widest border-slate-200"><Link2 className="w-3.5 h-3.5" />Connect</Button>
          <Button variant="outline" onClick={() => toast.info("다운로드 기능은 준비 중입니다.")} disabled={!state.slidePlan} className="gap-2 h-9 text-[11px] font-bold uppercase tracking-widest border-slate-200"><Download className="w-3.5 h-3.5" />Download</Button>
          <Button onClick={() => toast.success("슬라이드 설정이 확정되었습니다.")} disabled={!state.slidePlan || state.isLoading} className="gap-2 h-9 text-[11px] font-bold uppercase tracking-widest bg-slate-900 hover:bg-slate-800"><Sparkles className="w-3.5 h-3.5" />Finalize Slide</Button>
        </div>
      </footer>
    </div>
  );
}
