<!-- #region BulkCorrectionSidebar [C:3] [TYPE Component] [SEMANTICS translate, correction, bulk, sidebar, dictionary] -->
<!-- @BRIEF Component component: lib/components/translate/BulkCorrectionSidebar.svelte -->
<!-- @RELATION DEPENDS_ON -> [TranslateApi] -->
<!-- @RELATION DEPENDS_ON -> [EXT:frontend:dictionaryApi] -->
<!-- @UX_STATE closed -> Sidebar hidden -->
<!-- @UX_STATE collecting -> Items being collected, user can edit corrections -->
<!-- @UX_STATE reviewing -> User reviews collected corrections before submit -->
<!-- @UX_STATE submitting -> API call in progress -->
<!-- @UX_STATE submitted -> Success feedback -->
<!-- @UX_FEEDBACK Toast on success/error; count badge on toggle button -->
<!-- @UX_RECOVERY Remove individual items; retry submission -->
<script lang="ts">
  import { t } from '$lib/i18n/index.svelte.js';
  import { addToast } from '$lib/toasts.svelte.js';
  import { submitBulkCorrections, dictionaryApi } from '$lib/api/translate.js';

  let {
    runId = '',
    /** @type {Array<{sourceTerm: string, incorrectTarget: string, rowKey?: string}>} */
    initialItems = [],
    onClose = () => {},
    onSubmitted = () => {}
  } = $props();

  /** @type {'closed'|'collecting'|'reviewing'|'submitting'|'submitted'} */
  let uxState = $state('closed');
  let items = $state([]);
  let dictionaries = $state([]);
  let selectedDictId = $state('');
  let submitResult = $state(null);
  let isOpen = $state(false);

  // Open sidebar when items are provided
  $effect(() => {
    if (initialItems.length > 0) {
      // Merge new items into existing collection, deduplicate by sourceTerm+incorrectTarget
      for (const newItem of initialItems) {
        const exists = items.some(
          i => i.sourceTerm === newItem.sourceTerm && i.incorrectTarget === newItem.incorrectTarget
        );
        if (!exists) {
          items = [...items, { ...newItem, correctedTarget: '' }];
        }
      }
      if (!isOpen) {
        openSidebar();
      }
    }
  });

  async function openSidebar() {
    if (items.length === 0) return;
    isOpen = true;
    uxState = 'collecting';
    await loadDictionaries();
  }

  function closeSidebar() {
    isOpen = false;
    uxState = 'closed';
    onClose();
  }

  async function loadDictionaries() {
    try {
      const result = await dictionaryApi.fetchDictionaries({ page_size: 100 });
      dictionaries = Array.isArray(result) ? result : (result?.items || []);
      if (dictionaries.length === 1) {
        selectedDictId = dictionaries[0].id;
      }
    } catch (err) {
      addToast($t.translate?.common?.error + ': ' + ($t.translate?.corrections?.loading || 'Failed to load dictionaries'), 'error');
    }
  }

  function updateCorrection(index, value) {
    items = items.map((item, i) => i === index ? { ...item, correctedTarget: value } : item);
  }

  function removeItem(index) {
    items = items.filter((_, i) => i !== index);
    if (items.length === 0) {
      closeSidebar();
    }
  }

  async function handleSubmitAll() {
    if (!selectedDictId || items.length === 0) return;

    // Validate all items have corrected targets
    const invalidItems = items.filter(i => !i.correctedTarget.trim());
    if (invalidItems.length > 0) {
      addToast($t.translate?.corrections?.submit_failed || 'All items must have a corrected target', 'error');
      return;
    }

    uxState = 'submitting';
    try {
      const corrections = items.map(item => ({
        source_term: item.sourceTerm,
        incorrect_target_term: item.incorrectTarget,
        corrected_target_term: item.correctedTarget.trim(),
        origin_run_id: runId || undefined,
        origin_row_key: item.rowKey || undefined,
      }));

      const result = await submitBulkCorrections({
        corrections,
        dictionary_id: selectedDictId,
      });

      submitResult = result;
      uxState = 'submitted';

      const successCount = result.created_count || result.updated_count || corrections.length;
      const totalCount = corrections.length;
      if (successCount === totalCount) {
        addToast($t.translate?.corrections?.submit_success || 'All corrections submitted', 'success');
      } else {
        addToast(
          ($t.translate?.corrections?.submit_partial || '{success} of {total} submitted')
            .replace('{success}', successCount)
            .replace('{total}', totalCount),
          'info'
        );
      }
      onSubmitted(result);
    } catch (err) {
      addToast(err?.message || $t.translate?.corrections?.submit_failed || 'Failed to submit corrections', 'error');
      uxState = 'collecting';
    }
  }

  function resetAndClose() {
    items = [];
    submitResult = null;
    closeSidebar();
  }

  // Derived
  let validItemCount = $derived(items.filter(i => i.correctedTarget.trim()).length);
</script>

<!-- Toggle button -->
<button
  onclick={openSidebar}
  class="fixed right-0 top-1/3 z-40 flex items-center gap-2 px-3 py-2 bg-primary text-white rounded-l-lg shadow-lg hover:bg-primary-hover transition-all translate-x-0"
  class:translate-x-[360px]={isOpen}
  title={$t.translate?.corrections?.title}
>
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
  </svg>
  <span class="text-xs font-medium">{$t.translate?.corrections?.title}</span>
  {#if items.length > 0}
    <span class="inline-flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full">
      {items.length}
    </span>
  {/if}
</button>

<!-- Sidebar overlay -->
{#if isOpen}
  <div class="fixed inset-0 z-30 bg-black/20" onclick={closeSidebar}></div>

  <div class="fixed right-0 top-0 z-40 h-full w-[360px] bg-white shadow-2xl border-l border-gray-200 flex flex-col">
    <!-- Header -->
    <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200">
      <div>
        <h3 class="text-sm font-semibold text-gray-900">{$t.translate?.corrections?.title}</h3>
        <p class="text-xs text-gray-500">{$t.translate?.corrections?.subtitle}</p>
      </div>
      <button onclick={closeSidebar} class="p-1 text-gray-400 hover:text-gray-600 rounded hover:bg-gray-100">
        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
        </svg>
      </button>
    </div>

    <!-- Body -->
    <div class="flex-1 overflow-y-auto px-4 py-3 space-y-3">
      {#if uxState === 'collecting'}
        {#if items.length === 0}
          <div class="text-center py-8">
            <svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
            </svg>
            <p class="text-sm text-gray-500">{$t.translate?.corrections?.no_items}</p>
            <p class="text-xs text-gray-400 mt-1">{$t.translate?.corrections?.add_hint}</p>
          </div>
        {:else}
          {#each items as item, idx}
            <div class="border border-gray-200 rounded-lg p-3 space-y-2">
              <div class="flex items-start justify-between">
                <div class="flex-1 min-w-0">
                  <p class="text-xs font-medium text-gray-500">{$t.translate?.corrections?.source_term}</p>
                  <p class="text-sm font-mono text-gray-800 truncate">{item.sourceTerm}</p>
                </div>
                <button
                  onclick={() => removeItem(idx)}
                  class="ml-2 p-0.5 text-gray-400 hover:text-red-500 rounded"
                  title={$t.translate?.corrections?.remove}
                >
                  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
                  </svg>
                </button>
              </div>
              <div>
                <p class="text-xs font-medium text-red-500">{$t.translate?.corrections?.incorrect_target}</p>
                <p class="text-sm font-mono text-red-600 truncate">{item.incorrectTarget}</p>
              </div>
              <div>
                <label class="text-xs font-medium text-green-600">{$t.translate?.corrections?.corrected_target}</label>
                <input
                  type="text"
                  value={item.correctedTarget}
                  oninput={(e) => updateCorrection(idx, e.target.value)}
                  placeholder={$t.translate?.corrections?.corrected_placeholder}
                  class="w-full mt-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-green-500 focus:border-green-500"
                />
              </div>
            </div>
          {/each}
        {/if}

      {:else if uxState === 'submitting'}
        <div class="flex flex-col items-center justify-center py-12">
          <div class="animate-spin w-8 h-8 border-2 border-blue-600 border-t-transparent rounded-full mb-3"></div>
          <p class="text-sm text-gray-500">{$t.translate?.corrections?.submitting}</p>
        </div>

      {:else if uxState === 'submitted'}
        <div class="text-center py-8">
          <svg class="w-12 h-12 text-green-500 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
          </svg>
          <p class="text-sm font-medium text-gray-900">{$t.translate?.corrections?.submit_success}</p>
          {#if submitResult}
            <p class="text-xs text-gray-500 mt-1">
              {$t.translate?.corrections?.submit_partial
                ?.replace('{success}', submitResult.created_count || submitResult.updated_count || items.length)
                ?.replace('{total}', items.length)}
            </p>
          {/if}
        </div>
      {/if}
    </div>

    <!-- Footer -->
    <div class="border-t border-gray-200 px-4 py-3 space-y-3">
      {#if uxState === 'collecting' && items.length > 0}
        <!-- Dictionary selector -->
        <div>
          <label class="block text-xs font-medium text-gray-700 mb-1">{$t.translate?.corrections?.dictionary}</label>
          <select
            bind:value={selectedDictId}
            class="w-full px-2 py-1.5 border border-gray-300 rounded text-sm"
          >
            <option value="">{$t.translate?.corrections?.select_dictionary}</option>
            {#each dictionaries as dict}
              <option value={dict.id}>{dict.name}</option>
            {/each}
          </select>
        </div>

        <button
          onclick={handleSubmitAll}
          disabled={!selectedDictId || validItemCount === 0}
          class="w-full px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-primary-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
        >
          {$t.translate?.corrections?.submit_all?.replace('{count}', validItemCount)}
        </button>
      {:else if uxState === 'submitted'}
        <button
          onclick={resetAndClose}
          class="w-full px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-primary-hover transition-colors"
        >
          {$t.translate?.common?.close}
        </button>
      {/if}
    </div>
  </div>
{/if}
<!-- #endregion BulkCorrectionSidebar -->
