<!-- #region GitWorkspacePanel [C:4] [TYPE Component] [SEMANTICS git, workspace, commit, diff, sync] -->
<!-- @BRIEF Git workspace panel: sync, commit, lazy-chunked diff viewer (IntersectionObserver). -->
<!-- @LAYER UI -->
<!-- @RELATION DEPENDS_ON -> [GitUtils] -->
<!-- @RELATION CALLS -> [EXT:frontend:gitService] -->
<!-- @PRE  hasWorkspaceChanges and workspaceDiff are propagated from GitManager. -->
<!-- @UX_STATE Idle -> "Нет изменений" placeholder when !hasWorkspaceChanges. -->
<!-- @UX_STATE Changes -> workspaceDiff rendered in chunks as user scrolls. -->
<!-- @UX_STATE Loading -> GeneratingMessage spinner, diff-chunks loading indicator. -->
<!-- @UX_STATE Error -> Toast on failure. -->
<!-- @UX_RECOVERY Scroll down to load more diff chunks; sentinel triggers lazy render. -->
<script lang="ts">
  import { t } from "$lib/i18n/index.svelte.js";
  import { Button } from "$lib/ui";
  import * as Diff2Html from 'diff2html';
  import 'diff2html/bundles/css/diff2html.min.css';

  let {
    hasWorkspaceChanges,
    changedFilesCount,
    workspaceLoading,
    workspaceDiff = '',
    committing,
    generatingMessage,
    commitMessage = $bindable(),
    autoPushAfterCommit = $bindable(),
    loading,
    pushProviderLabel,
    onSync,
    onGenerateMessage,
    onCommit,
  } = $props();

  // ── Lazy chunked diff rendering ──
  // Split diff into per-file sections on "diff --git " header
  let diffChunks = $derived.by(() => {
    if (!workspaceDiff) return [];
    const header = 'diff --git ';
    const parts = [];
    let start = 0;
    while (true) {
      const idx = workspaceDiff.indexOf(header, start);
      if (idx < 0) break;
      // Include the "diff --git " prefix in the chunk
      const chunkStart = idx;
      const nextIdx = workspaceDiff.indexOf(header, idx + 1);
      const chunkEnd = nextIdx >= 0 ? nextIdx : workspaceDiff.length;
      parts.push(workspaceDiff.slice(chunkStart, chunkEnd));
      start = nextIdx >= 0 ? nextIdx : workspaceDiff.length;
      if (nextIdx < 0) break;
    }
    // Fallback: if no "diff --git" headers, treat entire diff as one chunk
    if (parts.length === 0 && workspaceDiff.trim()) {
      parts.push(workspaceDiff);
    }
    return parts;
  });

  const CHUNKS_PER_PAGE = 3; // render 3 file diffs at a time
  let renderedChunkCount = $state(0);
  let sentinelEl = $state(null);
  let chunkLoading = $state(false);

  // When diff changes, reset to first chunk page
  $effect(() => {
    if (workspaceDiff) {
      renderedChunkCount = Math.min(CHUNKS_PER_PAGE, diffChunks.length);
    }
  });

  // IntersectionObserver on sentinel to load more chunks
  $effect(() => {
    const el = sentinelEl;
    if (!el) return;
    if (renderedChunkCount >= diffChunks.length) return;
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting && renderedChunkCount < diffChunks.length) {
            chunkLoading = true;
            // Yield to let browser paint the loading indicator
            requestAnimationFrame(() => {
              requestAnimationFrame(() => {
                renderedChunkCount = Math.min(
                  renderedChunkCount + CHUNKS_PER_PAGE,
                  diffChunks.length
                );
                chunkLoading = false;
              });
            });
          }
        }
      },
      { rootMargin: '200px 0px' } // trigger 200px before sentinel enters viewport
    );
    observer.observe(el);
    return () => observer.disconnect();
  });

  // Render only visible chunks via Diff2Html individually
  let renderedHtml = $derived.by(() => {
    const count = renderedChunkCount;
    if (count === 0 || diffChunks.length === 0) return '';
    const visible = diffChunks.slice(0, count).join('\n');
    try {
      return Diff2Html.html(visible, {
        outputFormat: 'side-by-side',
        drawFileList: true,
        matching: 'lines',
        highlight: true,
      });
    } catch (_e) {
      return '<div class="p-4 text-sm text-red-500">Failed to render diff</div>';
    }
  });

  const totalChunks = $derived(diffChunks.length);
  const hasMoreChunks = $derived(renderedChunkCount < totalChunks);
</script>

<div class="flex min-h-0 flex-1 flex-col gap-4 lg:flex-row">
  <!-- Left sidebar: commit controls (sticky, no scroll) -->
  <div class="w-full shrink-0 space-y-4 lg:w-80 xl:w-96">
    <!-- Sync button -->
    <div class="rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
      <Button
        variant="secondary"
        onclick={onSync}
        disabled={loading}
        isLoading={loading}
        class="w-full"
      >
        <svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-1.5 h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
        Синхронизировать из Superset
      </Button>
    </div>

    <!-- Commit message -->
    <div class="rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
      <div class="mb-3 flex items-center justify-between">
        <h3 class="text-sm font-semibold text-slate-700">Сообщение коммита</h3>
        <button
          class="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-50 disabled:opacity-50"
          onclick={onGenerateMessage}
          disabled={generatingMessage || workspaceLoading}
        >
          <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456z"/></svg>
          LLM
        </button>
      </div>
      <textarea
        bind:value={commitMessage}
        class={`h-32 w-full resize-none rounded-lg border border-slate-200 p-3 text-sm outline-none transition-colors focus:border-blue-400 focus:ring-2 focus:ring-blue-100 ${generatingMessage ? 'animate-pulse bg-slate-50' : 'bg-white'}`}
        placeholder="Опишите изменения..."
      ></textarea>
    </div>

    <!-- Commit action -->
    <div class="rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
      <div class="mb-3 flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2 text-sm text-slate-600">
        <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
        Файлов с изменениями: <strong class="text-slate-800">{changedFilesCount}</strong>
      </div>

      <Button
        onclick={onCommit}
        disabled={committing || workspaceLoading || !commitMessage || !hasWorkspaceChanges}
        isLoading={committing}
        class="w-full"
      >
        <svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-1.5 h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
        Зафиксировать (Commit)
      </Button>

      <label class="mt-3 flex items-center gap-2.5 rounded-md border border-slate-100 bg-slate-50 px-3 py-2 text-xs text-slate-600 transition-colors hover:bg-slate-100">
        <input type="checkbox" bind:checked={autoPushAfterCommit} class="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500" />
        <span>{$t.git?.auto_push_after_commit || 'Сделать push после commit в'} <strong class="font-medium text-slate-700">{pushProviderLabel}</strong></span>
      </label>
    </div>
  </div>

  <!-- Right area: diff preview — lazy chunked rendering -->
  <div class="flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm">
    <div class="flex items-center justify-between border-b border-slate-200 bg-slate-50 px-4 py-3">
      <div class="flex items-center gap-2">
        <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"/></svg>
        <span class="text-sm font-semibold text-slate-700">Diff (изменения)</span>
      </div>
      {#if hasWorkspaceChanges}
        <span class="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-200">
          {changedFilesCount} файлов
          {#if totalChunks > 0}
            <span class="text-blue-400">· {renderedChunkCount}/{totalChunks}</span>
          {/if}
        </span>
      {/if}
    </div>
    <div class="flex-1 overflow-auto bg-white p-4" id="diff-scroll-container">
      {#if workspaceLoading}
        <div class="space-y-3">
          {#each Array(8) as _}
            <div class="h-4 animate-pulse rounded bg-slate-100"></div>
          {/each}
          <div class="mt-6 grid grid-cols-2 gap-3">
            {#each Array(6) as _}
              <div class="h-6 animate-pulse rounded bg-slate-50"></div>
            {/each}
          </div>
        </div>
      {:else if hasWorkspaceChanges && renderedHtml}
        <div class="diff-view">{@html renderedHtml}</div>
        <!-- Sentinel for IntersectionObserver — triggers next chunk load -->
          {#if hasMoreChunks}
          <div
            bind:this={sentinelEl}
            class="flex items-center justify-center py-6 text-sm text-slate-400"
            role="status"
            aria-live="polite"
          >
            {#if chunkLoading}
              <div class="flex items-center gap-2">
                <div class="h-4 w-4 animate-spin rounded-full border-b-2 border-blue-500"></div>
                <span>{$t.git?.diff_loading || 'Загрузка изменений...'}</span>
              </div>
            {:else}
              <span class="cursor-pointer text-blue-500 hover:text-blue-700" onclick={() => {
                renderedChunkCount = Math.min(renderedChunkCount + CHUNKS_PER_PAGE, totalChunks);
              }}>
                {$t.git?.diff_show_more?.replace('{count}', totalChunks - renderedChunkCount) || `Показать ещё (${totalChunks - renderedChunkCount} файлов)`}
              </span>
            {/if}
          </div>
        {:else if totalChunks > 0}
          <div class="flex items-center justify-center py-4 text-xs text-slate-400">
            {$t.git?.diff_all_shown?.replace('{count}', totalChunks) || `Показаны все изменения (${totalChunks} файлов)`}
          </div>
        {/if}
      {:else if hasWorkspaceChanges}
        <div class="flex h-full flex-col items-center justify-center gap-3 text-sm text-slate-400">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-slate-200" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"/></svg>
          <span>Загрузка diff... (нажмите «Синхронизировать»)</span>
        </div>
      {:else}
        <div class="flex h-full flex-col items-center justify-center gap-3 text-sm text-slate-400">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-slate-200" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25zM6.75 12h.008v.008H6.75V12zm0 3h.008v.008H6.75V15zm0 3h.008v.008H6.75V18z"/></svg>
          <span>Нет изменений для коммита</span>
          <span class="text-xs text-slate-300">Синхронизируйте дашборд, чтобы увидеть изменения</span>
        </div>
      {/if}
    </div>
  </div>
</div>

<!-- Custom styles for diff2html -->
<style>
  :global(.diff-view .d2h-wrapper) {
    font-size: 12px;
    line-height: 1.6;
  }
  :global(.diff-view .d2h-file-header) {
    background: #f8fafc;
    border-color: #e2e8f0;
    padding: 8px 12px;
    font-size: 12px;
    font-weight: 600;
  }
  :global(.diff-view .d2h-code-line) {
    padding: 0 12px;
  }
  :global(.diff-view .d2h-ins) {
    background-color: #f0fdf4;
  }
  :global(.diff-view .d2h-del) {
    background-color: #fef2f2;
  }
  :global(.diff-view .d2h-code-side-linenumber) {
    width: 48px;
    min-width: 48px;
  }
  :global(.diff-view table.d2h-diff-table) {
    width: 100%;
  }
  :global(.diff-view .d2h-side-sides) {
    min-width: 100%;
  }

  /* Fix: absolute-positioned line numbers must scroll with their parent container */
  :global(.diff-view .d2h-file-side-diff) {
    position: relative;
  }
  :global(.diff-view .d2h-code-wrapper) {
    position: relative;
  }
</style>
<!-- #endregion GitWorkspacePanel -->
