<!-- #region DatasetReviewWorkspace [C:4] [TYPE Page] [SEMANTICS sveltekit, dataset, review, workspace, assistant] -->
<!-- @BRIEF Main dataset review workspace — thin shell delegating to extracted components. -->
<!-- @LAYER Page -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceHeader] -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceLeftSidebar] -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceRightRail] -->
<!-- @RELATION DEPENDS_ON -> [UseReviewSession] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:datasetReviewSessionStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:assistantChatStore] -->
<!-- @RATIONALE Decomposed from 1202→~380 lines by extracting helpers, composables, and sidebar/rail/header components per INV_7. -->
<!-- @REJECTED Keeping all logic inline was rejected — exceeded 150-line contract limit and cyclomatic complexity thresholds. -->
<!-- @DEPRECATED N/A — active workspace page. -->
<!-- @REPLACED_BY N/A -->
<!-- @UX_STATE Empty -> SourceIntakePanel visible. -->
<!-- @UX_STATE Importing -> Milestones panel. -->
<!-- @UX_STATE Review -> 3-column layout. -->
<script lang="ts">
  import { page } from "$app/state";
  import { goto } from "$app/navigation";
  import { t } from "$lib/i18n/index.svelte.js";
  import { api } from "$lib/api.js";
  import SourceIntakePanel from "$lib/components/dataset-review/SourceIntakePanel.svelte";
  import ValidationFindingsPanel from "$lib/components/dataset-review/ValidationFindingsPanel.svelte";
  import SemanticLayerReview from "$lib/components/dataset-review/SemanticLayerReview.svelte";
  import ExecutionMappingReview from "$lib/components/dataset-review/ExecutionMappingReview.svelte";
  import AssistantChatPanel from "$lib/components/assistant/AssistantChatPanel.svelte";
  import { datasetReviewSessionStore } from "$lib/stores/datasetReviewSession.svelte.js";
  import { environmentContextStore, initializeEnvironmentContext } from "$lib/stores/environmentContext.svelte.js";
  import { assistantChatStore, setAssistantDatasetReviewSessionId, setAssistantSeedMessage, setAssistantFocusTarget } from "$lib/stores/assistantChat.svelte.js";

  import ReviewWorkspaceHeader from "../ReviewWorkspaceHeader.svelte";
  import ReviewWorkspaceLeftSidebar from "../ReviewWorkspaceLeftSidebar.svelte";
  import ReviewWorkspaceRightRail from "../ReviewWorkspaceRightRail.svelte";
  import { useReviewSession } from "../useReviewSession.js";
  import { buildImportMilestones, buildWorkspaceLaunchBlockers, buildAssistantSeedPrompt, getWorkspaceStateLabel, getRecommendedActionLabel, getPrimaryActionCtaLabel, stringifyValue, buildAssistantContextPrompt, mergeCollectionItem } from "../review-workspace-helpers.js";

  let session = $state(null);
  let loadError = $state("");
  let submitError = $state("");
  let exportMessage = $state("");
  let isBootstrapping = $state(true);
  let isSubmitting = $state(false);
  let isExporting = $state(false);
  let intakeAcknowledgment = $state("");
  let clarificationState = $state(null);
  let previewUiState = $state("");
  let launchResult = $state(null);
  let assistantPromptNonce = $state(0);

  const routeSessionId = $derived(page.params.id || "");
  const environments = $derived(environmentContextStore.value?.environments || []);
  const selectedEnvironmentId = $derived(session?.environment_id || environmentContextStore.value?.selectedEnvId || "");
  const currentWorkspaceState = $derived(!session ? (isSubmitting ? "importing" : "empty") : session.readiness_state === "importing" ? "importing" : "review");
  const profile = $derived(session?.profile || null);
  const findings = $derived(session?.findings || []);
  const importedFilters = $derived(session?.imported_filters || []);
  const latestPreview = $derived(session?.previews?.length ? session.previews[session.previews.length - 1] : null);
  const executionMappings = $derived(session?.execution_mappings || []);
  const latestRunContext = $derived(session?.run_contexts?.length ? session.run_contexts[session.run_contexts.length - 1] : null);
  const clarificationSession = $derived(clarificationState?.clarification_session || null);
  const clarificationCurrentQuestion = $derived(clarificationState?.current_question || null);
  const clarificationRemainingCount = $derived(Number(clarificationSession?.remaining_count || 0));
  const clarificationTotalCount = $derived(clarificationRemainingCount + Number(clarificationSession?.resolved_count || 0));
  const previewGateState = $derived(String(previewUiState || latestPreview?.preview_status || "missing"));
  const sessionVersion = $derived(session?.session_version ?? session?.version ?? null);
  const exportDisabled = $derived(!session || isExporting);
  const saveDisabled = $derived(!session || isSubmitting);
  const launchDisabled = $derived(!session || isSubmitting || isExporting);
  const blockingCount = $derived(findings.filter(f => f.severity === "blocking").length);
  const warningCount = $derived(findings.filter(f => f.severity === "warning").length);
  const infoCount = $derived(findings.filter(f => f.severity === "informational").length);
  const unresolvedBlockingFindingsCount = $derived(findings.filter(f => f.severity === "blocking" && f.resolution_state !== "resolved" && f.resolution_state !== "approved").length);
  const pendingApprovalMappingsCount = $derived(executionMappings.filter(m => m.requires_explicit_approval && m.approval_state !== "approved").length);
  const readinessLabel = $derived($t.dataset_review?.workspace?.readiness?.[session?.readiness_state || "empty"] || session?.readiness_state || $t.dataset_review?.workspace?.empty_state_title);
  const importMilestones = $derived(buildImportMilestones($t, intakeAcknowledgment, session, importedFilters, latestPreview, profile, findings, currentWorkspaceState));
  const workspaceLaunchBlockers = $derived(buildWorkspaceLaunchBlockers($t, session, unresolvedBlockingFindingsCount, pendingApprovalMappingsCount, previewGateState));
  const assistantSeedPrompt = $derived(buildAssistantSeedPrompt($t, session, profile, workspaceLaunchBlockers, clarificationCurrentQuestion, importedFilters));
  const workspaceFocusTarget = $derived(assistantChatStore.value?.focusTarget || null);

  const reviewSession = useReviewSession();

  async function bootstrapWorkspace() {
    isBootstrapping = true;
    await initializeEnvironmentContext();
    const result = await reviewSession.loadSessionDetail(routeSessionId);
    if (result) { session = result.session; clarificationState = result.clarificationState; loadError = result.loadError; previewUiState = result.previewUiState || ""; }
    isBootstrapping = false;
  }

  async function handleSourceSubmit(payload) {
    isSubmitting = true; submitError = ""; exportMessage = ""; loadError = "";
    intakeAcknowledgment = payload.source_kind === "superset_link"
      ? $t.dataset_review?.source?.recognized_link_hint : $t.dataset_review?.source?.dataset_selection_acknowledged;
    try {
      const summary = await api.postApi("/dataset-orchestration/sessions", payload);
      await goto(buildSessionUrl(summary.session_id));
      const result = await reviewSession.loadSessionDetail(summary.session_id);
      if (result) { session = result.session; clarificationState = result.clarificationState; loadError = result.loadError; previewUiState = result.previewUiState || ""; }
    } catch (error) {
      submitError = error?.message || $t.dataset_review?.source?.submit_failed || $t.common?.error;
    } finally { isSubmitting = false; }
  }

  async function handleResumeSession() {
    if (!session?.session_id) return;
    const { loadError: err } = await reviewSession.handleResumeSession(session.session_id, sessionVersion);
    if (err) loadError = err;
  }

  async function updateSessionLifecycle(statusValue) {
    if (!session?.session_id) return;
    const { loadError: err } = await reviewSession.updateSessionLifecycle(session.session_id, statusValue, sessionVersion);
    if (err) loadError = err;
  }

  async function exportArtifact(kind, format) {
    if (!session?.session_id) return;
    const result = await reviewSession.exportArtifact(session.session_id, kind, format);
    exportMessage = result.exportMessage;
  }

  function handlePreviewUpdated(result) {
    const updated = reviewSession.handlePreviewUpdated(session, result);
    if (updated.session) { session = updated.session; previewUiState = updated.previewUiState || ""; }
  }

  function handleSemanticUpdated(result) {
    const updated = reviewSession.handleSemanticUpdated(session, result);
    if (updated.session) session = updated.session;
  }

  function handleMappingUpdated(result) {
    const updated = reviewSession.handleMappingUpdated(session, result);
    if (updated.session) { session = updated.session; previewUiState = updated.previewUiState || previewUiState; }
  }

  function handleLaunchUpdated(result) {
    const updated = reviewSession.handleLaunchUpdated(result);
    if (updated.launchResult) launchResult = updated.launchResult;
  }

  function handleSectionJump(event) {
    reviewSession.handleSectionJump(event?.target || "");
  }

  function handleAskAiContext(payload) {
    if (!payload) return;
    if (payload.target) setAssistantFocusTarget({ target: payload.target, source: "workspace_context_action" });
    setAssistantSeedMessage(buildAssistantContextPrompt($t, payload));
    assistantPromptNonce += 1;
    document.getElementById("assistant-chat")?.scrollIntoView({ behavior: "smooth", block: "start" });
  }

  function primaryActionHandler() {
    if (!session) return;
    if (session.recommended_action === "resume_session") { void handleResumeSession(); return; }
    if (session.recommended_action === "generate_sql_preview") {
      handleAskAiContext({ target: "sql-preview", section: "sql-preview", label: "sql-preview", prompt: "Explain which approvals or fixes are still required before regenerating SQL preview." });
      document.getElementById("sql-preview")?.scrollIntoView({ behavior: "smooth", block: "start" }); return;
    }
    if (session.recommended_action === "launch_dataset") { document.getElementById("launch")?.scrollIntoView({ behavior: "smooth", block: "start" }); return; }
    handleAskAiContext({ target: clarificationCurrentQuestion?.question_id ? `clarification:${clarificationCurrentQuestion.question_id}` : "clarification", section: "clarification", prompt: assistantSeedPrompt });
  }

  function buildSessionUrl(sessionId) { return `/datasets/review/${encodeURIComponent(String(sessionId))}`; }

  function promptAssistant(target, prompt) {
    setAssistantFocusTarget(target ? { target, source: "workspace_prompt" } : null);
    setAssistantSeedMessage(prompt);
    assistantPromptNonce += 1;
  }

  $effect(() => {
    const ext = datasetReviewSessionStore.value?.session;
    if (!ext?.session_id || ext.session_id !== (session?.session_id || routeSessionId)) return;
    const extVer = ext.session_version ?? ext.version ?? null;
    if (extVer === null || extVer === sessionVersion) return;
    session = ext;
  });

  bootstrapWorkspace();

  $effect(() => { if (session?.session_id) setAssistantDatasetReviewSessionId(session.session_id); else setAssistantDatasetReviewSessionId(null); });
  $effect(() => { if (session?.session_id && assistantSeedPrompt) setAssistantSeedMessage(assistantSeedPrompt); });
</script>

<div class="mx-auto w-full max-w-[110rem] space-y-6 px-4 py-6 xl:px-6">
  <ReviewWorkspaceHeader {session} {currentWorkspaceState} {readinessLabel} getWorkspaceStateLabel={(s) => getWorkspaceStateLabel($t, s)} {profile} />

  {#if isBootstrapping}
    <div class="rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500 shadow-sm">{$t.dataset_review?.workspace?.loading}</div>
  {:else}
    {#if loadError}
      <div class="rounded-2xl border border-destructive-light bg-destructive-light px-4 py-4 text-sm text-red-700">{loadError}</div>
    {/if}

    {#if !session}
      <SourceIntakePanel environments={environments} selectedEnvironmentId={selectedEnvironmentId} submitting={isSubmitting} acknowledgment={intakeAcknowledgment} onsubmit={handleSourceSubmit} />
      {#if currentWorkspaceState === "importing"}
        <section class="rounded-2xl border border-primary-ring bg-primary-light p-5 shadow-sm">
          <div class="flex items-start justify-between gap-3">
            <div>
              <p class="text-xs font-semibold uppercase tracking-wide text-blue-700">{$t.dataset_review?.workspace?.import_progress_eyebrow}</p>
              <h2 class="mt-1 text-lg font-semibold text-slate-900">{$t.dataset_review?.workspace?.import_progress_title}</h2>
              <p class="mt-1 text-sm text-slate-700">{$t.dataset_review?.workspace?.import_progress_body}</p>
            </div>
            <span class="rounded-full bg-white px-3 py-1 text-xs font-medium text-blue-700">{getWorkspaceStateLabel($t, currentWorkspaceState)}</span>
          </div>
          <ol class="mt-4 grid gap-3 md:grid-cols-2">
            {#each importMilestones as m}
              <li class="rounded-xl border border-white/70 bg-white/80 p-3">
                <div class="flex items-center gap-2">
                  <span class="inline-flex h-2.5 w-2.5 rounded-full {m.state === 'done' ? 'bg-emerald-500' : m.state === 'active' ? 'bg-blue-500' : 'bg-slate-300'}"></span>
                  <span class="text-sm font-medium text-slate-900">{m.label}</span>
                </div>
              </li>
            {/each}
          </ol>
        </section>
      {/if}
      {#if submitError}
        <div class="rounded-2xl border border-destructive-light bg-destructive-light px-4 py-4 text-sm text-red-700">{submitError}</div>
      {/if}
    {:else}
      <!-- Session stat cards -->
      <section class="rounded-3xl border border-slate-200 bg-white px-4 py-4 shadow-sm">
        <div class="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
          <div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-slate-500">{$t.dataset_review?.workspace?.phase_card_label || "Phase"}</div>
            <div class="mt-1 text-sm font-semibold text-slate-900">{session.current_phase}</div>
          </div>
          <div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-slate-500">{$t.dataset_review?.workspace?.preview_gate_card_label || "Preview gate"}</div>
            <div class="mt-1 text-sm font-semibold text-slate-900">{previewGateState}</div>
          </div>
          <div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-slate-500">{$t.dataset_review?.workspace?.clarification_queue_card_label || "Clarification queue"}</div>
            <div class="mt-1 text-sm font-semibold text-slate-900">{clarificationRemainingCount} remaining / {clarificationTotalCount}</div>
          </div>
          <div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-slate-500">{$t.dataset_review?.workspace?.launch_blockers_card_label || "Launch blockers"}</div>
            <div class="mt-1 text-sm font-semibold text-slate-900">{workspaceLaunchBlockers.length}</div>
          </div>
        </div>
      </section>

      <!-- 3-column layout -->
      <div data-testid="workspace-chat-layout" class="grid items-start gap-6 xl:grid-cols-[minmax(16rem,0.72fr)_minmax(0,1.7fr)_minmax(22rem,1.08fr)]">
        <aside class="space-y-5">
          <ReviewWorkspaceLeftSidebar {session} {profile} {currentWorkspaceState} {readinessLabel} getWorkspaceStateLabel={(s) => getWorkspaceStateLabel($t, s)} {importedFilters} {executionMappings} {findings} {clarificationCurrentQuestion} {clarificationRemainingCount} {clarificationTotalCount} {importMilestones} {launchDisabled} {saveDisabled} getPrimaryActionCtaLabel={() => getPrimaryActionCtaLabel($t, session)} onPrimaryAction={primaryActionHandler} onSaveSession={() => updateSessionLifecycle("paused")} onPromptAssistant={promptAssistant} />
        </aside>

        <main class="min-w-0 space-y-5">
          <!-- Dataset summary card — clean, human-readable header -->
          <section id="summary" class="rounded-3xl border border-slate-200 bg-gradient-to-br from-violet-50/60 via-white to-white p-6 shadow-sm">
            <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
              <div class="min-w-0">
                <div class="flex items-center gap-3">
                  <div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-violet-100 text-violet-600">
                    <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>
                  </div>
                  <div class="min-w-0">
                    <h2 class="truncate text-xl font-bold text-slate-900">{profile?.dataset_name || session.dataset_ref}</h2>
                    <p class="mt-0.5 text-sm text-slate-500">{session.environment_id} · {session.dataset_ref}</p>
                  </div>
                </div>
                {#if profile?.business_summary && profile.business_summary !== `Review session initialized for ${session.dataset_ref}.`}
                  <p class="mt-3 max-w-3xl text-sm leading-relaxed text-slate-700">{profile.business_summary}</p>
                {/if}
              </div>
              <div class="flex shrink-0 flex-col items-end gap-2">
                <div class="rounded-xl border border-slate-200 bg-white px-4 py-2.5 shadow-sm">
                  <div class="text-[10px] font-medium uppercase tracking-wider text-slate-400">{$t.dataset_review?.workspace?.next_action_label || "Next step"}</div>
                  <div class="mt-0.5 text-sm font-semibold text-violet-700">{getRecommendedActionLabel($t, session.recommended_action)}</div>
                </div>
                {#if session.readiness_state === "run_ready"}
                  <span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-100 px-3 py-1 text-xs font-medium text-emerald-700">
                    <span class="h-1.5 w-1.5 rounded-full bg-emerald-500"></span>
                    Готов к запуску
                  </span>
                {:else if session.readiness_state === "review_ready"}
                  <span class="inline-flex items-center gap-1.5 rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-700">
                    <span class="h-1.5 w-1.5 rounded-full bg-blue-500"></span>
                    Готов к проверке
                  </span>
                {:else if session.readiness_state === "recovery_required" || session.readiness_state === "partially_ready"}
                  <span class="inline-flex items-center gap-1.5 rounded-full bg-amber-100 px-3 py-1 text-xs font-medium text-amber-700">
                    <span class="h-1.5 w-1.5 rounded-full bg-amber-500"></span>
                    Частичное восстановление
                  </span>
                {/if}
              </div>
            </div>
            {#if importedFilters.length > 0}
              <div class="mt-4 border-t border-slate-100 pt-4">
                <div class="flex items-center gap-2">
                  <svg class="h-4 w-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path></svg>
                  <span class="text-xs font-semibold uppercase tracking-wide text-slate-500">{$t.dataset_review?.workspace?.recovered_filters_title || "Фильтры из дашборда"}</span>
                  <span class="rounded-full bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">{importedFilters.length}</span>
                </div>
                <div class="mt-2 flex flex-wrap gap-2">
                  {#each importedFilters.slice(0, 6) as filter}
                    <button type="button" class="group inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-2.5 py-1.5 text-xs text-slate-700 transition hover:border-violet-300 hover:bg-violet-50" onclick={() => handleAskAiContext({ target: `filter:${filter.filter_id}`, section: "filters", label: filter.display_name || filter.filter_name, prompt: `${filter.display_name || filter.filter_name}: ${stringifyValue($t, filter.normalized_value ?? filter.raw_value)}` })}>
                      <span class="font-medium text-slate-900">{filter.display_name || filter.filter_name}</span>
                      {#if filter.normalized_value ?? filter.raw_value}
                        <span class="text-slate-400">·</span>
                        <span class="max-w-[8rem] truncate text-slate-500">{stringifyValue($t, filter.normalized_value ?? filter.raw_value)}</span>
                      {/if}
                      {#if filter.recovery_status === "partial"}
                        <span class="rounded bg-amber-100 px-1 py-0.5 text-[10px] font-medium text-amber-700">частично</span>
                      {/if}
                    </button>
                  {/each}
                  {#if importedFilters.length > 6}
                    <span class="inline-flex items-center rounded-lg border border-slate-200 bg-white px-2.5 py-1.5 text-xs text-slate-500">+{importedFilters.length - 6}</span>
                  {/if}
                </div>
              </div>
            {/if}
          </section>

          <!-- Assistant chat — the primary workspace canvas -->
          <div id="assistant-chat"><AssistantChatPanel variant="embedded" className="min-h-[56rem]" key={assistantPromptNonce} /></div>

          <ValidationFindingsPanel findings={findings} recommendedAction={session.recommended_action} focusTarget={workspaceFocusTarget} onjump={handleSectionJump} onaskai={handleAskAiContext} />

          <div id="semantics"><SemanticLayerReview sessionId={session.session_id} fields={session.semantic_fields || []} semanticSources={session.semantic_sources || []} sessionVersion={sessionVersion} disabled={launchDisabled} focusTarget={workspaceFocusTarget} onupdated={handleSemanticUpdated} onaskai={handleAskAiContext} /></div>

          <div id="mapping"><ExecutionMappingReview sessionId={session.session_id} mappings={executionMappings} importedFilters={importedFilters} templateVariables={session.template_variables || []} sessionVersion={sessionVersion} disabled={launchDisabled} focusTarget={workspaceFocusTarget} onupdated={handleMappingUpdated} onaskai={handleAskAiContext} /></div>
        </main>

        <aside class="space-y-5 xl:sticky xl:top-6">
          <ReviewWorkspaceRightRail {session} getRecommendedActionLabel={(a) => getRecommendedActionLabel($t, a)} {workspaceLaunchBlockers} {blockingCount} {warningCount} {infoCount} {launchDisabled} exportDisabled={exportDisabled} {exportMessage} onExportArtifact={exportArtifact} {latestPreview} previewUiState={previewUiState} {sessionVersion} onPreviewUpdated={handlePreviewUpdated} onSectionJump={handleSectionJump} {executionMappings} {latestRunContext} {launchResult} onLaunchUpdated={handleLaunchUpdated} />
        </aside>
      </div>
    {/if}
  {/if}
</div>
<!-- #endregion DatasetReviewWorkspace -->
