<!-- #region DatasetReviewWorkspace [C:4] [TYPE Page] [SEMANTICS sveltekit, dataset, review, workspace, assistant] -->
<!-- @ingroup Routes -->
<!-- @BRIEF Main dataset review workspace — thin page shell delegating state to DatasetReviewModel.
     Page handles only navigation, DOM scroll, and assistant store interactions. -->
<!-- @LAYER Page -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceHeader] -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceLeftSidebar] -->
<!-- @RELATION DEPENDS_ON -> [ReviewWorkspaceRightRail] -->
<!-- @RELATION BINDS_TO -> [DatasetReviewModel] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:datasetReviewSessionStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:assistantChatStore] -->
<!-- @STATE empty     — No session, SourceIntakePanel visible. -->
<!-- @STATE importing — Session being created, milestones panel shown. -->
<!-- @STATE review    — Session active, 3-column layout with findings, mappings, assistant. -->
<!-- @STATE error     — loadError or submitError displayed. -->
<!-- @ACTION handleSourceSubmit(payload)   — API call via model, navigate on success. -->
<!-- @ACTION handleResumeSession()         — PATCH via model, reload. -->
<!-- @ACTION primaryActionHandler()        — Execute recommended action with scroll + assistant. -->
<!-- @ACTION promptAssistant(target, msg)  — Set assistant focus + seed message. -->
<!-- @ACTION handleAskAiContext(payload)   — Build assistant context prompt, scroll to chat. -->
<!-- @RATIONALE Decomposed from 1202→~380 lines (now ~190 after DatasetReviewModel extraction).
     DatasetReviewModel in $lib/models/ hosts all state atoms, derived values, and core actions.
     Page is a thin shell — only navigation and DOM concerns remain inline.
     UseReviewSession composable migrated to $lib/api/dataset-review/useReviewSession.ts. -->
<script lang="ts">
  import { page } from "$app/state";
  import { goto } from "$app/navigation";
  import { t } from "$lib/i18n/index.svelte.js";
  import { Badge, Button } from "$lib/ui";
  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 } from "$lib/stores/environmentContext.svelte.js";
  import { setAssistantDatasetReviewSessionId, setAssistantSeedMessage, setAssistantFocusTarget } from "$lib/stores/assistantChat.svelte.js";
  import { DatasetReviewModel } from "$lib/models/DatasetReviewModel.svelte.ts";

  import ReviewWorkspaceHeader from "../ReviewWorkspaceHeader.svelte";
  import ReviewWorkspaceLeftSidebar from "../ReviewWorkspaceLeftSidebar.svelte";
  import ReviewWorkspaceRightRail from "../ReviewWorkspaceRightRail.svelte";
  import { getWorkspaceStateLabel, getRecommendedActionLabel, getPrimaryActionCtaLabel, stringifyValue } from "$lib/helpers/review-workspace-helpers.js";

  const m = new DatasetReviewModel();
  const routeSessionId = page.params.id || "";

  // ── Helpers (page-specific, not model) ──────────────────────

  async function handleSourceSubmit(payload: Record<string, unknown>) {
    const sessionId = await m.handleSourceSubmit(payload);
    if (sessionId) {
      await goto(DatasetReviewModel.buildSessionUrl(sessionId));
    }
  }

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

  function primaryActionHandler() {
    if (!m.session) return;
    if (m.session.recommended_action === "resume_session") { void m.handleResumeSession(); return; }
    if (m.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 (m.session.recommended_action === "launch_dataset") {
      document.getElementById("launch")?.scrollIntoView({ behavior: "smooth", block: "start" });
      return;
    }
    handleAskAiContext({
      target: m.clarificationCurrentQuestion?.question_id ? `clarification:${m.clarificationCurrentQuestion.question_id}` : "clarification",
      section: "clarification",
      prompt: m.assistantSeedPrompt,
    });
  }

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

  function handleSectionJump(event: { target?: string }) {
    const targets: Record<string, string> = {
      preview: "sql-preview", "sql-preview": "sql-preview",
      mappings: "mapping", mapping: "mapping",
      semantics: "semantics", summary: "summary",
      clarification: "assistant-chat", filters: "summary",
      intake: "summary", "next-action": "next-action",
    };
    const elId = targets[event?.target || ""] || event?.target || "";
    document.getElementById(elId)?.scrollIntoView({ behavior: "smooth", block: "start" });
  }

  // ── Lifecycle ──────────────────────────────────────────────

  m.bootstrap(routeSessionId);

  $effect(() => {
    const ext = datasetReviewSessionStore.value?.session;
    if (ext) m.syncFromStore(ext as Record<string, unknown> | null | undefined);
  });

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

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

  {#if m.isBootstrapping}
    <div class="rounded-2xl border border-border bg-surface-card p-6 text-sm text-text-muted shadow-sm">{$t.dataset_review?.workspace?.loading}</div>
  {:else}
    {#if m.loadError}
      <div class="rounded-2xl border border-destructive-light bg-destructive-light px-4 py-4 text-sm text-destructive">{m.loadError}</div>
    {/if}

    {#if !m.session}
      <SourceIntakePanel environments={environmentContextStore.value?.environments || []} selectedEnvironmentId={m.session?.environment_id || environmentContextStore.value?.selectedEnvId || ""} submitting={m.isSubmitting} acknowledgment={m.intakeAcknowledgment} onsubmit={handleSourceSubmit} />
      {#if m.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-primary">{$t.dataset_review?.workspace?.import_progress_eyebrow}</p>
              <h2 class="mt-1 text-lg font-semibold text-text">{$t.dataset_review?.workspace?.import_progress_title}</h2>
              <p class="mt-1 text-sm text-text">{$t.dataset_review?.workspace?.import_progress_body}</p>
            </div>
            <span class="rounded-full bg-surface-card px-3 py-1 text-xs font-medium text-primary">{getWorkspaceStateLabel($t, m.currentWorkspaceState)}</span>
          </div>
          <ol class="mt-4 grid gap-3 md:grid-cols-2">
            {#each m.importMilestones as milestone}
              <li class="rounded-xl border border-border/70 bg-surface-card/80 p-3">
                <div class="flex items-center gap-2">
                  <span class="inline-flex h-2.5 w-2.5 rounded-full {milestone.state === 'done' ? 'bg-success' : milestone.state === 'active' ? 'bg-primary-ring' : 'bg-border'}"></span>
                  <span class="text-sm font-medium text-text">{milestone.label}</span>
                </div>
              </li>
            {/each}
          </ol>
        </section>
      {/if}
      {#if m.submitError}
        <div class="rounded-2xl border border-destructive-light bg-destructive-light px-4 py-4 text-sm text-destructive">{m.submitError}</div>
      {/if}
    {:else}
      <!-- Session stat cards -->
      <section class="rounded-3xl border border-border bg-surface-card 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-border bg-surface-page px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-text-muted">{$t.dataset_review?.workspace?.phase_card_label || "Phase"}</div>
            <div class="mt-1 text-sm font-semibold text-text">{m.session?.current_phase}</div>
          </div>
          <div class="rounded-2xl border border-border bg-surface-page px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-text-muted">{$t.dataset_review?.workspace?.preview_gate_card_label || "Preview gate"}</div>
            <div class="mt-1 text-sm font-semibold text-text">{m.previewGateState}</div>
          </div>
          <div class="rounded-2xl border border-border bg-surface-page px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-text-muted">{$t.dataset_review?.workspace?.clarification_queue_card_label || "Clarification queue"}</div>
            <div class="mt-1 text-sm font-semibold text-text">{m.clarificationRemainingCount} remaining / {m.clarificationTotalCount}</div>
          </div>
          <div class="rounded-2xl border border-border bg-surface-page px-4 py-3">
            <div class="text-[11px] uppercase tracking-wide text-text-muted">{$t.dataset_review?.workspace?.launch_blockers_card_label || "Launch blockers"}</div>
            <div class="mt-1 text-sm font-semibold text-text">{m.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={m.session} profile={m.profile} currentWorkspaceState={m.currentWorkspaceState} readinessLabel={m.readinessLabel} getWorkspaceStateLabel={(s) => getWorkspaceStateLabel($t, s)} importedFilters={m.importedFilters} executionMappings={m.executionMappings} findings={m.findings} clarificationCurrentQuestion={m.clarificationCurrentQuestion} clarificationRemainingCount={m.clarificationRemainingCount} clarificationTotalCount={m.clarificationTotalCount} importMilestones={m.importMilestones} launchDisabled={m.launchDisabled} saveDisabled={m.saveDisabled} getPrimaryActionCtaLabel={() => getPrimaryActionCtaLabel($t, m.session)} onPrimaryAction={primaryActionHandler} onSaveSession={() => m.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-border bg-gradient-to-br from-primary-light/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-primary-light text-primary">
                    <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-text">{m.profile?.dataset_name || m.session?.dataset_ref}</h2>
                    <p class="mt-0.5 text-sm text-text-muted">{m.session?.environment_id} · {m.session?.dataset_ref}</p>
                  </div>
                </div>
                {#if m.profile?.business_summary && m.profile.business_summary !== `Review session initialized for ${m.session?.dataset_ref}.`}
                  <p class="mt-3 max-w-3xl text-sm leading-relaxed text-text">{m.profile.business_summary}</p>
                {/if}
              </div>
              <div class="flex shrink-0 flex-col items-end gap-2">
                <div class="rounded-xl border border-border bg-surface-card px-4 py-2.5 shadow-sm">
                  <div class="text-[10px] font-medium uppercase tracking-wider text-text-subtle">{$t.dataset_review?.workspace?.next_action_label || "Next step"}</div>
                  <div class="mt-0.5 text-sm font-semibold text-primary">{getRecommendedActionLabel($t, m.session?.recommended_action)}</div>
                </div>
                {#if m.session?.readiness_state === "run_ready"}
                  <Badge variant="success" dot={true}>Готов к запуску</Badge>
                {:else if m.session?.readiness_state === "review_ready"}
                  <Badge variant="primary" dot={true}>Готов к проверке</Badge>
                {:else if m.session?.readiness_state === "recovery_required" || m.session?.readiness_state === "partially_ready"}
                  <Badge variant="warning" dot={true}>Частичное восстановление</Badge>
                {/if}
              </div>
            </div>
            {#if m.importedFilters.length > 0}
              <div class="mt-4 border-t border-border pt-4">
                <div class="flex items-center gap-2">
                  <svg class="h-4 w-4 text-text-subtle" 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-text-muted">{$t.dataset_review?.workspace?.recovered_filters_title || "Фильтры из дашборда"}</span>
                  <Badge variant="primary" size="sm">{m.importedFilters.length}</Badge>
                </div>
                <div class="mt-2 flex flex-wrap gap-2">
                  {#each m.importedFilters.slice(0, 6) as filter}
                    <Button variant="ghost" class="group inline-flex items-center gap-1.5 rounded-lg border border-border bg-surface-card px-2.5 py-1.5 text-xs text-text transition hover:border-primary-ring hover:bg-primary-light" 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-text">{filter.display_name || filter.filter_name}</span>
                      {#if filter.normalized_value ?? filter.raw_value}
                        <span class="text-text-subtle">·</span>
                        <span class="max-w-[8rem] truncate text-text-muted">{stringifyValue($t, filter.normalized_value ?? filter.raw_value)}</span>
                      {/if}
                      {#if filter.recovery_status === "partial"}
                        <span class="rounded bg-warning-light px-1 py-0.5 text-[10px] font-medium text-warning">частично</span>
                      {/if}
                    </Button>
                  {/each}
                  {#if m.importedFilters.length > 6}
                    <span class="inline-flex items-center rounded-lg border border-border bg-surface-card px-2.5 py-1.5 text-xs text-text-muted">+{m.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={m.assistantPromptNonce} /></div>

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

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

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

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