<!-- #region DatasetHub [C:5] [TYPE Page] [SEMANTICS sveltekit,dataset,workspace] -->
<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import { SvelteSet } from "svelte/reactivity";
  import { t } from "$lib/i18n/index.svelte.js";
  import { api } from "$lib/api.js";
  import HelpTooltip from "$lib/ui/HelpTooltip.svelte";
  import { debounce } from "$lib/utils/debounce.js";
  import {
    environmentContextStore,
    initializeEnvironmentContext,
    setSelectedEnvironment,
  } from "$lib/stores/environmentContext.svelte.js";
  import {
    openDrawerForTaskIfPreferred,
  } from "$lib/stores/taskDrawer.svelte.js";
  import StatsBar from "./StatsBar.svelte";
  import DatasetList from "./DatasetList.svelte";
  import DatasetPreview from "./DatasetPreview.svelte";

  let selectedEnv = $state(null);
  let datasets = $state([]);
  let dsError = $state(null), dsLoading = $state(true);
  let stats = $state(null);
  let activeFilter = $state(null);
  let page = $state(1), pageSize = $state(10), total = $state(0), totalPages = $state(1);
  let searchQuery = $state("");
  let selectedIds = $state(new SvelteSet());
  let selectedDatasetId = $state(null), detailData = $state(null), detailLoading = $state(false), detailError = $state(null);
  let showMapColumnsModal = $state(false), showGenerateDocsModal = $state(false);
  let mapSourceType = $state("sqllab"), mapDatabases = $state([]), mapDatabaseId = $state(null), mapSqlQuery = $state(""), mapFileData = $state(null);
  let llmProvider = $state(""), llmOptions = $state({});
  let isMobile = $state(false), mobileSlideOpen = $state(false);
  let ws = null;

  // Debounced search
  const debouncedSearch = debounce((q) => { searchQuery = q; page = 1; loadDatasets(); }, 300);

  async function loadDatasets(filter, p) {
    if (!selectedEnv) return;
    dsLoading = true;
    dsError = null;
    try {
      const resp = await api.getDatasets(selectedEnv, {
        search: searchQuery || undefined,
        filter: filter ?? activeFilter ?? undefined,
        page: p ?? page,
        page_size: pageSize,
      });
      datasets = resp.datasets.map(d => ({
        id: d.id,
        table_name: d.table_name,
        schema: d.schema,
        database: d.database,
        column_count: d.mapped_fields?.total ?? 0,
        metric_count: d.metric_count ?? 0,
        mappedFields: d.mapped_fields ? { total: d.mapped_fields.total, mapped: d.mapped_fields.mapped } : null,
        lastTask: d.last_task ? { status: d.last_task.status, id: d.last_task.task_id } : null,
      }));
      stats = resp.stats ?? null;
      total = resp.total ?? 0;
      totalPages = resp.total_pages ?? 1;
      page = resp.page ?? 1;
      // Clean selected IDs
      const keepIds = new Set();
      datasets.forEach(d => { if (selectedIds.has(d.id)) keepIds.add(d.id); });
      selectedIds = keepIds;
    } catch (e) {
      dsError = e.message || $t.datasets?.load_failed;
    } finally {
      dsLoading = false;
    }
  }

  async function loadDetail(datasetId) {
    if (!datasetId) return;
    detailLoading = true;
    detailError = null;
    try {
      const d = await api.getDatasetDetail(selectedEnv, datasetId);
      detailData = { ...d, schema: d.schema_name ?? d.schema };
    } catch (e) {
      detailError = e.message || $t.datasets?.load_detail_failed;
    } finally {
      detailLoading = false;
    }
  }

  function selectDataset(id) {
    selectedDatasetId = id;
    detailData = null;
    detailError = null;
    if (isMobile) mobileSlideOpen = true;
    loadDetail(id);
  }

  function closeDetail() {
    selectedDatasetId = null;
    detailData = null;
    detailError = null;
    mobileSlideOpen = false;
  }

  function handleFilter(filterKey) {
    activeFilter = filterKey;
    page = 1;
    loadDatasets(filterKey, 1);
  }

  function handlePageChange(p) {
    page = p;
    loadDatasets(undefined, p);
  }

  function handleAction(dataset, action) {
    if (action === "map_columns") {
      selectedIds = new SvelteSet([dataset.id]);
      showMapColumnsModal = true;
      mapSourceType = "sqllab";
      mapDatabaseId = null;
      mapSqlQuery = "";
      mapFileData = null;
      // Fetch databases on open
      if (selectedEnv) {
        api.getEnvironmentDatabases(selectedEnv).then(dbs => mapDatabases = dbs ?? []).catch(() => {});
      }
    } else if (action === "generate_docs") {
      selectedIds = new SvelteSet([dataset.id]);
      showGenerateDocsModal = true;
      llmProvider = "";
      llmOptions = {};
    }
  }

  function handleCheckbox(dataset, checked) {
    if (checked === "all") {
      datasets.forEach(d => selectedIds.add(d.id));
      selectedIds = new Set(selectedIds);
    } else if (checked === "clear-all") {
      selectedIds = new SvelteSet();
    } else if (checked) {
      selectedIds.add(dataset.id);
      selectedIds = new Set(selectedIds);
    } else {
      selectedIds.delete(dataset.id);
      selectedIds = new Set(selectedIds);
    }
  }

  // Bulk actions
  async function handleBulkMapColumns() {
    if (selectedIds.size === 0) return;
    if (mapSourceType === "sqllab" && !mapDatabaseId) return;
    if (mapSourceType === "xlsx" && (!mapFileData || mapFileData.length === 0)) return;
    try {
      let fileData = null;
      if (mapSourceType === "xlsx" && mapFileData?.length) fileData = mapFileData[0].name;
      const resp = await api.postApi("/datasets/map-columns", {
        env_id: selectedEnv, dataset_ids: [...selectedIds],
        source_type: mapSourceType, database_id: mapDatabaseId || undefined, sql_query: mapSqlQuery || undefined, file_data: fileData || undefined,
      });
      showMapColumnsModal = false;
      selectedIds = new SvelteSet();
      if (resp.task_id) openDrawerForTaskIfPreferred(resp.task_id);
    } catch (e) {
      alert($t.datasets?.mapping_task_failed);
    }
  }

  async function handleBulkGenerateDocs() {
    if (selectedIds.size === 0) return;
    if (!llmProvider) { alert($t.datasets?.select_llm_provider); return; }
    try {
      const resp = await api.postApi("/datasets/generate-docs", {
        env_id: selectedEnv, dataset_ids: [...selectedIds], llm_provider: llmProvider, options: llmOptions,
      });
      showGenerateDocsModal = false;
      selectedIds = new SvelteSet();
      if (resp.task_id) openDrawerForTaskIfPreferred(resp.task_id);
    } catch (e) {
      alert($t.datasets?.docs_task_failed);
    }
  }

  // WebSocket
  function connectWs() {
    if (!selectedEnv) return;
    const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
    const baseUrl = `${proto}//${window.location.host}`;
    let wsUrl = `${baseUrl}/ws/datasets/${selectedEnv}`;
    const token = localStorage.getItem('auth_token');
    if (token) {
      wsUrl += `?token=${encodeURIComponent(token)}`;
    }
    ws = new WebSocket(wsUrl);
    ws.onmessage = (e) => {
      try {
        const msg = JSON.parse(e.data);
        if (msg.type === "dataset.updated") {
          loadDatasets();
          // Refresh detail too if it's one of the updated datasets
          if (selectedDatasetId && msg.payload?.dataset_ids?.includes(selectedDatasetId)) {
            loadDetail(selectedDatasetId);
          }
        }
      } catch {}
    };
    ws.onerror = () => {};
  }

  function disconnectWs() {
    if (ws) { ws.close(); ws = null; }
  }

  // Mobile check
  function checkMobile() { isMobile = window.innerWidth < 768; }
  let resizeHandler = null;

  onMount(async () => {
    await initializeEnvironmentContext();
    selectedEnv = environmentContextStore.value?.selectedEnvId;
    if (selectedEnv) {
      setSelectedEnvironment(selectedEnv);
      await loadDatasets();
      connectWs();
    }
    checkMobile();
    resizeHandler = () => { checkMobile(); };
    window.addEventListener("resize", resizeHandler);
    document.addEventListener("keydown", handleEscape);
  });

  onDestroy(() => {
    disconnectWs();
    if (resizeHandler) window.removeEventListener("resize", resizeHandler);
    document.removeEventListener("keydown", handleEscape);
  });

  function handleEscape(e) { if (e.key === "Escape" && mobileSlideOpen) closeDetail(); }

  // Environment change
  $effect(() => {
    const next = environmentContextStore.value?.selectedEnvId;
    if (next && next !== selectedEnv) {
      selectedEnv = next;
      setSelectedEnvironment(next);
      page = 1;
      selectedIds = new SvelteSet();
      closeDetail();
      disconnectWs();
      loadDatasets();
      connectWs();
    }
  });
</script>

<div class="max-w-screen-2xl mx-auto px-4 pt-4 pb-6">
  <!-- Header -->
  <div class="flex items-center justify-between mb-4">
    <h1 class="text-2xl font-bold text-gray-900">{$t.nav?.datasets}</h1>
    <div class="flex items-center gap-3">
      <span class="px-2 py-1 text-xs rounded border border-gray-300 bg-gray-50 text-gray-600">
        {$t.datasets?.all}: {stats?.total ?? 0}
      </span>
      <button class="px-3 py-1 text-sm bg-primary text-white rounded hover:bg-primary-hover" title={$t.datasets?.refresh_tip} onclick={() => loadDatasets()}>
        {$t.common?.refresh ?? '⟳'}
      </button>
    </div>
  </div>

  <!-- Error Banner for list -->
  {#if dsError}
    <div class="bg-red-50 border border-red-300 text-red-700 px-3 py-2 rounded mb-4 text-sm flex items-center justify-between" role="alert">
      <span>{dsError}</span>
      <button class="px-2 py-0.5 bg-red-600 text-white rounded text-xs" onclick={() => loadDatasets()}>{$t.common?.retry ?? 'Retry'}</button>
    </div>
  {/if}

  <!-- Stats Bar -->
  <StatsBar {stats} activeFilter={activeFilter} isLoading={dsLoading} onfilter={handleFilter} />

  <!-- Split View -->
  <div class="grid gap-4 {isMobile ? 'block' : 'grid-cols-[40%_60%]'}" style="min-height: 60vh">
    <!-- Left Panel: Dataset List -->
    <div class={isMobile && mobileSlideOpen ? 'hidden' : ''}>
      <DatasetList
        {datasets}
        {selectedIds}
        isLoading={dsLoading}
        error={null}
        {total}
        {page}
        {totalPages}
        {pageSize}
        {searchQuery}
        onselect={selectDataset}
        onaction={handleAction}
        onpagechange={handlePageChange}
        onsearch={debouncedSearch}
        oncheckbox={handleCheckbox}
      />
    </div>

    <!-- Right Panel (Desktop inline / Mobile slide-over) -->
    {#if isMobile && mobileSlideOpen}
      <!-- Backdrop -->
      <button
        class="fixed inset-0 bg-black/30 z-40"
        onclick={closeDetail}
        aria-label="Close detail"
      ></button>
      <!-- Slide-over -->
      <div
        class="fixed inset-y-0 right-0 w-[90vw] max-w-lg bg-white shadow-xl z-50 overflow-y-auto"
        role="dialog"
        aria-modal="true"
      >
        <div class="sticky top-0 bg-white border-b border-gray-200 px-4 py-2 flex items-center justify-between z-10">
          <span class="font-medium text-sm">{detailData?.table_name ?? $t.datasets?.no_selection}</span>
          <button class="text-gray-400 hover:text-gray-700 text-lg" onclick={closeDetail} aria-label="Close">&times;</button>
        </div>
        <DatasetPreview dataset={detailData} isLoading={detailLoading} error={detailError} onretry={() => detailData && loadDetail(detailData.id)} envId={selectedEnv} />
      </div>
    {:else if !isMobile}
      <div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
        <DatasetPreview dataset={detailData} isLoading={detailLoading} error={detailError} onretry={() => selectedDatasetId && loadDetail(selectedDatasetId)} envId={selectedEnv} />
      </div>
    {/if}
  </div>

  <!-- Bulk Action Panel (bottom) -->
  {#if selectedIds.size >= 2}
    <div class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 shadow-lg p-3 z-50">
      <div class="max-w-screen-2xl mx-auto flex items-center justify-between">
        <span class="font-medium text-sm">✓ {selectedIds.size} {$t.datasets?.selected}</span>
        <div class="flex gap-2">
          <button class="px-3 py-1.5 text-sm bg-primary text-white rounded hover:bg-primary-hover" title={$t.datasets?.bulk_map_tip} onclick={() => { showMapColumnsModal = true; mapSourceType = "sqllab"; mapDatabaseId = null; mapSqlQuery = ""; mapFileData = null; if (selectedEnv) api.getEnvironmentDatabases(selectedEnv).then(dbs => mapDatabases = dbs ?? []).catch(() => {}); }}>
            🗺 {$t.datasets?.action_map_columns}
          </button>
          <button class="px-3 py-1.5 text-sm bg-primary text-white rounded hover:bg-primary-hover" title={$t.datasets?.bulk_docs_tip} onclick={() => { showGenerateDocsModal = true; }}>
            📄 {$t.datasets?.generate_docs}
          </button>
          <button class="px-3 py-1.5 text-sm border border-gray-300 rounded hover:bg-gray-100" title={$t.datasets?.clear_selection_tip} onclick={() => selectedIds = new SvelteSet()}>
            ✕ {$t.common?.cancel ?? 'Clear'}
          </button>
        </div>
      </div>
    </div>
  {/if}

  <!-- Map Columns Modal -->
  {#if showMapColumnsModal}
    <div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" role="dialog" aria-modal="true" onclick={(e) => { if (e.target === e.currentTarget) showMapColumnsModal = false; }}>
      <div class="bg-white rounded-lg shadow-xl p-6 max-w-md w-full" style="max-height: 90vh; overflow-y: auto;" onclick={(e) => e.stopPropagation()}>
        <h3 class="text-lg font-semibold mb-4">{$t.datasets?.bulk_map_columns}</h3>
        <p class="text-sm text-gray-600 mb-3">{$t.datasets?.selected_count?.replace('{count}', String(selectedIds.size))}</p>

        <span class="flex items-center gap-1.5 mb-1.5">
          <span class="text-sm font-medium text-gray-700">{$t.datasets?.source_type ?? 'Тип источника'}</span>
          <HelpTooltip text={$t.datasets?.source_type_tip || ""} />
        </span>
        <select
          class="w-full border rounded px-2 py-1.5 mb-3"
          title={$t.datasets?.source_type_tip}
          bind:value={mapSourceType}
          onchange={() => { if (mapSourceType === 'sqllab' && mapDatabases.length === 0 && selectedEnv) { api.getEnvironmentDatabases(selectedEnv).then(dbs => mapDatabases = dbs ?? []).catch(() => {}); } }}
        >
          <option value="sqllab">{$t.datasets?.source_sqllab ?? 'Superset SQL Lab'}</option>
          <option value="xlsx">{$t.datasets?.source_xlsx}</option>
        </select>
        {#if mapSourceType === "sqllab"}
          <span class="flex items-center gap-1.5 mb-1.5">
            <label class="text-sm font-medium text-gray-700" for="map-database-select">{$t.datasets?.source_database ?? 'База данных'}</label>
            <HelpTooltip text={$t.datasets?.database_tip || ""} />
          </span>
          <select
            id="map-database-select"
            class="w-full border rounded px-2 py-1.5 mb-3"
            title={$t.datasets?.database_tip}
            bind:value={mapDatabaseId}
          >
            <option value="">-- {$t.datasets?.select_database ?? 'выберите базу данных'} --</option>
            {#each mapDatabases as db (db.id)}
              <option value={db.id}>{db.database_name || db.name}</option>
            {/each}
          </select>
          <span class="flex items-center gap-1.5 mb-1.5">
            <label class="text-sm font-medium text-gray-700" for="map-sql-query">
              {$t.datasets?.sql_query ?? 'SQL-запрос'}
              <span class="text-xs text-gray-400 font-normal ml-1">({$t.datasets?.sql_query_hint ?? 'оставьте пустым для information_schema'})</span>
            </label>
            <HelpTooltip text={$t.datasets?.sql_query_tip || ""} />
          </span>
          <!-- SQL template chips -->
          <div class="flex flex-wrap gap-1.5 mb-2">
            <span class="text-xs text-gray-400 mr-0.5 self-center">{$t.datasets?.sql_templates ?? 'Шаблоны'}:</span>
            <button
              type="button"
              class="px-2 py-0.5 text-xs border border-gray-300 rounded-full text-gray-500 hover:bg-blue-50 hover:border-blue-300 hover:text-blue-600 transition-colors"
              title={$t.datasets?.sql_template_info_schema_tip}
              onclick={() => { mapSqlQuery = "SELECT c.column_name, c.data_type, pgd.description\nFROM information_schema.columns c\nLEFT JOIN pg_catalog.pg_description pgd ON pgd.objsubid = c.ordinal_position\n  AND pgd.objoid = (SELECT attrelid FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = c.table_name) AND attname = c.column_name)\nWHERE c.table_schema = 'public'\n  AND c.table_name = 'your_table_name'\nORDER BY c.ordinal_position;" }}
            >information_schema</button>
            <button
              type="button"
              class="px-2 py-0.5 text-xs border border-gray-300 rounded-full text-gray-500 hover:bg-blue-50 hover:border-blue-300 hover:text-blue-600 transition-colors"
              title={$t.datasets?.sql_template_custom_tip}
              onclick={() => { mapSqlQuery = "SELECT column_name, description AS verbose_name\nFROM my_column_mappings\nWHERE table_name = 'your_table_name'\nORDER BY column_name;" }}
            >{$t.datasets?.sql_template_custom ?? 'Кастомная таблица'}</button>
            <button
              type="button"
              class="px-2 py-0.5 text-xs border border-gray-300 rounded-full text-gray-400 hover:border-red-300 hover:text-red-500 transition-colors"
              onclick={() => { mapSqlQuery = ""; }}
            >{$t.datasets?.sql_template_clear ?? 'Очистить'}</button>
          </div>
          <textarea
            id="map-sql-query"
            class="w-full border rounded px-2 py-1.5 mb-4 font-mono text-xs"
            rows="4"
            title={$t.datasets?.sql_query_tip}
            placeholder="SELECT column_name, description AS verbose_name FROM my_mappings WHERE table_name = 'my_table';"
            bind:value={mapSqlQuery}
          ></textarea>
        {:else}
          <span class="flex items-center gap-1.5 mb-1.5">
            <label class="text-sm font-medium text-gray-700" for="map-file-input">{$t.datasets?.xlsx_file ?? 'XLSX-файл'}</label>
            <HelpTooltip text={$t.datasets?.file_input_tip || ""} />
          </span>
          <input
            id="map-file-input"
            type="file"
            accept=".xlsx"
            class="w-full mb-4"
            title={$t.datasets?.file_input_tip}
            onchange={(e) => mapFileData = e.target.files}
          />
        {/if}
        <div class="flex justify-end gap-2">
          <button class="px-3 py-1.5 border rounded hover:bg-gray-100" title={$t.datasets?.cancel_tip} onclick={() => showMapColumnsModal = false}>{$t.common?.cancel}</button>
          <button class="px-3 py-1.5 bg-primary text-white rounded hover:bg-primary-hover" title={$t.datasets?.start_mapping_tip} onclick={handleBulkMapColumns}>{$t.datasets?.start_mapping}</button>
        </div>
      </div>
    </div>
  {/if}

  <!-- Generate Docs Modal -->
  {#if showGenerateDocsModal}
    <div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" role="dialog" aria-modal="true" onclick={(e) => { if (e.target === e.currentTarget) showGenerateDocsModal = false; }}>
      <div class="bg-white rounded-lg shadow-xl p-6 max-w-md w-full" onclick={(e) => e.stopPropagation()}>
        <h3 class="text-lg font-semibold mb-4">{$t.datasets?.bulk_docs_generation}</h3>
        <p class="text-sm text-gray-600 mb-3">{$t.datasets?.selected_count?.replace('{count}', String(selectedIds.size))}</p>
        <span class="flex items-center gap-1.5 mb-1.5">
          <span class="text-sm font-medium text-gray-700">{$t.datasets?.select_llm_provider ?? 'LLM-провайдер'}</span>
          <HelpTooltip text={$t.datasets?.llm_provider_tip || ""} />
        </span>
        <select class="w-full border rounded px-2 py-1.5 mb-4" title={$t.datasets?.llm_provider_tip} bind:value={llmProvider}>
          <option value="">{$t.datasets?.select_llm_provider_option}</option>
          <option value="openai">OpenAI</option>
          <option value="anthropic">Anthropic</option>
          <option value="local">Local LLM</option>
        </select>
        <div class="flex justify-end gap-2">
          <button class="px-3 py-1.5 border rounded hover:bg-gray-100" title={$t.datasets?.cancel_tip} onclick={() => showGenerateDocsModal = false}>{$t.common?.cancel}</button>
          <button class="px-3 py-1.5 bg-primary text-white rounded hover:bg-primary-hover" title={$t.datasets?.start_docs_tip} onclick={handleBulkGenerateDocs}>{$t.datasets?.generate_docs}</button>
        </div>
      </div>
    </div>
  {/if}
</div>
<!-- #endregion DatasetHub -->