<script lang="ts">
  // 홈 대시보드 — 데일리 홈 cockpit (확정 시안 dashboard-sage-3 안1 골격 + 안2 검토/파이프라인 위젯 + 안3 도메인 분포 한 줄).
  // 정보 흐름: 인사 → 오늘 요약 띠(검토 대기 + 디제스트 + 스탯) → 2열(좌: 빠른 캡처·활동 / 우: 학습·도메인 분포·고정).
  // 데이터는 전부 기존 엔드포인트 wiring(백엔드 변경 0). 학습 streak/복습 마감은 전용 엔드포인트 부재라 링크형으로 degrade.
  import { onMount } from 'svelte';
  import {
    dashboardSummary,
    refresh,
    type DashboardSummary,
    type PipelineStatus,
    type QueueLag,
  } from '$lib/stores/system';
  import { domainBgClass, domainLabel } from '$lib/utils/domainSlug';
  import { user } from '$lib/stores/auth';
  import { api } from '$lib/api';
  import { queueOverview, refreshQueueOverview } from '$lib/stores/queueOverview';
  import {
    MACHINE_STATE_LABEL, machineChipClass, machineDotClass, formatRate, etaPhrase,
  } from '$lib/utils/queueDisplay';
  import type { QueueOverview } from '$lib/types/queue';
  import EmptyState from '$lib/components/ui/EmptyState.svelte';
  import Skeleton from '$lib/components/ui/Skeleton.svelte';
  import {
    Scale, FileText, Pin, ChevronRight, GraduationCap, Upload, Newspaper,
  } from 'lucide-svelte';
  import { addToast } from '$lib/stores/toast';

  let summary = $derived<DashboardSummary | null>($dashboardSummary);
  let loading = $derived(summary === null);

  // ─── 인사 헤더 ───
  const greetingName = $derived($user?.username ?? 'hyungi');
  const todayLabel = new Intl.DateTimeFormat('ko-KR', {
    year: 'numeric', month: 'long', day: 'numeric', weekday: 'long',
  }).format(new Date());

  // ─── 디제스트 헤드라인 (best-effort, 기존 /digest) ───
  interface DigestLead {
    topic_label: string;
    article_count: number;
    importance_score: number;
    country: string;
    date: string;
  }
  let digestLead = $state<DigestLead | null>(null);

  const COUNTRY_KO: Record<string, string> = {
    KR: '한국', JP: '일본', US: '미국', CN: '중국', DE: '독일',
    FR: '프랑스', GB: '영국', TW: '대만',
  };
  function countryKo(c: string): string {
    return COUNTRY_KO[c?.toUpperCase?.()] ?? c ?? '';
  }

  // ─── 도메인 분포 (best-effort, 기존 /documents/tree) ───
  interface DomainDist { name: string; count: number; }
  let domainDist = $state<DomainDist[]>([]);
  let domainTotal = $derived(domainDist.reduce((s, d) => s + d.count, 0));
  function domainCount(slugLike: string): number {
    // domainBgClass 와 동일 매핑 기준으로 특정 도메인 건수 추출 (스탯 띠용)
    const target = domainBgClass(slugLike);
    return domainDist.find((d) => domainBgClass(d.name) === target)?.count ?? 0;
  }

  // ─── 빠른 캡처 (기존 POST /memos) ───
  let captureText = $state('');
  let capturing = $state(false);
  async function quickCapture() {
    const content = captureText.trim();
    if (!content || capturing) return;
    capturing = true;
    try {
      await api('/memos/', { method: 'POST', body: JSON.stringify({ content }) });
      captureText = '';
      addToast('success', '메모 저장됨');
      void refresh(); // 메모 수 등 요약 즉시 갱신(60s 폴 기다리지 않음)
    } catch {
      addToast('error', '메모 저장 실패');
    } finally {
      capturing = false;
    }
  }
  function onCaptureKeydown(e: KeyboardEvent) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); void quickCapture(); }
  }

  // ─── 핀 고정 메모 (기존 /memos?pinned) ───
  let pinnedMemos = $state<any[]>([]);
  function pinTitle(memo: any): string {
    const firstLine = memo.content?.split('\n')[0]?.replace(/^#+\s*/, '').trim();
    return memo.title || firstLine || '메모';
  }

  onMount(async () => {
    // 핀 메모
    try {
      const res = await api<any>('/memos/?pinned=true&page_size=4&archived=false');
      pinnedMemos = res.items || [];
    } catch { /* 빈 배열 유지 */ }
    // 디제스트 최신 — countries→topics flatten 후 중요도 desc(동률 시 기사수 desc) top
    try {
      const d = await api<any>('/digest');
      const topics = (d.countries || []).flatMap((c: any) =>
        (c.topics || []).map((t: any) => ({ ...t, country: c.country })));
      topics.sort((a: any, b: any) =>
        (b.importance_score - a.importance_score) || (b.article_count - a.article_count));
      if (topics[0]) {
        digestLead = {
          topic_label: topics[0].topic_label,
          article_count: topics[0].article_count,
          importance_score: topics[0].importance_score,
          country: topics[0].country,
          date: d.digest_date,
        };
      }
    } catch { /* 디제스트 없으면 블록 자동 생략 */ }
    // 도메인 분포 — 트리 top-level 노드 건수
    try {
      const tree = await api<any[]>('/documents/tree');
      domainDist = (tree || [])
        .map((n) => ({ name: n.name as string, count: n.count as number }))
        .sort((a, b) => b.count - a.count);
    } catch { /* 분포 없으면 카드 자동 생략 */ }
  });

  // ─── 파이프라인 (기존 로직 재사용, 칩 요약 + 상세 접힘) ───
  const STAGE_ORDER = ['extract', 'stt', 'classify', 'embed', 'preview', 'thumbnail'] as const;
  const STAGE_LABEL: Record<string, string> = {
    extract: '추출', stt: '전사', classify: '분류', embed: '임베딩',
    preview: '미리보기', thumbnail: '썸네일',
  };

  // ─── 처리 머신 보드 (안2) + ETA (안5 라이트) — GET /api/queue/overview ───
  // 홈은 30s 폴링 (store 기본 60s 위에 추가 — inFlight 합치기로 중복 호출 0).
  // 백엔드 미배포/실패 시 store=null → 보드 자체가 조용히 생략 (silent 비차단).
  let queue = $derived<QueueOverview | null>($queueOverview);

  // 머신 담당 단계 라벨 — STAGE_LABEL 재사용 + overview 전용 단계 보강
  // (backend services/queue_overview.py _STAGE_ORDER 와 동기), 미지 키는 raw
  const QUEUE_STAGE_LABEL: Record<string, string> = {
    ...STAGE_LABEL,
    summarize: '요약', chunk: '청크', markdown: '마크다운',
    fulltext: '전문', deep_summary: '심층분석',
  };
  function queueStageLabel(stage: string): string {
    return QUEUE_STAGE_LABEL[stage] ?? stage;
  }

  onMount(() => {
    void refreshQueueOverview();
    const handle = setInterval(() => void refreshQueueOverview(), 30_000);
    return () => clearInterval(handle);
  });

  interface PipelineRow {
    stage: string; label: string;
    pending: number; processing: number; failed: number; total: number;
    oldestPendingAgeSec: number | null;
  }

  function buildPipelineRows(items: PipelineStatus[], lag: QueueLag[]): PipelineRow[] {
    const lagMap = new Map(lag.map((l) => [l.stage, l]));
    const grouped = new Map<string, { pending: number; processing: number; failed: number; ageSec: number | null }>();
    for (const it of items) {
      const cur = grouped.get(it.stage) ?? { pending: 0, processing: 0, failed: 0, ageSec: null };
      if (it.status === 'pending') cur.pending += it.count;
      else if (it.status === 'processing') cur.processing += it.count;
      else if (it.status === 'failed') cur.failed += it.count;
      grouped.set(it.stage, cur);
    }
    for (const l of lag) {
      grouped.set(l.stage, {
        pending: l.pending, processing: l.processing, failed: l.failed,
        ageSec: l.oldest_pending_age_sec,
      });
    }
    const allStages = new Set([...grouped.keys(), ...lagMap.keys()]);
    const orderedStages = [
      ...STAGE_ORDER.filter((s) => allStages.has(s)),
      ...[...allStages].filter((s) => !STAGE_ORDER.includes(s as never)),
    ];
    return orderedStages.map((stage) => {
      const r = grouped.get(stage)!;
      return {
        stage, label: STAGE_LABEL[stage] ?? stage,
        pending: r.pending, processing: r.processing, failed: r.failed,
        total: r.pending + r.processing + r.failed,
        oldestPendingAgeSec: r.ageSec,
      };
    });
  }

  let pipelineRows = $derived(
    summary ? buildPipelineRows(summary.pipeline_status, summary.queue_lag ?? []) : []
  );
  let pipelineMax = $derived(Math.max(1, ...pipelineRows.map((r) => r.total)));
  let totalFailed = $derived(summary?.failed_count ?? 0);
  let totalPending = $derived(pipelineRows.reduce((s, r) => s + r.pending, 0));
  let totalProcessing = $derived(pipelineRows.reduce((s, r) => s + r.processing, 0));

  let pipelineManualClosed = $state(false);
  let pipelineOpen = $derived(
    pipelineManualClosed ? false : (queue?.totals.failed ?? totalFailed) > 0
  );

  // 단계별 현황 (2026-06-11 피드백 재설계: 완료가 보여야 한다 — overview.stages 단일 소스)
  // active = 오늘 움직임이 있는 단계만, idle = 전부 0 인 단계는 한 줄로 숨김.
  let stageRows = $derived(queue?.stages ?? []);
  let activeStageRows = $derived(
    stageRows.filter((r) => r.pending + r.processing + r.failed + r.done_today > 0)
  );
  let idleStageRows = $derived(
    stageRows.filter((r) => r.pending + r.processing + r.failed + r.done_today === 0)
  );
  let stageDoneToday = $derived(stageRows.reduce((s, r) => s + r.done_today, 0));

  function formatAge(sec: number | null): string {
    if (sec == null || sec <= 0) return '';
    if (sec < 60) return `${sec}초 전`;
    if (sec < 3600) return `${Math.floor(sec / 60)}분 전`;
    if (sec < 86400) return `${Math.floor(sec / 3600)}시간 전`;
    return `${Math.floor(sec / 86400)}일 전`;
  }

  function formatTime(dateStr: string) {
    const d = new Date(dateStr);
    if (isNaN(d.getTime())) return ''; // 빈 문자열/유효하지 않은 created_at → 'Invalid Date' 회피
    const diff = Date.now() - d.getTime();
    if (diff < 60000) return '방금';
    if (diff < 3600000) return `${Math.floor(diff / 60000)}분 전`;
    if (diff < 86400000) return `${Math.floor(diff / 3600000)}시간 전`;
    if (diff < 604800000) return `${Math.floor(diff / 86400000)}일 전`;
    return d.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
  }
</script>

<div class="p-4 lg:p-8">
  <div class="max-w-[1680px] mx-auto">

    <!-- ═══ 인사 헤더 ═══ -->
    <div class="flex items-baseline gap-2.5 flex-wrap">
      <h1 class="text-2xl font-bold text-text tracking-tight">안녕하세요, {greetingName}</h1>
      <span class="text-sm text-dim">오늘도 지식 쌓는 날.</span>
    </div>
    <div class="text-xs text-faint mt-1 mb-6 tracking-wide">{todayLabel}</div>

    {#if loading}
      <!-- 스켈레톤 -->
      <div class="bg-surface border border-default rounded-card p-5 mb-5">
        <Skeleton w="w-40" h="h-10" />
        <Skeleton w="w-full" h="h-4" class="mt-4" />
        <Skeleton w="w-2/3" h="h-4" class="mt-2" />
      </div>
      <div class="grid grid-cols-1 lg:grid-cols-[1fr_360px] gap-5">
        <div class="space-y-5">
          <div class="bg-surface border border-default rounded-card p-5"><Skeleton w="w-24" h="h-4" /><Skeleton w="w-full" h="h-10" class="mt-3" /></div>
          <div class="bg-surface border border-default rounded-card p-5"><Skeleton w="w-24" h="h-4" /><Skeleton w="w-full" h="h-40" class="mt-3" /></div>
        </div>
        <div class="space-y-5">
          <div class="bg-surface border border-default rounded-card p-5"><Skeleton w="w-full" h="h-24" /></div>
          <div class="bg-surface border border-default rounded-card p-5"><Skeleton w="w-full" h="h-32" /></div>
        </div>
      </div>

    {:else if summary}

      <!-- ═══ 오늘 요약 띠 ═══ -->
      <div class="bg-surface border border-default rounded-card p-5 lg:p-6 mb-5">
        <!-- 검토 대기 + 디제스트 -->
        <div class="flex flex-col sm:flex-row items-stretch gap-5">
          <!-- 검토 대기 강조 -->
          <div class="flex flex-col justify-center sm:pr-6 sm:border-r border-default sm:min-w-[150px]">
            <span class="text-4xl font-extrabold tracking-tight leading-none {summary.inbox_count > 0 ? 'text-warning' : 'text-success'}">
              {summary.inbox_count.toLocaleString()}
            </span>
            <span class="text-[11px] text-dim mt-1.5 uppercase tracking-wide">검토 대기 문서</span>
            {#if summary.inbox_count > 0}
              <a href="/inbox" class="text-[11px] text-accent font-semibold mt-2 hover:underline">검토 시작 →</a>
            {:else}
              <span class="text-[11px] text-dim mt-2">미분류 없음</span>
            {/if}
          </div>

          <!-- 디제스트 톱 (best-effort) -->
          {#if digestLead}
            <a href="/digest" class="flex-1 flex flex-col justify-center gap-1.5 group">
              <div class="flex items-center gap-2">
                <span class="text-[10px] font-bold text-error bg-error/10 rounded px-1.5 py-0.5 uppercase tracking-wide">속보</span>
                <span class="text-[11px] text-faint">{digestLead.date} 브리핑</span>
              </div>
              <div class="text-[15px] font-semibold text-text leading-snug group-hover:text-accent transition-colors">
                {digestLead.topic_label}
              </div>
              <div class="text-[11px] text-dim">
                관련 기사 <strong class="text-text">{digestLead.article_count}건</strong>
                · 중요도 {digestLead.importance_score.toFixed(2)}
                · {countryKo(digestLead.country)}
              </div>
            </a>
          {:else}
            <a href="/news" class="flex-1 flex items-center gap-2 text-sm text-dim hover:text-accent transition-colors">
              <Newspaper size={16} /> 오늘의 뉴스 브리핑 보기 →
            </a>
          {/if}
        </div>

        <!-- 스탯 띠 -->
        <div class="flex flex-nowrap overflow-x-auto border-t border-default mt-4 pt-4">
          {@render stat((summary.documents_count ?? 0).toLocaleString(), '문서', 'text-accent')}
          {@render stat((summary.news_count ?? 0).toLocaleString(), '뉴스')}
          {#if domainTotal > 0}
            {@render stat(domainCount('Industrial_Safety').toLocaleString(), '산업안전', 'text-domain-safety')}
            {@render stat(domainCount('Engineering').toLocaleString(), '엔지니어링', 'text-domain-engineering')}
          {/if}
          {#if summary.category_counts?.library}
            {@render stat(summary.category_counts.library.toLocaleString(), '자료실')}
          {/if}
          {@render stat((summary.memos_count ?? 0).toLocaleString(), '메모')}
        </div>
      </div>

      <!-- ═══ 2열 본문 ═══ -->
      <div class="grid grid-cols-1 lg:grid-cols-[1fr_360px] gap-5 items-start">

        <!-- ─── 왼쪽 ─── -->
        <div class="space-y-5">

          <!-- 빠른 캡처 -->
          <div class="bg-surface border border-default rounded-card p-5">
            <div class="text-[11px] font-bold text-dim uppercase tracking-wider mb-3">빠른 캡처</div>
            <div class="flex gap-2 items-center">
              <input
                class="flex-1 h-9 border border-default rounded-md bg-bg text-text text-sm px-3.5 outline-none focus:border-accent transition-colors placeholder:text-faint"
                type="text"
                placeholder="메모 한 줄 남기기…"
                bind:value={captureText}
                onkeydown={onCaptureKeydown}
                disabled={capturing}
              />
              <button
                class="h-9 px-4 rounded-md bg-accent text-white text-xs font-semibold hover:bg-accent-hover transition-colors disabled:opacity-50 shrink-0"
                onclick={quickCapture}
                disabled={capturing || !captureText.trim()}
              >저장</button>
            </div>
            <div class="flex gap-2 mt-2.5">
              <a href="/documents" class="inline-flex items-center gap-1.5 text-[11px] text-accent-hover bg-accent/10 rounded-md px-2.5 py-1 hover:bg-accent/20 transition-colors">
                <Upload size={11} /> 파일 업로드
              </a>
            </div>
          </div>

          <!-- 최근 활동 타임라인 -->
          <div class="bg-surface border border-default rounded-card p-5">
            <div class="flex items-baseline justify-between mb-3">
              <span class="text-[11px] font-bold text-dim uppercase tracking-wider">최근 활동</span>
              <div class="flex items-center gap-3">
                {#if summary.law_alerts > 0}
                  <a href="/documents?source=law_monitor"
                    class="text-[11px] flex items-center gap-1 px-2.5 py-1 rounded-full bg-warning/10 text-warning border border-warning/20 hover:bg-warning/20 transition-colors">
                    <Scale size={11} /> 법령 {summary.law_alerts}
                  </a>
                {/if}
                <a href="/documents" class="text-[11px] text-accent hover:underline">전체 보기 →</a>
              </div>
            </div>

            {#if summary.recent_documents.length > 0}
              <div class="flex flex-col">
                {#each summary.recent_documents as doc, i (doc.id)}
                  <a href="/documents/{doc.id}"
                    class="grid grid-cols-[auto_14px_minmax(0,1fr)] gap-x-3 py-2.5 {i > 0 ? 'border-t border-default' : ''} group">
                    <div class="text-[10px] text-faint text-right pt-1 whitespace-nowrap tabular-nums w-14">{formatTime(doc.created_at)}</div>
                    <div class="flex flex-col items-center">
                      <span class="w-2 h-2 rounded-full mt-1.5 shrink-0 {domainBgClass(doc.ai_domain)}"></span>
                      {#if i < summary.recent_documents.length - 1}<span class="flex-1 w-px bg-default mt-1"></span>{/if}
                    </div>
                    <div class="pb-1 min-w-0">
                      <div class="text-[10px] font-bold uppercase tracking-wide text-dim mb-0.5">{domainLabel(doc.ai_domain)}</div>
                      <div class="text-[13px] text-text leading-snug group-hover:text-accent transition-colors truncate">{doc.title || '제목 없음'}</div>
                    </div>
                  </a>
                {/each}
              </div>
            {:else}
              <EmptyState
                icon={FileText}
                title="아직 문서가 없습니다"
                description="NAS PKM 폴더에 파일을 추가하면 자동으로 인덱싱됩니다."
              />
            {/if}
          </div>
        </div>

        <!-- ─── 오른쪽 ─── -->
        <div class="space-y-5">

          <!-- 학습 (streak/복습 마감은 백엔드 부재로 링크형 degrade) -->
          <a href="/study" class="block bg-surface border border-default rounded-card p-5 hover:bg-surface-hover transition-colors group">
            <div class="flex items-center justify-between">
              <span class="text-[11px] font-bold text-dim uppercase tracking-wider">학습</span>
              <GraduationCap size={16} class="text-faint" />
            </div>
            <div class="text-[15px] font-semibold text-text mt-3 group-hover:text-accent transition-colors">암기 노트 학습 시작 →</div>
            <div class="text-[11px] text-dim mt-1">검수함 · 복습함 · 암기카드</div>
          </a>

          <!-- 도메인 분포 + 파이프라인 -->
          {#if domainDist.length > 0}
            <div class="bg-surface border border-default rounded-card p-5">
              <div class="text-[11px] font-bold text-dim uppercase tracking-wider mb-2">도메인 분포</div>
              <div class="text-[11px] text-faint mb-3">전체 <strong class="text-base font-bold text-text tracking-tight align-baseline">{domainTotal.toLocaleString()}</strong>건</div>

              <!-- 분포 막대 -->
              <div class="flex gap-0.5 h-2 rounded mb-4 overflow-hidden">
                {#each domainDist as d (d.name)}
                  <div class="h-full rounded-sm {domainBgClass(d.name)}" style="width:{domainTotal > 0 ? (d.count / domainTotal) * 100 : 0}%"></div>
                {/each}
              </div>

              <div class="flex flex-col gap-1.5">
                {#each domainDist.slice(0, 6) as d (d.name)}
                  <a href="/documents?domain={encodeURIComponent(d.name)}" class="flex items-center gap-2 text-xs hover:text-accent transition-colors group">
                    <span class="w-2.5 h-2.5 rounded-sm shrink-0 {domainBgClass(d.name)}"></span>
                    <span class="flex-1 min-w-0 text-text truncate group-hover:text-accent">{domainLabel(d.name)}</span>
                    <span class="font-semibold text-dim tabular-nums">{d.count.toLocaleString()}</span>
                  </a>
                {/each}
              </div>

              <!-- 파이프라인 칩 (안2 흡수) -->
              <div class="flex items-center gap-1.5 flex-wrap mt-4 pt-3.5 border-t border-default">
                <span class="text-[10px] text-faint uppercase tracking-wide mr-1">파이프라인</span>
                {#if totalFailed > 0}<span class="text-[10px] font-bold rounded px-2 py-0.5 text-error bg-error/10">실패 {totalFailed}</span>{/if}
                {#if totalPending > 0}<span class="text-[10px] font-bold rounded px-2 py-0.5 text-warning bg-warning/10">대기 {totalPending}</span>{/if}
                {#if totalProcessing > 0}<span class="text-[10px] font-bold rounded px-2 py-0.5 text-success bg-success/10">처리중 {totalProcessing}</span>{/if}
                {#if totalFailed === 0 && totalPending === 0 && totalProcessing === 0}<span class="text-[10px] font-bold rounded px-2 py-0.5 text-success bg-success/10">정상</span>{/if}
              </div>
            </div>
          {/if}

          <!-- 고정 항목 -->
          {#if pinnedMemos.length > 0}
            <div class="bg-surface border border-default rounded-card p-5">
              <div class="flex items-baseline justify-between mb-3">
                <span class="text-[11px] font-bold text-dim uppercase tracking-wider">고정 항목</span>
                <a href="/memos" class="text-[11px] text-accent hover:underline">관리 →</a>
              </div>
              <div class="flex flex-col gap-2">
                {#each pinnedMemos as memo (memo.id)}
                  <a href="/memos" class="flex items-start gap-2.5 px-3 py-2.5 rounded-lg bg-bg hover:bg-surface-hover transition-colors">
                    <span class="text-[9px] font-bold rounded px-1.5 py-0.5 uppercase tracking-wide shrink-0 mt-0.5 text-accent-hover bg-accent/10">메모</span>
                    <span class="text-xs text-text leading-snug flex-1 min-w-0 break-words">{pinTitle(memo)}</span>
                    <Pin size={11} class="text-faint shrink-0 mt-0.5" />
                  </a>
                {/each}
              </div>
            </div>
          {/if}
        </div>
      </div>

      <!-- ═══ 처리 머신 보드 (안2) + ETA 라인 (안5 라이트) ═══ -->
      {#if queue}
        <div class="mt-5">
          <div class="text-[11px] font-bold text-dim uppercase tracking-wider mb-3">처리 머신</div>
          <div class="grid grid-cols-1 md:grid-cols-3 gap-3">
            {#each queue.machines as m (m.key)}
              <div class="bg-surface border border-default rounded-card p-4">
                <!-- 헤더: 상태 dot + 라벨 + state 칩 -->
                <div class="flex items-center justify-between gap-2 mb-2">
                  <span class="flex items-center gap-2 text-[13px] font-bold text-text min-w-0">
                    <span class="w-2 h-2 rounded-full shrink-0 {machineDotClass(m.state)}"></span>
                    <span class="truncate">{m.label}</span>
                  </span>
                  <span class="text-[10px] font-bold rounded-full px-2 py-0.5 shrink-0 {machineChipClass(m.state)}">
                    {MACHINE_STATE_LABEL[m.state]}
                  </span>
                </div>
                <!-- 담당 단계 칩 -->
                {#if m.stages.length > 0}
                  <div class="flex flex-wrap gap-1 mb-2.5">
                    {#each m.stages as s (s)}
                      <span class="text-[10px] font-semibold rounded-full px-2 py-0.5 bg-surface-hover text-dim">{queueStageLabel(s)}</span>
                    {/each}
                  </div>
                {/if}
                <!-- 대기 · 처리율 · 오늘 -->
                <div class="text-xs text-dim tabular-nums">
                  대기 <strong class="text-text">{m.pending.toLocaleString()}</strong>
                  · 처리율 <strong class="text-text">{formatRate(m.done_1h)}/h</strong>
                  · 오늘 <strong class="text-text">{m.done_today.toLocaleString()}</strong>건
                </div>
                <!-- 맥북 보류 (sleep 등 자동 재개 대기) -->
                {#if m.key === 'macbook' && m.deferred_pending > 0}
                  <div class="text-[11px] font-semibold text-warning mt-1.5 tabular-nums">보류 {m.deferred_pending.toLocaleString()}건 — 자동 재개 대기</div>
                {/if}
                <!-- 지금 처리 중인 문서 -->
                {#if m.current.length > 0}
                  <div class="text-[11px] text-dim border-t border-dashed border-default mt-2.5 pt-2 truncate"
                    title={m.current.map((c) => `${c.title} (${queueStageLabel(c.stage)})`).join(' · ')}>
                    지금: {m.current[0].title} ({queueStageLabel(m.current[0].stage)}){m.current.length > 1 ? ` 외 ${m.current.length - 1}건` : ''}
                  </div>
                {/if}
              </div>
            {/each}
          </div>

          <!-- ETA 한 줄 (안5 라이트 — 추정치) -->
          <div class="text-xs text-dim mt-2.5 px-1 tabular-nums"
            title="현재 페이스 기반 추정치 — 유입 변동 시 달라질 수 있습니다">
            요약 대기 <strong class="text-text">{queue.summarize_eta.pending.toLocaleString()}건</strong>
            — 소화 {formatRate(queue.summarize_eta.done_rate_1h)}/h
            · 유입 {formatRate(queue.summarize_eta.inflow_rate_1h)}/h
            {#if queue.summarize_eta.eta_minutes != null}
              · <span class="text-accent font-semibold">{etaPhrase(queue.summarize_eta.eta_minutes)}</span>
            {:else}
              · 유입 우세(백필 중)
            {/if}
          </div>
        </div>
      {/if}

      <!-- ═══ 단계 상세 (기존 stage 테이블 — 접힘 강등, 실패 있을 때 자동 펼침) ═══ -->
      <details
        class="mt-5"
        open={pipelineOpen}
        ontoggle={(e) => { if (!e.currentTarget.open) pipelineManualClosed = true; }}
      >
        <summary class="flex items-center justify-between px-5 py-3.5 bg-surface border border-default rounded-card cursor-pointer hover:bg-surface-hover transition-colors select-none list-none">
          <span class="text-sm font-semibold text-text flex items-center gap-2">
            <ChevronRight size={14} class="transition-transform details-chevron" />
            단계별 현황
          </span>
          <span class="text-xs text-dim flex items-center gap-2.5">
            {#if queue}
              {#if stageDoneToday > 0}<span class="text-success">오늘 {stageDoneToday.toLocaleString()} 완료</span>{/if}
              {#if queue.totals.failed > 0}<span class="text-error font-medium">실패 {queue.totals.failed}</span>{/if}
              {#if queue.totals.pending > 0}<span>대기 {queue.totals.pending.toLocaleString()}</span>{/if}
              {#if stageDoneToday === 0 && queue.totals.failed === 0 && queue.totals.pending === 0}<span>모든 단계 한가함</span>{/if}
            {:else}
              {#if totalFailed > 0}<span class="text-error font-medium">실패 {totalFailed}</span>{/if}
              {#if totalPending > 0}<span>대기 {totalPending}</span>{/if}
            {/if}
          </span>
        </summary>

        <div class="mt-2 px-5 py-4 bg-surface border border-default rounded-card">
          {#if queue}
            {#if activeStageRows.length > 0}
              <div class="space-y-3.5">
                {#each activeStageRows as row (row.stage)}
                  {@const total = row.done_today + row.pending + row.processing}
                  {@const donePct = total > 0 ? (row.done_today / total) * 100 : 0}
                  {@const procPct = total > 0 ? (row.processing / total) * 100 : 0}
                  <div>
                    <div class="flex items-baseline justify-between text-xs mb-1.5 gap-2">
                      <span class="font-medium text-text flex items-center gap-1.5 whitespace-nowrap">
                        {queueStageLabel(row.stage)}
                        {#if row.processing > 0}
                          <span class="inline-block w-1.5 h-1.5 rounded-full bg-accent animate-pulse"></span>
                          <span class="text-accent font-normal">처리 중 {row.processing}</span>
                        {/if}
                      </span>
                      <span class="text-dim tabular-nums flex items-center gap-2.5 whitespace-nowrap">
                        {#if row.done_today > 0}<span class="text-success">오늘 {row.done_today.toLocaleString()} 완료</span>{/if}
                        {#if row.pending > 0}<span>대기 {row.pending.toLocaleString()}</span>{/if}
                        {#if row.failed > 0}<span class="text-error font-medium">실패 {row.failed}</span>{/if}
                      </span>
                    </div>
                    <!-- 게이지 = 이 단계의 오늘 진척 (완료 / 완료+대기) — 가득 찬 초록 = 다 끝남 -->
                    <div class="flex h-1.5 w-full overflow-hidden rounded-sm bg-bg" title="오늘 완료 {row.done_today.toLocaleString()} / 잔여 {row.pending.toLocaleString()}">
                      {#if donePct > 0}<div class="bg-success/70 h-full" style="width: {donePct}%"></div>{/if}
                      {#if procPct > 0}<div class="bg-accent h-full" style="width: {Math.max(procPct, 1)}%"></div>{/if}
                    </div>
                    {#if row.pending > 0 && row.oldest_pending_age_sec && row.oldest_pending_age_sec > 600}
                      <p class="text-[10px] mt-1 tabular-nums {row.oldest_pending_age_sec > 21600 ? 'text-warning' : 'text-faint'}">
                        가장 오래 기다린 항목 {formatAge(row.oldest_pending_age_sec)}
                      </p>
                    {/if}
                  </div>
                {/each}
              </div>
            {:else}
              <p class="text-xs text-dim text-center py-3">대기·처리·실패 없음 — 모든 단계가 한가합니다</p>
            {/if}
            {#if idleStageRows.length > 0}
              <p class="text-[11px] text-faint mt-4 pt-3 border-t border-default">
                비어 있음: {idleStageRows.map((r) => queueStageLabel(r.stage)).join(' · ')}
              </p>
            {/if}
          {:else}
            <p class="text-xs text-dim text-center py-3">현황을 불러오지 못했습니다</p>
          {/if}
        </div>
      </details>

    {/if}
  </div>
</div>

{#snippet stat(value: string, label: string, colorClass = 'text-text')}
  <div class="flex flex-col items-start px-4 first:pl-0 border-l border-default first:border-l-0 min-w-[64px]">
    <span class="text-xl font-bold tracking-tight leading-none {colorClass}">{value}</span>
    <span class="text-[10px] text-faint mt-1 uppercase tracking-wide">{label}</span>
  </div>
{/snippet}

<style>
  details[open] :global(.details-chevron) { transform: rotate(90deg); }
  details summary::-webkit-details-marker { display: none; }
</style>
