<script>
  import { page } from '$app/stores';
  import { goto } from '$app/navigation';
  import { api } from '$lib/api';
  import { addToast } from '$lib/stores/toast';
  import { Info, List, LayoutGrid, ChevronLeft, X, Plus, Trash2, Tag, FolderTree, Rows3, Rows2, Sparkles } from 'lucide-svelte';
  import DocumentCard from '$lib/components/DocumentCard.svelte';
  import DocumentTable from '$lib/components/DocumentTable.svelte';
  import DocumentViewer from '$lib/components/DocumentViewer.svelte';
  import DocumentMetaRail from '$lib/components/DocumentMetaRail.svelte';
  import UploadDropzone from '$lib/components/UploadDropzone.svelte';
  import Drawer from '$lib/components/ui/Drawer.svelte';
  import Modal from '$lib/components/ui/Modal.svelte';
  import ConfirmDialog from '$lib/components/ui/ConfirmDialog.svelte';
  import Button from '$lib/components/ui/Button.svelte';
  import Select from '$lib/components/ui/Select.svelte';
  import TextInput from '$lib/components/ui/TextInput.svelte';
  import { ui } from '$lib/stores/uiState.svelte';
  import { useIsXl } from '$lib/composables/useMedia.svelte';
  import { useListKeyboardNav } from '$lib/composables/useListKeyboardNav.svelte';
  import { pLimit } from '$lib/utils/pLimit';
  import { isQuestion } from '$lib/utils/isQuestion';
  import AskAnswerCard from '$lib/components/AskAnswerCard.svelte';

  // D.2: 필터 칩에서 사용할 format 화이트리스트.
  // 백엔드 `GET /documents/?format=...` 파라미터가 이미 받음.
  const FORMATS = ['pdf', 'hwp', 'hwpx', 'md', 'docx', 'xlsx', 'png', 'jpg'];

  // D.3: bulk 도메인 옵션 (Sidebar DOMAIN_COLORS와 일치)
  const DOMAIN_OPTIONS = [
    { value: 'Knowledge/Philosophy', label: 'Knowledge / Philosophy' },
    { value: 'Knowledge/Language', label: 'Knowledge / Language' },
    { value: 'Knowledge/Engineering', label: 'Knowledge / Engineering' },
    { value: 'Knowledge/Industrial_Safety', label: 'Knowledge / Industrial Safety' },
    { value: 'Knowledge/Programming', label: 'Knowledge / Programming' },
    { value: 'Knowledge/General', label: 'Knowledge / General' },
    { value: 'Reference', label: 'Reference' },
  ];

  const MAX_SELECTION = 50;

  // 뷰 모드 (localStorage 기억)
  let viewMode = $state(typeof localStorage !== 'undefined' ? (localStorage.getItem('viewMode') || 'card') : 'card');
  function toggleViewMode() {
    viewMode = viewMode === 'card' ? 'table' : 'card';
    if (typeof localStorage !== 'undefined') localStorage.setItem('viewMode', viewMode);
  }

  // D.5: 테이블 밀도 (localStorage 기억)
  let tableDensity = $state(
    typeof localStorage !== 'undefined'
      ? (localStorage.getItem('tableDensity') || 'comfortable')
      : 'comfortable'
  );
  function toggleDensity() {
    tableDensity = tableDensity === 'compact' ? 'comfortable' : 'compact';
    if (typeof localStorage !== 'undefined') localStorage.setItem('tableDensity', tableDensity);
  }

  let documents = $state([]);
  let total = $state(0);
  let loading = $state(true);
  let searchQuery = $state('');
  let searchMode = $state('hybrid');
  let searchResults = $state(null);
  let selectedDoc = $state(null);

  // Phase D: 이드 답변 상태
  let askData = $state(null);
  let askLoading = $state(false);
  let askError = $state(false);
  let askDismissed = $state(false);

  /** ask 호출 래퍼 — 향후 GET→POST 전환 시 여기만 수정 */
  async function askSearch(q) {
    askLoading = true;
    askError = false;
    askData = null;
    try {
      askData = await api(`/search/ask?q=${encodeURIComponent(q)}&limit=10`);
    } catch {
      askError = true;
      askData = null;
    } finally {
      askLoading = false;
    }
  }

  let showAskCard = $derived(
    !askDismissed && (
      askLoading ||
      (askData && !askData.refused && askData.ai_answer && askData.synthesis_status === 'completed')
    )
  );

  // D.1: 3-패널 레이아웃 상태.
  // - xl+ : inline rail 사용 (metaRailOpen, localStorage persistent)
  // - < xl: Drawer 프리미티브 사용 (ui.openDrawer('meta'))
  const isXl = useIsXl();

  let metaRailOpen = $state(
    typeof localStorage !== 'undefined'
      ? localStorage.getItem('metaRailOpen') !== 'false'
      : true
  );

  function setMetaRailOpen(v) {
    metaRailOpen = v;
    if (typeof localStorage !== 'undefined') {
      localStorage.setItem('metaRailOpen', String(v));
    }
  }

  function toggleInfoPanel() {
    if (!selectedDoc) return;
    if (isXl.current) {
      setMetaRailOpen(!metaRailOpen);
    } else {
      if (ui.isDrawerOpen('meta')) ui.closeDrawer();
      else ui.openDrawer('meta');
    }
  }

  function handleDocDelete() {
    selectedDoc = null;
    if (ui.isDrawerOpen('meta')) ui.closeDrawer();
    loadDocuments();
  }

  // xl+ 진입 시 열려있던 drawer는 자동으로 닫는다 (rail로 승계).
  $effect(() => {
    if (isXl.current && ui.isDrawerOpen('meta')) {
      ui.closeDrawer();
    }
  });

  // URL params → filter
  let currentPage = $derived(parseInt($page.url.searchParams.get('page') || '1'));
  let filterDomain = $derived($page.url.searchParams.get('domain') || '');
  let filterSubGroup = $derived($page.url.searchParams.get('sub_group') || '');
  let filterTag = $derived($page.url.searchParams.get('tag') || '');
  let filterSource = $derived($page.url.searchParams.get('source') || '');
  let filterFormat = $derived($page.url.searchParams.get('format') || '');
  let filterCategory = $derived($page.url.searchParams.get('category') || '');

  // D.2: 필터 칩 popover 상태
  let tagPopoverOpen = $state(false);
  let formatPopoverOpen = $state(false);

  // D.3: 다중 선택 + bulk action 상태
  let selectedIds = $state(new Set());
  let bulkDomainValue = $state('');
  let bulkTagValue = $state('');
  let bulkBusy = $state(false);

  // D.4: 키보드 네비게이션
  let kbIndex = $state(0);

  function scrollSelectedIntoView() {
    // 다음 tick에 DOM 업데이트 후 선택 행으로 스크롤
    queueMicrotask(() => {
      const el = document.querySelector('[data-kb-selected="true"]');
      if (el instanceof HTMLElement) {
        el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
      }
    });
  }

  useListKeyboardNav({
    get items() {
      return items;
    },
    get index() {
      return kbIndex;
    },
    setIndex(i) {
      kbIndex = i;
      scrollSelectedIntoView();
    },
    onenter(doc) {
      if (doc) selectDoc(doc);
    },
  });

  $effect(() => {
    const _p = currentPage;
    const _d = filterDomain;
    const _s = filterSubGroup;
    const _t = filterTag;
    const _src = filterSource;
    const _f = filterFormat;
    const _c = filterCategory;
    const urlQ = $page.url.searchParams.get('q') || '';
    const urlMode = $page.url.searchParams.get('mode') || 'hybrid';

    searchQuery = urlQ;
    searchMode = urlMode;
    selectedDoc = null;
    selectedIds = new Set(); // D.3: URL/필터 변경 시 선택 초기화
    kbIndex = 0; // D.4: 키보드 커서 초기화
    askData = null; askLoading = false; askError = false; askDismissed = false;
    if (ui.isDrawerOpen('meta')) ui.closeDrawer();

    if (urlQ) {
      doSearch(urlQ, urlMode);
    } else {
      loadDocuments();
    }
  });

  async function loadDocuments() {
    loading = true;
    searchResults = null;
    try {
      const params = new URLSearchParams();
      params.set('page', String(currentPage));
      params.set('page_size', '20');
      if (filterDomain) params.set('domain', filterDomain);
      if (filterSubGroup) params.set('sub_group', filterSubGroup);
      if (filterTag) params.set('tag', filterTag);
      if (filterSource) params.set('source', filterSource);
      if (filterFormat) params.set('format', filterFormat);
      if (filterCategory) params.set('category', filterCategory);

      const data = await api(`/documents/?${params}`);
      documents = data.items;
      total = data.total;
    } catch (err) {
      addToast('error', '문서 목록 로딩 실패');
    } finally {
      loading = false;
    }
  }

  function submitSearch() {
    const params = new URLSearchParams($page.url.searchParams);
    params.delete('page');
    if (searchQuery.trim()) {
      params.set('q', searchQuery.trim());
    } else {
      params.delete('q');
    }
    if (searchMode !== 'hybrid') {
      params.set('mode', searchMode);
    } else {
      params.delete('mode');
    }
    for (const [key, val] of [...params.entries()]) {
      if (!val) params.delete(key);
    }
    const qs = params.toString();
    goto(`/documents${qs ? '?' + qs : ''}`, { noScroll: true });
  }

  function handleSearchKeydown(e) {
    if (e.key === 'Enter' && !e.isComposing) {
      e.preventDefault();
      e.stopPropagation(); // 키보드 nav의 Enter(selectDoc) 차단
      submitSearch();
    }
  }

  async function doSearch(q, mode) {
    loading = true;

    // 질문형이면 ask 병렬 호출 (검색 결과와 독립)
    if (isQuestion(q)) {
      askSearch(q); // fire-and-forget, 자체적으로 askLoading/askData 관리
    }

    try {
      const data = await api(`/search/?q=${encodeURIComponent(q)}&mode=${mode}&limit=50`);
      searchResults = data.results;
      total = data.total;
    } catch (err) {
      addToast('error', '검색 실패');
      searchResults = [];
    } finally {
      loading = false;
    }
  }

  function changePage(p) {
    const params = new URLSearchParams($page.url.searchParams);
    if (p > 1) {
      params.set('page', String(p));
    } else {
      params.delete('page');
    }
    const qs = params.toString();
    goto(`/documents${qs ? '?' + qs : ''}`, { noScroll: true });
  }

  function clearAllFilters() {
    goto('/documents', { noScroll: true });
    searchQuery = '';
  }

  // D.2: 필터 add/remove 헬퍼 — URL 라운드트립으로 필터 상태 관리.
  function addFilter(key, value) {
    const params = new URLSearchParams($page.url.searchParams);
    params.set(key, value);
    params.delete('page');
    const qs = params.toString();
    goto(`/documents${qs ? '?' + qs : ''}`, { noScroll: true });
    tagPopoverOpen = false;
    formatPopoverOpen = false;
  }

  function removeFilter(key) {
    const params = new URLSearchParams($page.url.searchParams);
    params.delete(key);
    // domain 제거 시 sub_group도 함께 제거 (의존성)
    if (key === 'domain') params.delete('sub_group');
    const qs = params.toString();
    goto(`/documents${qs ? '?' + qs : ''}`, { noScroll: true });
  }

  function selectDoc(doc) {
    selectedDoc = selectedDoc?.id === doc.id ? null : doc;
  }

  // D.3: 다중 선택 핸들러
  function handleSelectionChange(next) {
    selectedIds = next;
  }

  function selectAll() {
    selectedIds = new Set(items.map((d) => d.id));
  }

  function clearSelection() {
    selectedIds = new Set();
  }

  // D.3: bulk action 실행기 — pLimit(5) + Promise.allSettled.
  // TODO(backend): POST /documents/batch-update — 단일 트랜잭션으로 교체.
  async function runBulk(label, taskFn) {
    const ids = [...selectedIds];
    if (ids.length === 0 || ids.length > MAX_SELECTION) return;
    bulkBusy = true;
    const limit = pLimit(5);
    const results = await Promise.allSettled(
      ids.map((id) => limit(() => taskFn(id)))
    );
    bulkBusy = false;
    const success = results.filter((r) => r.status === 'fulfilled').length;
    const failed = results.length - success;
    if (failed > 0) {
      addToast('error', `${label}: ${failed}건 실패`);
    }
    if (success > 0) {
      addToast('success', `${label}: ${success}건 완료`);
    }
    clearSelection();
    loadDocuments();
  }

  async function bulkApplyDomain() {
    if (!bulkDomainValue) return;
    const domain = bulkDomainValue;
    await runBulk('도메인 변경', (id) =>
      api(`/documents/${id}`, {
        method: 'PATCH',
        body: JSON.stringify({ ai_domain: domain }),
      })
    );
    bulkDomainValue = '';
    ui.closeModal('bulk-domain');
  }

  async function bulkAddTag() {
    const tag = bulkTagValue.trim();
    if (!tag) return;
    await runBulk('태그 추가', async (id) => {
      const doc = items.find((d) => d.id === id);
      const existing = Array.isArray(doc?.ai_tags) ? doc.ai_tags : [];
      if (existing.includes(tag)) return; // 이미 있으면 skip
      const nextTags = [...existing, tag];
      return api(`/documents/${id}`, {
        method: 'PATCH',
        body: JSON.stringify({ ai_tags: nextTags }),
      });
    });
    bulkTagValue = '';
    ui.closeModal('bulk-tag');
  }

  async function bulkDelete() {
    await runBulk('삭제', (id) =>
      api(`/documents/${id}?delete_file=true`, { method: 'DELETE' })
    );
    // ConfirmDialog가 자체적으로 닫힘
  }

  function handleKeydown(e) {
    if (e.key === 'Escape') {
      ui.handleEscape(); // drawer/modal stack 우선순위로 중앙 처리
    }
  }

  let totalPages = $derived(Math.ceil(total / 20));
  let items = $derived(searchResults || documents);
  let hasActiveFilters = $derived(
    !!filterDomain || !!filterSubGroup || !!filterTag || !!filterSource || !!filterFormat || !!searchQuery
  );

  // D.3 derived
  let selectionCount = $derived(selectedIds.size);
  let selectionOverLimit = $derived(selectionCount > MAX_SELECTION);
  let allVisibleSelected = $derived(
    items.length > 0 && items.every((d) => selectedIds.has(d.id))
  );

  // D.4: 키보드 nav 커서 ID (scrollIntoView 대상)
  // items가 변경되면 kbIndex를 범위 내로 clamp
  $effect(() => {
    if (kbIndex >= items.length && items.length > 0) {
      kbIndex = items.length - 1;
    } else if (items.length === 0) {
      kbIndex = 0;
    }
  });
  let kbSelectedId = $derived(items[kbIndex]?.id ?? null);

  // D.2: 현재 결과 집계 — 상위 20개 태그 (클라이언트 집계, 백엔드 변경 없음).
  let topTags = $derived.by(() => {
    const counts = new Map();
    for (const d of items) {
      for (const t of d.ai_tags || []) {
        counts.set(t, (counts.get(t) || 0) + 1);
      }
    }
    return [...counts.entries()]
      .sort((a, b) => b[1] - a[1])
      .slice(0, 20);
  });

  // 바깥 클릭으로 popover 닫기
  $effect(() => {
    if (!tagPopoverOpen && !formatPopoverOpen) return;
    function onDocClick(e) {
      const target = e.target;
      if (!(target instanceof Element)) return;
      if (!target.closest('[data-popover-root]')) {
        tagPopoverOpen = false;
        formatPopoverOpen = false;
      }
    }
    document.addEventListener('click', onDocClick);
    return () => document.removeEventListener('click', onDocClick);
  });
</script>

<svelte:window on:keydown={handleKeydown} />

<div class="flex h-full">
  <!-- 가운데: 기존 list + viewer 세로 split (변경 없음) -->
  <div class="flex-1 min-w-0 flex flex-col min-h-0">
  <!-- 상단 영역 -->
  <div class="{selectedDoc ? 'h-[30%] shrink-0 border-b border-default' : 'flex-1'} flex flex-col min-h-0">
    <!-- 업로드 드롭존 -->
    <UploadDropzone onupload={loadDocuments} />

    <!-- 검색바 + 정보 버튼 (고정) -->
    <div class="flex gap-2 px-4 py-2 shrink-0">
      <input
        data-search-input
        type="text"
        bind:value={searchQuery}
        onkeydown={handleSearchKeydown}
        placeholder="검색어 입력 후 Enter (/ 키로 포커스)"
        class="flex-1 px-3 py-1.5 bg-surface border border-default rounded-lg text-text text-sm focus:border-accent outline-none"
      />
      <select
        bind:value={searchMode}
        onchange={() => { if (searchQuery) submitSearch(); }}
        class="hidden sm:block px-2 py-1.5 bg-surface border border-default rounded-lg text-text text-xs"
      >
        <option value="hybrid">하이브리드</option>
        <option value="fts">전문검색</option>
        <option value="trgm">부분매칭</option>
        <option value="vector">의미검색</option>
      </select>
      {#if searchQuery.trim()}
        <a
          href={`/ask?q=${encodeURIComponent(searchQuery.trim())}`}
          class="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-default text-dim hover:text-accent hover:border-accent transition-colors text-xs"
          title="이 쿼리로 AI 답변 보기"
        >
          <Sparkles size={14} /> <span class="hidden sm:inline">AI 답변</span>
        </a>
      {/if}
      <button
        onclick={toggleViewMode}
        class="p-1.5 rounded-lg border border-default text-dim hover:text-accent hover:border-accent transition-colors"
        aria-label="뷰 모드 전환"
        title={viewMode === 'card' ? '테이블 뷰' : '카드 뷰'}
      >
        {#if viewMode === 'card'}
          <List size={16} />
        {:else}
          <LayoutGrid size={16} />
        {/if}
      </button>
      {#if viewMode === 'table'}
        <button
          type="button"
          onclick={toggleDensity}
          class="p-1.5 rounded-lg border border-default text-dim hover:text-accent hover:border-accent transition-colors"
          aria-label="밀도 전환"
          title={tableDensity === 'compact' ? '여유 있게' : '촘촘하게'}
        >
          {#if tableDensity === 'compact'}
            <Rows2 size={16} />
          {:else}
            <Rows3 size={16} />
          {/if}
        </button>
      {/if}
      {#if selectedDoc}
        {@const isPanelActive = isXl.current ? metaRailOpen : ui.isDrawerOpen('meta')}
        <button
          type="button"
          onclick={toggleInfoPanel}
          class="p-1.5 rounded-lg border transition-colors {isPanelActive
            ? 'border-accent text-accent bg-accent/10'
            : 'border-default text-dim hover:text-accent hover:border-accent'}"
          aria-label="문서 정보"
          aria-pressed={isPanelActive}
          title="문서 정보"
        >
          <Info size={16} />
        </button>
      {/if}
    </div>

    <!-- D.2: 필터 칩 row -->
    <div class="flex flex-wrap items-center gap-1.5 px-4 pb-2 shrink-0">
      {#if filterDomain}
        <span class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-accent/15 text-accent border border-accent/30">
          {filterDomain.replace('Knowledge/', '')}{filterSubGroup ? ` / ${filterSubGroup}` : ''}
          <button
            type="button"
            onclick={() => removeFilter('domain')}
            class="hover:text-error -mr-0.5 ml-0.5"
            aria-label="도메인 필터 제거"
          >
            <X size={10} />
          </button>
        </span>
      {/if}

      {#if filterTag}
        <span class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-surface text-text border border-default">
          #{filterTag}
          <button
            type="button"
            onclick={() => removeFilter('tag')}
            class="hover:text-error -mr-0.5 ml-0.5"
            aria-label="태그 필터 제거"
          >
            <X size={10} />
          </button>
        </span>
      {/if}

      {#if filterFormat}
        <span class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-surface text-text border border-default uppercase">
          {filterFormat}
          <button
            type="button"
            onclick={() => removeFilter('format')}
            class="hover:text-error -mr-0.5 ml-0.5"
            aria-label="형식 필터 제거"
          >
            <X size={10} />
          </button>
        </span>
      {/if}

      {#if filterSource}
        <span class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-surface text-text border border-default">
          {filterSource}
          <button
            type="button"
            onclick={() => removeFilter('source')}
            class="hover:text-error -mr-0.5 ml-0.5"
            aria-label="소스 필터 제거"
          >
            <X size={10} />
          </button>
        </span>
      {/if}

      <!-- + 태그 popover -->
      <div class="relative" data-popover-root>
        <button
          type="button"
          onclick={() => { tagPopoverOpen = !tagPopoverOpen; formatPopoverOpen = false; }}
          class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-surface text-dim border border-default border-dashed hover:text-text hover:border-accent transition-colors"
          aria-haspopup="listbox"
          aria-expanded={tagPopoverOpen}
        >
          <Plus size={10} /> 태그
        </button>
        {#if tagPopoverOpen}
          <div
            class="absolute top-full left-0 mt-1 z-dropdown bg-surface border border-default rounded-md shadow-lg min-w-[180px] max-h-[280px] overflow-y-auto py-1"
            role="listbox"
            aria-label="태그 선택"
          >
            {#if topTags.length === 0}
              <p class="text-[10px] text-dim px-3 py-2">현재 결과에 태그 없음</p>
            {:else}
              {#each topTags as [tag, count]}
                <button
                  type="button"
                  role="option"
                  aria-selected={filterTag === tag}
                  onclick={() => addFilter('tag', tag)}
                  class="flex items-center justify-between w-full text-left text-[11px] px-3 py-1 text-text hover:bg-surface-hover"
                >
                  <span>#{tag}</span>
                  <span class="text-dim ml-2 tabular-nums">{count}</span>
                </button>
              {/each}
            {/if}
          </div>
        {/if}
      </div>

      <!-- + 형식 popover -->
      <div class="relative" data-popover-root>
        <button
          type="button"
          onclick={() => { formatPopoverOpen = !formatPopoverOpen; tagPopoverOpen = false; }}
          class="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 rounded bg-surface text-dim border border-default border-dashed hover:text-text hover:border-accent transition-colors"
          aria-haspopup="listbox"
          aria-expanded={formatPopoverOpen}
        >
          <Plus size={10} /> 형식
        </button>
        {#if formatPopoverOpen}
          <div
            class="absolute top-full left-0 mt-1 z-dropdown bg-surface border border-default rounded-md shadow-lg min-w-[140px] py-1"
            role="listbox"
            aria-label="형식 선택"
          >
            {#each FORMATS as fmt}
              <button
                type="button"
                role="option"
                aria-selected={filterFormat === fmt}
                onclick={() => addFilter('format', fmt)}
                class="flex items-center w-full text-left text-[11px] px-3 py-1 text-text hover:bg-surface-hover uppercase"
              >
                {fmt}
              </button>
            {/each}
          </div>
        {/if}
      </div>

      {#if hasActiveFilters}
        <button
          type="button"
          onclick={clearAllFilters}
          class="text-[10px] text-dim hover:text-text px-2 py-0.5 ml-auto"
        >
          초기화
        </button>
      {/if}
    </div>

    <!-- D.3: 선택 toolbar (selection.size > 0 시) -->
    {#if selectionCount > 0}
      <div
        class="sticky top-0 z-dropdown flex flex-wrap items-center gap-2 px-4 py-2 shrink-0 bg-accent/10 border-y border-accent/30 backdrop-blur-sm"
      >
        <span class="text-xs font-medium {selectionOverLimit ? 'text-error' : 'text-accent'}">
          {selectionCount}건 선택
          {#if selectionOverLimit}
            <span class="ml-1">(최대 {MAX_SELECTION}건 초과)</span>
          {/if}
        </span>
        <button
          type="button"
          onclick={selectAll}
          disabled={allVisibleSelected}
          class="text-[11px] text-dim hover:text-text disabled:opacity-40 disabled:cursor-not-allowed"
        >
          전체 선택
        </button>
        <button
          type="button"
          onclick={clearSelection}
          class="text-[11px] text-dim hover:text-text"
        >
          선택 해제
        </button>
        <div class="flex-1"></div>
        <Button
          variant="secondary"
          size="sm"
          icon={FolderTree}
          disabled={selectionOverLimit || bulkBusy}
          onclick={() => ui.openModal('bulk-domain')}
        >
          일괄 도메인
        </Button>
        <Button
          variant="secondary"
          size="sm"
          icon={Tag}
          disabled={selectionOverLimit || bulkBusy}
          onclick={() => ui.openModal('bulk-tag')}
        >
          일괄 태그
        </Button>
        <Button
          variant="danger"
          size="sm"
          icon={Trash2}
          disabled={selectionOverLimit || bulkBusy}
          onclick={() => ui.openModal('bulk-delete')}
        >
          일괄 삭제
        </Button>
      </div>
    {/if}

    <!-- 스크롤 영역 (목록) -->
    <div class="flex-1 overflow-y-auto overflow-x-hidden px-4">
    <!-- 결과 헤더 — 카운트만. 필터는 위의 칩 row가 표시. -->
    {#if !loading}
      <div class="flex items-center mb-2">
        <span class="text-[10px] text-dim">{total}건</span>
      </div>
    {/if}

    <!-- Phase D: 이드 답변 카드 (질문형 검색 시) -->
    {#if showAskCard}
      <div class="mb-3 min-h-[72px]">
        <AskAnswerCard
          data={askData}
          loading={askLoading}
          error={askError}
          onCitationClick={(docId) => goto(`/documents/${docId}`)}
          onDismiss={() => { askDismissed = true; }}
        />
      </div>
    {/if}

    <!-- 결과 -->
    {#if loading}
      <div class="space-y-1.5">
        {#each Array(5) as _}
          <div class="bg-surface rounded-lg p-3 border border-default animate-pulse h-14"></div>
        {/each}
      </div>
    {:else if items.length === 0}
      <div class="text-center py-12 text-dim">
        {#if searchQuery}
          <p class="text-sm mb-2">'{searchQuery}'에 대한 결과가 없습니다</p>
          <button onclick={clearAllFilters} class="text-xs text-accent hover:underline">필터 초기화</button>
        {:else if hasActiveFilters}
          <p class="text-sm mb-2">이 분류에 문서가 없습니다</p>
          <button onclick={clearAllFilters} class="text-xs text-accent hover:underline">필터 초기화</button>
        {:else}
          <p class="text-sm">등록된 문서가 없습니다</p>
        {/if}
      </div>
    {:else}
      {#if viewMode === 'table'}
        <DocumentTable
          {items}
          selectedId={selectedDoc?.id}
          onselect={selectDoc}
          selectable
          {selectedIds}
          onselectionchange={handleSelectionChange}
          {kbSelectedId}
          density={tableDensity}
        />
      {:else}
        <div class="space-y-1">
          {#each items as doc}
            <div data-kb-selected={doc.id === kbSelectedId}>
              <DocumentCard
                {doc}
                showDomain={!filterDomain}
                selected={selectedDoc?.id === doc.id}
                onselect={selectDoc}
                selectable
                {selectedIds}
                onselectionchange={handleSelectionChange}
              />
            </div>
          {/each}
        </div>
      {/if}

      {#if !searchResults && totalPages > 1}
        <div class="flex justify-center gap-1 mt-4">
          {#each Array(totalPages) as _, i}
            <button
              onclick={() => changePage(i + 1)}
              class="px-2.5 py-0.5 rounded text-xs transition-colors
                {currentPage === i + 1 ? 'bg-accent text-white' : 'bg-surface text-dim hover:text-text'}"
            >
              {i + 1}
            </button>
          {/each}
        </div>
      {/if}
    {/if}
    </div>
  </div>

  <!-- 하단: 뷰어 (70%, 전체 너비) -->
  {#if selectedDoc}
    <div class="flex-1 min-h-0">
      <DocumentViewer doc={selectedDoc} />
    </div>
  {/if}
  </div>
  <!-- /가운데 컬럼 -->

  <!-- 우측: xl+ persistent rail (hidden xl:flex) -->
  {#if selectedDoc}
    <aside
      class="hidden xl:flex shrink-0 border-l border-default {metaRailOpen ? 'w-rail' : 'w-10'}"
      aria-label="문서 정보 rail"
    >
      {#if metaRailOpen}
        <DocumentMetaRail
          doc={selectedDoc}
          onclose={() => setMetaRailOpen(false)}
          ondelete={handleDocDelete}
        />
      {:else}
        <button
          type="button"
          onclick={() => setMetaRailOpen(true)}
          class="w-10 h-full flex items-center justify-center text-dim hover:text-text hover:bg-surface-hover transition-colors"
          aria-label="정보 패널 펼치기"
          title="정보 패널 펼치기"
        >
          <ChevronLeft size={16} />
        </button>
      {/if}
    </aside>
  {/if}
</div>

<!-- < xl 폴백: Drawer primitive (우측 슬라이드) -->
<Drawer id="meta" side="right" width="rail" aria-label="문서 정보">
  {#if selectedDoc}
    <DocumentMetaRail
      doc={selectedDoc}
      onclose={() => ui.closeDrawer()}
      ondelete={handleDocDelete}
    />
  {/if}
</Drawer>

<!-- D.3: Bulk action modals -->
<Modal id="bulk-domain" title="일괄 도메인 변경" size="sm">
  <p class="text-xs text-dim mb-3">{selectionCount}건의 문서에 새 도메인을 지정합니다.</p>
  <Select
    bind:value={bulkDomainValue}
    options={DOMAIN_OPTIONS}
    placeholder="도메인 선택"
    label="도메인"
  />
  {#snippet footer()}
    <Button variant="ghost" size="sm" onclick={() => ui.closeModal('bulk-domain')}>취소</Button>
    <Button
      variant="primary"
      size="sm"
      disabled={!bulkDomainValue}
      loading={bulkBusy}
      onclick={bulkApplyDomain}
    >
      적용
    </Button>
  {/snippet}
</Modal>

<Modal id="bulk-tag" title="일괄 태그 추가" size="sm">
  <p class="text-xs text-dim mb-3">{selectionCount}건의 문서에 태그를 추가합니다. 이미 같은 태그가 있으면 건너뜁니다.</p>
  <TextInput
    bind:value={bulkTagValue}
    placeholder="태그 입력"
    label="태그"
  />
  {#snippet footer()}
    <Button variant="ghost" size="sm" onclick={() => ui.closeModal('bulk-tag')}>취소</Button>
    <Button
      variant="primary"
      size="sm"
      disabled={!bulkTagValue.trim()}
      loading={bulkBusy}
      onclick={bulkAddTag}
    >
      추가
    </Button>
  {/snippet}
</Modal>

<ConfirmDialog
  id="bulk-delete"
  title="선택한 문서 삭제"
  message="{selectionCount}건의 문서를 원본 파일과 함께 삭제합니다. 되돌릴 수 없습니다."
  confirmLabel="삭제"
  tone="danger"
  loading={bulkBusy}
  onconfirm={bulkDelete}
/>
