<!-- #region TranslationJobConfig [C:4] [TYPE Page] [SEMANTICS sveltekit, translate, job, config, llm, datasource] -->
<!-- @BRIEF Translation job configuration page — 5-step wizard with logical separation:
  Step 1 (Config tab): datasource selection (auto-detects DB dialect), column mapping,
    LLM provider, target languages (multi-select from LANGUAGES).
  Step 2 (Preview tab): translation quality gate — shows sample translations before target config.
  Step 3 (Target tab): target DB/schema/table + column mapping + schema hint validation.
  Step 4 (Run tab): incremental/full translation execution with run history.
  Step 5 (Schedule tab): cron scheduling for saved jobs only.
-->

<!-- ===== UX CONTRACTS ===== -->
<!-- @UX_STATE idle — page not yet initialised. Skeleton loader visible. -->
<!-- @UX_STATE loading — environments, LLM providers and dictionaries being fetched. Full-page spinner. -->
<!-- @UX_STATE configured — job data loaded, form populated, tabs rendered. User interaction enabled. -->
<!-- @UX_STATE saving — PUT/POST in flight. Save button disabled, spinner shown. -->
<!-- @UX_STATE validation_error — form validation failed. Red borders on invalid fields, error summary card. -->
<!-- @UX_STATE datasource_unavailable — selected datasource not found in Superset. Warning banner, datasource re-selection prompt. -->
<!-- @UX_STATE error — unrecoverable error (job not found, network down). Error page with retry button. -->

<!-- @UX_STEP Config (tab) — Step 1: datasource selection with auto-detected DB dialect + column mapping + LLM + target languages (MultiSelect from LANGUAGES). Save required before advancing. -->
<!-- @UX_STEP Preview (tab) — Step 2: translation quality gate. Available after save when configValid=true. Shows sample translations before target config. -->
<!-- @UX_STEP Target (tab) — Step 3: target DB/schema/table config. Only for saved jobs (after Preview). Schema validation via TargetSchemaHint. DB dialect auto-detected from Superset. -->
<!-- @UX_STEP Run (tab) — Step 4: translation execution. Run history, incremental/full modes, progress tracking via translationRunStore. -->
<!-- @UX_STEP Schedule (tab) — Step 5: cron scheduling. Only for saved jobs. Sets schedule via API. -->

<!-- @UX_FEEDBACK Toast "info" — environment switched (handleEnvChange resets targetDatabaseId). -->
<!-- @UX_FEEDBACK Toast "success" — job saved/updated/run completed. -->
<!-- @UX_FEEDBACK Toast "error" — API failure, validation errors, run failed. -->
<!-- @UX_FEEDBACK Modal — BulkReplaceModal for find-and-replace corrections. -->
<!-- @UX_FEEDBACK Inline progress — TranslationRunProgress bar during execution. -->
<!-- @UX_FEEDBACK Schema validation — TargetSchemaHint inline result (green check / red missing columns). -->

<!-- @UX_RECOVERY Config tab save error — user can retry save; validation errors shown inline. -->
<!-- @UX_RECOVERY Preview unavailable — configValid incomplete; user returned to Config tab with missing fields highlighted. -->
<!-- @UX_RECOVERY Datasource unavailable — warning banner + re-selection prompt. -->
<!-- @UX_RECOVERY Run failure — error toast + run history shows FAILED status; user can retry. -->
<!-- @UX_RECOVERY Schedule 404 — silently handled (no schedule = not configured); user can create one. -->

<!-- @UX_REACTIVITY Props -> $page.params.id (jobId), $page.params.id === 'new' (isNewJob). -->
<!-- @UX_REACTIVITY LocalState -> $state for all form fields, $state(uxState), $state(activeTab), $state(isSaving), $state(validationErrors). -->
<!-- @UX_REACTIVITY Derived -> $derived(configValid, pageTitle, tabs, columnList, logicalColumns). -->
<!-- @UX_REACTIVITY Effect -> $effect watches jobId + environments to loadJob; $effect watches environmentId for datasource reload (on new jobs). -->
<!-- @UX_REACTIVITY Store -> translationRunStore (fromStore wrapper), addToast from $lib/toasts. -->

<!-- @UX_DEPENDENCY Datasource selection -> auto-fills databaseDialect, sourceTable, sourceSchema. -->
<!-- @UX_DEPENDENCY Environment selection -> reloads databases, resets targetDatabaseId. -->
<!-- @UX_DEPENDENCY Config valid -> gates Preview tab availability and Run button. -->
<!-- @UX_DEPENDENCY Job saved (existingJob != null) -> unlocks Target and Schedule tabs. -->

<!-- @UX_TEST: idle -> {wait: environments loaded, expected: configured, tabs rendered} -->
<!-- @UX_TEST: configured -> {fill: name, select: datasource, click: save, expected: saving -> toast success} -->
<!-- @UX_TEST: configured -> {click: Preview tab, expected: if configValid -> preview renders; else -> redirect to Config} -->
<!-- @UX_TEST: configured -> {click: Run, expected: if configValid -> run starts, progress bar appears} -->
<!-- @UX_TEST: error -> {simulate: network failure, expected: error toast, retry available} -->

<!-- @RATIONALE Target Config extracted into its own tab (between Preview and Run) so users can review translation quality before configuring the target table for inserts. -->
<!-- @RATIONALE DB dialect auto-detected from Superset datasource — user never manually selects it; eliminates dialect mismatch errors. -->
<!-- @RATIONALE Target languages are human languages from LANGUAGE_LABELS, not DB types — sent to LLM as translation direction. -->
<!-- @REJECTED Keeping target table in Config tab would force users to fill insert details before seeing preview — contradicts the "preview before target" UX requirement. -->
<!-- @REJECTED Manual DB dialect selection in UI rejected — auto-detection from Superset is more reliable and eliminates configuration errors. -->
<!-- @REJECTED Single language field (target_language) rejected in favour of target_languages list — supports multi-language translation per row. -->
<!-- @REJECTED Extracting all sub-tabs into separate page components was rejected due to deep state coupling between datasource selection, column loading, and form validation — would require a global store or excessive prop drilling. -->
<!-- @DEPRECATED N/A — active page. -->
<!-- @REPLACED_BY N/A — no replacement. -->
<script lang="ts">
  import { onMount, onDestroy } from 'svelte';
  import { goto } from '$app/navigation';
  import { page } from '$app/state';
  import { t, _ } from '$lib/i18n/index.svelte.js';
  import { addToast } from '$lib/toasts.svelte.js';
  import { api } from '$lib/api.js';
  import {
    fetchJobs,
    createJob,
    updateJob,
    fetchDatasources,
  } from '$lib/api/translate.js';
  import MultiSelect from '$lib/components/ui/MultiSelect.svelte';
  import TranslationPreview from '$lib/components/translate/TranslationPreview.svelte';
  import TranslationRunProgress from '$lib/components/translate/TranslationRunProgress.svelte';
  import TranslationRunResult from '$lib/components/translate/TranslationRunResult.svelte';
  import ScheduleConfig from '$lib/components/translate/ScheduleConfig.svelte';
	import { triggerRun, fetchRunHistory, cancelRun } from '$lib/api/translate.js';
  import BulkReplaceModal from '$lib/components/translate/BulkReplaceModal.svelte';
  import ConfigTabForm from '$lib/components/translate/ConfigTabForm.svelte';
  import TargetTabForm from '$lib/components/translate/TargetTabForm.svelte';
  import RunTabContent from '$lib/components/translate/RunTabContent.svelte';
	import {
		translationRunStore,
		startTranslationRun,
		resetTranslationRun,
		clearOnCompleteCallback,
	} from '$lib/stores/translationRun.svelte.js';



  /** @type {string} idle | loading | configured | saving | validation_error | datasource_unavailable */
  let uxState = $state('idle');
  let isNewJob = $derived(page.params.id === 'new');
  let jobId = $derived(page.params.id);
  let existingJob = $state(null);
  let error = $state(null);

  // Form state
  let name = $state('');
  let description = $state('');

  let sourceDatasourceId = $state('');
  let sourceTable = $state('');
  let targetSchema = $state('');
  let targetTable = $state('');
  let sourceKeyCols = $state([]);
  let targetKeyCols = $state([]);
  let translationColumn = $state('');
  let targetColumn = $state('');
  let targetLanguageColumn = $state('');
  let targetSourceColumn = $state('');
  let targetSourceLanguageColumn = $state('');
  let contextColumns = $state([]);
  let targetLanguages = $state(['en']);
  let includeSourceReference = $state(true);
  let providerId = $state('');
  let batchSize = $state(50);
  let disableReasoning = $state(false);
  let upsertStrategy = $state('MERGE');
  let dictionaryIds = $state([]);
  let status = $state('DRAFT');

  // Datasource & env state
  let environmentId = $state('');
  let environments = $state([]);
  let datasourceId = $state('');
  let datasourceSearch = $state('');
  let availableColumns = $state([]);
  let virtualColumns = $state([]);
  let databaseDialect = $state('');
  let llmProviders = $state([]);
  let availableDictionaries = $state([]);
  let targetDatabaseId = $state('');
  let databases = $state([]);
  let databasesLoading = $state(false);

  // Tab state
  /** @type {'config'|'preview'|'target'|'run'|'schedule'} */
  let activeTab = $state('config');
  let tabs = $derived.by(() => {
    const base = [
      { id: 'config', label: $t.translate?.config?.basic_info || 'Configuration' },
      { id: 'preview', label: $t.translate?.preview?.title || 'Preview' },
      { id: 'run', label: $t.translate?.config?.run_translation || 'Run' },
    ];
    if (!isNewJob && existingJob) {
      // @RATIONALE Target Config tab inserted between Preview and Run so users can
      //   review/preview translations before configuring the target table for inserts.
      base.splice(2, 0, { id: 'target', label: $t.translate?.config?.target_table_tab || 'Target Config' });
      base.push({ id: 'schedule', label: $t.translate?.schedule?.tab_label || 'Schedule' });
    }
    return base;
  });

  // Derived
  let columnList = $derived(availableColumns);
  let logicalColumns = $derived(availableColumns.filter(c => c.is_physical !== false));
  let virtualColumnNames = $derived(virtualColumns);

  let isSaving = $state(false);
  let validationErrors = $state({});
  let warnings = $state([]);

  // Derived: is the job config complete enough for preview/run?
  // @RATIONALE Removed !isNewJob from configValid — preview availability should depend
  //   on config completeness only, not on whether the job was saved. The preview tab
  //   content already gates rendering on existingJob via the template.
  // @REJECTED Keeping !isNewJob in configValid would block preview when user wants to
  //   see results before configuring target table details.
  let configValid = $derived(
    !!translationColumn
    && !!datasourceId
    && targetLanguages.length > 0
    && !!providerId
  );
  let pageTitle = $derived.by(() => {
    if (isNewJob) return $t.translate?.config?.new_title || 'New Translation Job';
    const jobName = existingJob?.name || name;
    const base = $t.translate?.config?.edit_title || 'Edit Translation Job';
    return jobName ? `${jobName} | ${base}` : base;
  });

  // Run state — using global store to survive page navigation
  let currentRunId = $derived(translationRunStore.value?.runId || null);
  let completedRuns = $state([]);
  let expandedRunIds = $state([]);
  let isRunning = $state(false);
  let isFullRun = $state(false);
	let runError = $state('');
	let showPageBulkReplace = $state(false);
	/** Local flag to hide progress bar when run completes — avoids touching the store
	 *  and triggering cascading reactive updates through fromStore. */
	let runComplete = $state(false);

  async function handleTriggerRun(full = false) {
    isRunning = true;
    runComplete = false;
    isFullRun = full;
    runError = '';
    try {
      const run = await triggerRun(jobId, full);
      startTranslationRun(run.id, { jobId, isFullRun: full, onComplete: handleRunComplete });
      addToast(full ? 'Полный перевод запущен (все строки)' : _('translate.config.run_started'), 'success');
    } catch (err) {
      runError = err?.message || _('translate.config.run_failed');
      isRunning = false;
    }
  }

  function handleRunComplete(statusData) {
    if (runComplete) return; // Idempotent: run already marked complete
    const finishedRunId = currentRunId;
    isRunning = false;
    runComplete = true;
    expandedRunIds = [];
    if (statusData?.status !== 'CANCELLED') {
      loadRunHistory();
    }
    addToast(`${_('translate.run.run_id')} ${finishedRunId}`, 'info');
  }

  async function loadRunHistory() {
    try {
      const data = await fetchRunHistory(jobId, { page_size: 5 });
      completedRuns = (data?.items || []).filter(
        (run) => run && typeof run.id === 'string' && run.id.trim().length > 0
      );
      expandedRunIds = expandedRunIds.filter((runId) => completedRuns.some((run) => run.id === runId));
    } catch (_err) {
      completedRuns = [];
      expandedRunIds = [];
    }
  }

  function toggleRunDetails(runId) {
    if (!runId) return;
    if (expandedRunIds.includes(runId)) {
      expandedRunIds = expandedRunIds.filter((id) => id !== runId);
    } else {
      expandedRunIds = [...expandedRunIds, runId];
    }
  }

  async function handleRetryRun() {
    if (currentRunId) {
      try {
        await cancelRun(currentRunId);
      } catch (_e) { /* ignore */ }
    }
    resetTranslationRun();
    await handleTriggerRun();
  }

  async function handleRetryInsert() {
    if (currentRunId) {
      try {
        const result = await api.postApi(`/translate/runs/${currentRunId}/retry-insert`, {});
        addToast(_('translate.config.insert_retry_started'), 'success');
      } catch (err) {
        addToast(err?.message || _('translate.config.insert_retry_failed'), 'error');
      }
    }
  }

  onMount(async () => {
    // Load environments first
    try {
      const envs = await api.getEnvironments?.() || [];
      environments = Array.isArray(envs) ? envs : [];
      if (environments.length > 0 && !environmentId) {
        environmentId = environments[0]?.id || '';
      }
    } catch (_e) {}

    await loadInitialData();

    // Handle new-job initialisation (runs once on mount)
    if (isNewJob) {
      if (environmentId) {
        await loadDatabases();
      }
      uxState = 'configured';
    }
  });

  // Watch for jobId changes — handles:
  // 1. Direct navigation to existing job page (environments loaded in onMount)
  // 2. Navigation after createJob (goto → param change, component reused)
  // Guards: only runs when environments are ready and it's an existing job
  $effect(() => {
    if (jobId && !isNewJob && environments.length > 0) {
      loadJob();
    }
  });

  // Clean up store callback when page unmounts — the store keeps
  // polling so the global indicator in the layout still works.
  onDestroy(() => {
    clearOnCompleteCallback();
  });

  /** @returns {Promise<void>} */
  async function loadInitialData() {
    try {
      // Load LLM providers
      try {
        const consolidated = await api.getConsolidatedSettings?.() || {};
        llmProviders = consolidated.llm_providers || [];
        // Also try settings endpoint
        if (llmProviders.length === 0 && api.getLlmStatus) {
          const llmStatus = await api.getLlmStatus().catch(() => ({}));
          if (llmStatus?.providers) {
            llmProviders = llmStatus.providers;
          }
        }
      } catch (_e) {
        llmProviders = [];
      }

      // Load dictionaries (from available translate dictionaries)
      try {
        const result = await api.fetchApi('/translate/dictionaries').catch(() => ({}));
        availableDictionaries = Array.isArray(result) ? result : (result?.items || []);
      } catch (_e) {
        availableDictionaries = [];
      }
    } catch (err) {
      console.warn('[translate/config] Failed to load some initial data:', err);
    }
  }

  /** @returns {Promise<void>} */
  async function loadJob() {
    uxState = 'loading';
    try {
      const jobs = await fetchJobs({ page_size: 100 });
      const jobsArr = Array.isArray(jobs) ? jobs : (jobs?.results || []);
      const job = jobsArr.find(j => j.id === jobId);
      if (!job) {
        // Try direct fetch
        try {
          const directJob = await api.fetchApi(`/translate/jobs/${jobId}`);
          existingJob = directJob;
        } catch (_e) {
          error = _('translate.config.job_not_found').replace('{id}', jobId);
          uxState = 'error';
          return;
        }
      } else {
        existingJob = job;
      }

      // Populate form
      const j = existingJob;
      name = j.name || '';
      description = j.description || '';
      sourceDatasourceId = j.source_datasource_id || '';
      sourceTable = j.source_table || '';
      targetSchema = j.target_schema || '';
      targetTable = j.target_table || '';
      sourceKeyCols = j.source_key_cols || [];
      targetKeyCols = j.target_key_cols || [];
      translationColumn = j.translation_column || '';
      targetColumn = j.target_column || '';
      targetLanguageColumn = j.target_language_column || '';
      targetSourceColumn = j.target_source_column || '';
      targetSourceLanguageColumn = j.target_source_language_column || '';
      contextColumns = j.context_columns || [];
      targetLanguages = j.target_languages?.length > 0 ? j.target_languages : (j.target_language ? [j.target_language] : ['en']);
      providerId = j.provider_id || '';
      batchSize = j.batch_size || 50;
      includeSourceReference = j.include_source_reference ?? true;
      disableReasoning = j.disable_reasoning || false;
      upsertStrategy = j.upsert_strategy || 'MERGE';
      dictionaryIds = j.dictionary_ids || [];
      status = j.status || 'DRAFT';

      // Restore target database
      if (j.target_database_id) {
        targetDatabaseId = j.target_database_id;
      }

      // Restore environment from stored job FIRST (before loading databases)
      if (j.environment_id && environments.some(e => e.id === j.environment_id)) {
        environmentId = j.environment_id;
      } else {
        environmentId = environments[0]?.id || '';
      }

      // Load databases for the (now correct) environment
      if (environmentId) {
        await loadDatabases();
      }

      // If there's a datasource, load its columns and look up the name
      if (j.source_datasource_id && environments.length > 0) {
        datasourceId = j.source_datasource_id;
        if (j.source_table) {
          const dialect = j.database_dialect || '';
          datasourceSearch = dialect
            ? `${j.source_table} (${dialect})`
            : j.source_table;
        } else {
          // Fallback — try to look up the actual datasource name from the API
          datasourceSearch = `Dataset #${j.source_datasource_id}`;
          try {
            const dsList = await fetchDatasources(environmentId, '');
            const found = dsList.find(ds => String(ds.id) === String(j.source_datasource_id));
            if (found) {
              datasourceSearch = `${found.table_name} (${found.database_name} · ${found.database_dialect})`;
              databaseDialect = found.database_dialect || '';
            }
          } catch (_e) {
            // Keep fallback "Dataset #<id>" if API fails
          }
        }
      }

      uxState = 'configured';
      // Load run history
      await loadRunHistory();
    } catch (err) {
      error = err?.message || 'Failed to load job';
      uxState = 'error';
    }
  }

  async function loadDatabases() {
    if (!environmentId) {
      databases = [];
      return;
    }
    databasesLoading = true;
    try {
      databases = await api.getEnvironmentDatabases(environmentId) || [];
    } catch (_e) {
      databases = [];
    } finally {
      databasesLoading = false;
    }
  }

  /** @param {Event} event */
  async function handleEnvChange(event) {
    const newEnv = event.target.value;
    environmentId = newEnv;
    databaseDialect = '';
    targetDatabaseId = '';
    await loadDatabases();
  }

  /** @returns {string[]} */
  function validate() {
    const errs = {};
    const warns = [];

    if (!name.trim()) {
      errs.name = _('translate.config.name_required');
    }

    if (targetLanguages.length === 0) {
      errs.targetLanguages = 'At least one target language is required';
    }

    if (!translationColumn) {
      errs.translationColumn = _('translate.config.translation_column_required');
    }

    if (sourceKeyCols.length > 0 && sourceKeyCols.some((_, i) => !targetKeyCols[i])) {
      errs.targetKeyCols = _('translate.config.key_mapping_required');
    }

    // Warn about virtual key columns
    for (const col of sourceKeyCols) {
      if (virtualColumnNames.includes(col)) {
        warns.push(_('translate.config.virtual_column_warning').replace('{col}', col));
      }
    }

    // Check duplicate column mapping
    if (contextColumns.includes(translationColumn)) {
      warns.push(_('translate.config.redundant_column_warning'));
    }

    validationErrors = errs;
    warnings = warns;
    return Object.keys(errs);
  }

  /** @returns {Promise<void>} */
  async function handleSave() {
    const errs = validate();
    if (errs.length > 0) {
      uxState = 'validation_error';
      return;
    }

    isSaving = true;
    uxState = 'saving';
    const payload = {
      name: name.trim(),
      description: description.trim() || null,
      source_dialect: existingJob?.source_dialect || 'SQL',
      target_dialect: existingJob?.target_dialect || 'en',
      source_datasource_id: datasourceId || null,
      source_table: sourceTable || null,
      target_schema: targetSchema || null,
      target_table: targetTable || null,
      source_key_cols: sourceKeyCols,
      target_key_cols: targetKeyCols,
      translation_column: translationColumn || null,
      target_column: targetColumn || null,
      target_language_column: targetLanguageColumn || null,
      target_source_column: targetSourceColumn || null,
      target_source_language_column: targetSourceLanguageColumn || null,
      context_columns: contextColumns,
      target_languages: targetLanguages?.length > 0 ? targetLanguages : null,
      provider_id: providerId || null,
      batch_size: parseInt(batchSize) || 50,
      include_source_reference: includeSourceReference,
      disable_reasoning: disableReasoning,
      upsert_strategy: upsertStrategy,
      dictionary_ids: dictionaryIds,
      database_dialect: databaseDialect || existingJob?.source_dialect || '',
      environment_id: environmentId || null,
      target_database_id: targetDatabaseId || null,
      status: status,
    };

    try {
      if (isNewJob) {
        const created = await createJob(payload);
        existingJob = created;
        addToast(_('translate.config.job_created'), 'success');
        goto(`/translate/${created.id}`);
      } else {
        await updateJob(jobId, payload);
        addToast(_('translate.config.job_updated'), 'success');
        uxState = 'configured';
      }
    } catch (err) {
      addToast(err?.message || _('translate.config.save_failed'), 'error');
      uxState = 'validation_error';
    } finally {
      isSaving = false;
    }
  }

  function getJobStatusLabel(value) {
    const labels = {
      DRAFT: $t.translate?.config?.status_draft || 'Draft',
      READY: $t.translate?.config?.status_ready || 'Ready',
      ACTIVE: $t.translate?.config?.status_active || 'Active',
      RUNNING: $t.translate?.jobs?.status_running || 'Running',
      COMPLETED: $t.translate?.jobs?.status_completed || 'Completed',
      FAILED: $t.translate?.jobs?.status_failed || 'Failed',
      CANCELLED: $t.translate?.history?.status_cancelled || 'Cancelled',
    };
    return labels[value] || value || ($t.translate?.config?.status_draft || 'Draft');
  }
</script>

<svelte:head>
  <title>{pageTitle}</title>
</svelte:head>

<div class="container mx-auto px-4 py-6 max-w-full xl:max-w-7xl">
  <!-- Header -->
  <div class="flex items-center justify-between mb-6">
    <div>
      <h1 class="text-2xl font-bold text-gray-900">
        {isNewJob ? $t.translate?.config?.new_title : $t.translate?.config?.edit_title}
      </h1>
      {#if !isNewJob && existingJob}
        <p class="text-sm text-gray-500 mt-1">{$t.translate?.config?.id_label.replace('{id}', existingJob.id)}</p>
      {/if}
    </div>
    <div class="flex items-center gap-3">
      <button
        onclick={() => goto('/translate')}
        class="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
      >
        {$t.translate?.config?.cancel}
      </button>
      <button
        onclick={handleSave}
        disabled={isSaving}
        class="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
      >
        {isSaving ? $t.translate?.config?.saving : $t.translate?.config?.save}
      </button>
    </div>
  </div>

  <!-- Warnings -->
  {#if warnings.length > 0}
    <div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
      <h4 class="text-sm font-medium text-yellow-800 mb-2">{$t.translate?.config?.warnings_title}</h4>
      <ul class="list-disc list-inside text-sm text-yellow-700 space-y-1">
        {#each warnings as w}
          <li>{w}</li>
        {/each}
      </ul>
    </div>
  {/if}

  <!-- Loading State -->
  {#if uxState === 'loading'}
    <div class="space-y-6">
      {#each Array(6) as _}
        <div class="h-16 bg-gray-100 rounded-lg animate-pulse" />
      {/each}
    </div>

  <!-- Error State -->
  {:else if uxState === 'error'}
    <div class="bg-destructive-light border border-destructive-light rounded-lg p-6 text-center">
      <p class="text-red-700 mb-3">{error || $t.translate?.common?.unknown_error}</p>
      <button
        onclick={() => goto('/translate')}
        class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors"
      >
        {$t.translate?.config?.back_to_jobs}
      </button>
    </div>

  <!-- Datasource Unavailable -->
  {:else if uxState === 'datasource_unavailable'}
    <div class="bg-orange-50 border border-orange-200 rounded-lg p-4 mb-6">
      <p class="text-orange-700 text-sm">
        {$t.translate?.config?.datasource_unavailable}
      </p>
    </div>

  <!-- Form -->
  {:else if uxState === 'configured' || uxState === 'saving' || uxState === 'validation_error' || uxState === 'idle'}
    <!-- Tab Navigation -->
    <div class="border-b border-gray-200 mb-6">
      <nav class="flex space-x-1" aria-label="Tabs">
        {#each tabs as tab}
          <button
            onclick={() => { activeTab = tab.id; }}
              disabled={(tab.id === 'preview' && (!configValid || isNewJob)) || (tab.id === 'target' && isNewJob)}
              class="px-4 py-2.5 text-sm font-medium rounded-t-lg transition-colors
                {activeTab === tab.id
                  ? 'bg-white text-primary border border-b-white border-gray-200 -mb-px'
                  : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'}
                {(tab.id === 'preview' && (!configValid || isNewJob)) || (tab.id === 'target' && isNewJob) ? 'opacity-40 cursor-not-allowed' : ''}"
              title={tab.id === 'preview' && (!configValid || isNewJob) ? ($t.translate?.preview?.config_incomplete || 'Configuration incomplete') : tab.id === 'target' && isNewJob ? ($t.translate?.config?.save_job_first || 'Save the job first') : ''}
          >
            {tab.label}
          </button>
        {/each}
      </nav>
    </div>

    <!-- Config tab: always mounted to preserve state across tab switches -->
    <div class={activeTab === 'config' ? '' : 'hidden'}>
      <ConfigTabForm
        bind:name
        bind:description
        bind:environmentId
        {environments}
        bind:datasourceId
        bind:datasourceSearch
        bind:databaseDialect
        bind:availableColumns
        bind:virtualColumns
        bind:sourceKeyCols
        bind:targetKeyCols
        bind:translationColumn
        bind:contextColumns
        bind:targetLanguages
        bind:targetLanguageColumn
        bind:targetSourceColumn
        bind:targetSourceLanguageColumn
        bind:providerId
        {llmProviders}
        bind:dictionaryIds
        {availableDictionaries}
        bind:disableReasoning
        {validationErrors}
        onEnvChange={handleEnvChange}
      />
    </div>

    <!-- Schedule tab: mounted when not on new-job page, hidden when not active -->
    {#if !isNewJob}
    <div class={activeTab === 'schedule' ? '' : 'hidden'}>
      <section class="bg-white border border-gray-200 rounded-lg">
        <ScheduleConfig {jobId} />
      </section>
    </div>
    {/if}

    <!-- Preview tab: always mounted to preserve state across tab switches -->
    <div class={activeTab === 'preview' ? '' : 'hidden'}>
    {#if !isNewJob && existingJob}
      {#if !configValid}
        <div class="bg-amber-50 border border-amber-200 rounded-lg p-6 text-center">
          <p class="text-sm text-amber-700 font-medium mb-2">
            {$t.translate?.preview?.config_incomplete}
          </p>
          <p class="text-xs text-amber-600">
            {$t.translate?.preview?.config_requirements}
          </p>
        </div>
      {:else}
        <TranslationPreview {jobId} {environmentId} />
      {/if}
    {:else}
      <div class="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
        <p class="text-sm text-gray-500">{$t.translate?.config?.save_job_first || 'Save the job first'}</p>
      </div>
    {/if}
    </div>

    <!-- Target Config tab: target table configuration for inserts — after preview, before run -->
    <div class={activeTab === 'target' ? '' : 'hidden'}>
    {#if !isNewJob && existingJob}
      <TargetTabForm
        bind:targetSchema
        bind:targetTable
        bind:targetDatabaseId
        bind:targetColumn
        bind:targetLanguageColumn
        bind:targetSourceColumn
        bind:targetSourceLanguageColumn
        bind:targetKeyCols
        bind:translationColumn
        bind:batchSize
        bind:includeSourceReference
        bind:upsertStrategy
        {environmentId}
        {databases}
        {databasesLoading}
      />
    {:else}
      <div class="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
        <p class="text-sm text-gray-500">{$t.translate?.config?.save_job_first || 'Save the job first'}</p>
      </div>
    {/if}
    </div>

    <!-- Run tab: always mounted to preserve state across tab switches -->
    <div class={activeTab === 'run' ? '' : 'hidden'}>
    {#if !isNewJob && existingJob}
      <RunTabContent
        bind:status
        {jobId}
        {isRunning}
        {isFullRun}
        {runError}
        {currentRunId}
        {runComplete}
        {completedRuns}
        {expandedRunIds}
        bind:showPageBulkReplace
        onTriggerRun={handleTriggerRun}
        onRetryRun={handleRetryRun}
        onRetryInsert={handleRetryInsert}
        onLoadRunHistory={loadRunHistory}
        onToggleRunDetails={toggleRunDetails}
        onBulkReplaceApplied={() => { showPageBulkReplace = false; loadRunHistory(); }}
        onBulkReplaceClose={() => showPageBulkReplace = false}
        {getJobStatusLabel}
      />
    {:else}
      <div class="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
        <p class="text-sm text-gray-500">{$t.translate?.config?.save_job_first || 'Save the job first'}</p>
      </div>
    {/if}
    </div>
  {/if}
</div>

<BulkReplaceModal
	show={showPageBulkReplace}
	runId={completedRuns[0]?.id || ''}
	targetLanguages={targetLanguages}
	onClose={() => showPageBulkReplace = false}
	onApplied={(count) => { showPageBulkReplace = false; loadRunHistory(); }}
/>
<!-- #endregion TranslationJobConfig -->
