<script lang="ts">
  import { onDestroy, onMount } from "svelte";
  import { open as openDialog } from "@tauri-apps/plugin-dialog";
  import { Cloud, Download, FolderOpen, GitBranch, Globe2, LoaderCircle, LockKeyhole, RefreshCw, Search, X } from "@lucide/svelte";
  import { listIntegrationRepositories } from "../git";
  import { configuredIntegrationSources } from "../integrations";
  import type { AppLanguage, CloneOptions, GitIntegrationProvider, GitIntegrationRepository, GitIntegrationSettings, GitIntegrationSource } from "../types";

  type CloneSource = "url" | "integrations";

  interface Props {
    isBusy: boolean;
    error: string;
    language: AppLanguage;
    integrations: GitIntegrationSettings;
    onClone: (remoteUrl: string, parentPath: string, directoryName: string, options: CloneOptions, provider?: GitIntegrationProvider, accountId?: string) => void;
    onClose: () => void;
  }

  let { isBusy = false, error = "", language = "en", integrations, onClone = () => {}, onClose = () => {} }: Props = $props();
  let source = $state<CloneSource>("url");
  let remoteUrl = $state("");
  let parentPath = $state("");
  let directoryName = $state("");
  let directoryNameEdited = $state(false);
  let directoryAutoName = $state("");
  let browseError = $state("");
  let visibleError = $state("");
  let errorHideTimer: ReturnType<typeof setTimeout> | undefined;
  let selectedSourceId = $state("");
  let selectedRepositoryId = $state("");
  let repositorySearch = $state("");
  let repositoriesBySource = $state<Record<string, GitIntegrationRepository[]>>({});
  let loadingSourceId = $state("");
  let repositoryError = $state("");
  let repositoryListElement = $state<HTMLDivElement>();
  let repositoryScrollbarElement = $state<HTMLDivElement>();
  let repositoryScrollbarVisible = $state(false);
  let repositoryScrollbarTop = $state(0);
  let repositoryScrollbarHeight = $state(28);
  let repositoryScrollTop = $state(0);
  let repositoryScrollMax = $state(0);
  let repositoryScrollbarPointerId = $state<number>();
  let repositoryScrollbarDragY = 0;
  let repositoryScrollbarDragScrollTop = 0;
  let repositoryScrollbarFrame: number | undefined;
  let repositoryRequestId = 0;
  let shallowClone = $state(false);
  let branchToClone = $state("");
  let shallowLimitMode = $state<"depth" | "since">("depth");
  let shallowDepth = $state(1);
  let shallowSince = $state("");
  let customFlags = $state("");
  let sparseCheckout = $state(false);
  let bloblessClone = $state(false);
  let sparsePathInput = $state("");

  const isGerman = $derived(language === "de");
  const configuredSources = $derived(configuredIntegrationSources(integrations));
  const activeSource = $derived(configuredSources.find((candidate) => candidate.id === selectedSourceId));
  const activeRepositories = $derived(activeSource ? repositoriesBySource[activeSource.id] ?? [] : []);
  const filteredRepositories = $derived.by(() => {
    const query = repositorySearch.trim().toLocaleLowerCase();
    if (!query) return activeRepositories;
    return activeRepositories.filter((repository) => `${repository.fullName} ${repository.description}`.toLocaleLowerCase().includes(query));
  });
  const azureRepositoryGroups = $derived.by(() => {
    if (activeSource?.provider !== "azure-devops") return [];
    const groups = new Map<string, GitIntegrationRepository[]>();
    for (const repository of filteredRepositories) {
      const separator = repository.fullName.indexOf("/");
      const project = separator > 0 ? repository.fullName.slice(0, separator) : (isGerman ? "Weitere Repositories" : "Other repositories");
      const repositories = groups.get(project) ?? [];
      repositories.push(repository);
      groups.set(project, repositories);
    }
    return [...groups.entries()].map(([project, repositories]) => ({ project, repositories }));
  });
  const selectedRepository = $derived(activeRepositories.find((repository) => repository.id === selectedRepositoryId));
  const directorySuggestion = $derived(directoryNameFromRemoteUrl(remoteUrl));
  const sparsePaths = $derived.by(() => [...new Set(sparsePathInput.split(/\r?\n/).map((path) => path.trim()).filter(Boolean))]);
  const shallowLimitValid = $derived(!shallowClone || (shallowLimitMode === "depth" ? Number.isInteger(shallowDepth) && shallowDepth > 0 : shallowSince.trim().length > 0));
  const cloneOptionsValid = $derived(shallowLimitValid);
  const canSubmit = $derived(!isBusy && remoteUrl.trim().length > 0 && parentPath.trim().length > 0 && cloneOptionsValid);

  $effect(() => {
    const nextError = error || browseError;
    if (errorHideTimer) clearTimeout(errorHideTimer);
    visibleError = nextError;
    if (nextError) errorHideTimer = setTimeout(() => { visibleError = ""; }, 6000);
  });

  $effect(() => {
    filteredRepositories.length;
    loadingSourceId;
    repositoryError;
    scheduleRepositoryScrollbarUpdate();
  });

  onMount(() => {
    window.addEventListener("resize", scheduleRepositoryScrollbarUpdate);
    return () => window.removeEventListener("resize", scheduleRepositoryScrollbarUpdate);
  });

  onDestroy(() => {
    if (errorHideTimer) clearTimeout(errorHideTimer);
    if (repositoryScrollbarFrame !== undefined) cancelAnimationFrame(repositoryScrollbarFrame);
  });

  function scheduleRepositoryScrollbarUpdate() {
    if (repositoryScrollbarFrame !== undefined) cancelAnimationFrame(repositoryScrollbarFrame);
    repositoryScrollbarFrame = requestAnimationFrame(() => {
      repositoryScrollbarFrame = undefined;
      updateRepositoryScrollbar();
    });
  }

  function updateRepositoryScrollbar() {
    const list = repositoryListElement;
    const track = repositoryScrollbarElement;
    if (!list || !track) {
      repositoryScrollbarVisible = false;
      return;
    }
    const scrollMax = Math.max(0, list.scrollHeight - list.clientHeight);
    const trackHeight = track.clientHeight;
    const thumbHeight = scrollMax > 0
      ? Math.max(28, trackHeight * (list.clientHeight / list.scrollHeight))
      : trackHeight;
    const thumbTravel = Math.max(0, trackHeight - thumbHeight);
    repositoryScrollTop = list.scrollTop;
    repositoryScrollMax = scrollMax;
    repositoryScrollbarHeight = thumbHeight;
    repositoryScrollbarTop = scrollMax > 0 ? (list.scrollTop / scrollMax) * thumbTravel : 0;
    repositoryScrollbarVisible = scrollMax > 1;
  }

  function startRepositoryScrollbarDrag(event: PointerEvent) {
    if (!repositoryListElement) return;
    event.preventDefault();
    event.stopPropagation();
    repositoryScrollbarPointerId = event.pointerId;
    repositoryScrollbarDragY = event.clientY;
    repositoryScrollbarDragScrollTop = repositoryListElement.scrollTop;
    (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);
  }

  function dragRepositoryScrollbar(event: PointerEvent) {
    if (repositoryScrollbarPointerId !== event.pointerId || !repositoryListElement || !repositoryScrollbarElement) return;
    const thumbTravel = repositoryScrollbarElement.clientHeight - repositoryScrollbarHeight;
    if (thumbTravel <= 0) return;
    repositoryListElement.scrollTop = repositoryScrollbarDragScrollTop
      + ((event.clientY - repositoryScrollbarDragY) / thumbTravel) * repositoryScrollMax;
  }

  function stopRepositoryScrollbarDrag(event: PointerEvent) {
    if (repositoryScrollbarPointerId !== event.pointerId) return;
    repositoryScrollbarPointerId = undefined;
    const target = event.currentTarget as HTMLElement;
    if (target.hasPointerCapture(event.pointerId)) target.releasePointerCapture(event.pointerId);
  }

  function jumpRepositoryScrollbar(event: MouseEvent) {
    if (event.target !== event.currentTarget || !repositoryListElement || !repositoryScrollbarElement) return;
    const track = repositoryScrollbarElement.getBoundingClientRect();
    const thumbTravel = track.height - repositoryScrollbarHeight;
    if (thumbTravel <= 0) return;
    const targetTop = Math.max(0, Math.min(thumbTravel, event.clientY - track.top - repositoryScrollbarHeight / 2));
    repositoryListElement.scrollTop = (targetTop / thumbTravel) * repositoryScrollMax;
  }

  function handleRepositoryScrollbarKey(event: KeyboardEvent) {
    if (!repositoryListElement) return;
    const page = repositoryListElement.clientHeight * 0.85;
    const changes: Record<string, number> = {
      ArrowUp: repositoryListElement.scrollTop - 40,
      ArrowDown: repositoryListElement.scrollTop + 40,
      PageUp: repositoryListElement.scrollTop - page,
      PageDown: repositoryListElement.scrollTop + page,
      Home: 0,
      End: repositoryScrollMax,
    };
    if (!(event.key in changes)) return;
    event.preventDefault();
    repositoryListElement.scrollTop = changes[event.key];
  }

  function directoryNameFromRemoteUrl(url: string): string {
    const trimmed = url.trim().split(/[?#]/, 1)[0]?.replace(/[\\/]+$/g, "") ?? "";
    const lastSegment = trimmed.split(/[\\/:]/).filter(Boolean).pop() ?? "";
    return lastSegment.replace(/\.git$/i, "").trim();
  }

  function errorToMessage(error: unknown): string {
    if (error instanceof Error) return error.message;
    if (typeof error === "string") return error;
    try { return JSON.stringify(error) ?? "Unknown error"; } catch { return "Unknown error"; }
  }

  async function chooseParentFolder() {
    if (isBusy) return;
    browseError = "";
    try {
      const selected = await openDialog({ title: isGerman ? "Zielordner zum Klonen auswählen" : "Select clone destination", directory: true, multiple: false, defaultPath: parentPath.trim() || undefined });
      if (typeof selected === "string") parentPath = selected;
    } catch (error) { browseError = errorToMessage(error); }
  }

  function setRemoteUrl(nextRemoteUrl: string) {
    remoteUrl = nextRemoteUrl;
    if (directoryNameEdited) return;
    directoryAutoName = directoryNameFromRemoteUrl(nextRemoteUrl);
    directoryName = directoryAutoName;
  }

  function handleRemoteInput(event: Event) {
    setRemoteUrl((event.currentTarget as HTMLInputElement).value);
    selectedRepositoryId = "";
  }

  function handleDirectoryInput(event: Event) {
    const nextDirectoryName = (event.currentTarget as HTMLInputElement).value;
    directoryNameEdited = nextDirectoryName.trim().length > 0 && nextDirectoryName !== directoryAutoName;
  }

  function selectRepository(repository: GitIntegrationRepository) {
    selectedRepositoryId = repository.id;
    setRemoteUrl(repository.cloneUrl);
  }

  function sortRepositories(repositories: GitIntegrationRepository[]): GitIntegrationRepository[] {
    return [...repositories].sort((left, right) => left.fullName.localeCompare(
      right.fullName,
      isGerman ? "de" : "en",
      { numeric: true, sensitivity: "base" },
    ));
  }

  async function loadRepositories(integrationSource: GitIntegrationSource, force = false) {
    selectedSourceId = integrationSource.id;
    selectedRepositoryId = "";
    repositorySearch = "";
    repositoryError = "";
    if (!force && repositoriesBySource[integrationSource.id]) return;
    const requestId = ++repositoryRequestId;
    loadingSourceId = integrationSource.id;
    try {
      const repositories = await listIntegrationRepositories(integrationSource.provider, integrationSource.baseUrl, integrationSource.accountId);
      if (requestId === repositoryRequestId) repositoriesBySource = { ...repositoriesBySource, [integrationSource.id]: sortRepositories(repositories) };
    } catch (error) {
      if (requestId === repositoryRequestId) repositoryError = errorToMessage(error);
    } finally {
      if (requestId === repositoryRequestId) loadingSourceId = "";
    }
  }

  function selectIntegrationSource(integrationSource: GitIntegrationSource) {
    source = "integrations";
    void loadRepositories(integrationSource);
  }

  function showUrlInput() {
    source = "url";
    selectedRepositoryId = "";
  }

  function formatUpdatedAt(value: string): string {
    if (!value) return "";
    const date = new Date(value);
    return Number.isNaN(date.getTime()) ? "" : new Intl.DateTimeFormat(isGerman ? "de-DE" : "en-US", { dateStyle: "medium" }).format(date);
  }

  function submit(event: SubmitEvent) {
    event.preventDefault();
    if (canSubmit) onClone(
      remoteUrl.trim(),
      parentPath.trim(),
      directoryName.trim(),
      {
        branch: shallowClone && branchToClone.trim() ? branchToClone.trim() : null,
        blobless: sparseCheckout && bloblessClone,
        customFlags: shallowClone ? customFlags.trim() : "",
        shallowDepth: shallowClone && shallowLimitMode === "depth" ? shallowDepth : null,
        shallowSince: shallowClone && shallowLimitMode === "since" ? shallowSince : null,
        sparse: sparseCheckout,
        sparsePaths: sparseCheckout ? sparsePaths : [],
      },
      source === "integrations" ? activeSource?.provider : undefined,
      source === "integrations" ? activeSource?.accountId : undefined,
    );
  }
</script>

<div class="dialog-backdrop" role="presentation">
  <div class="dialog clone-repository-dialog" role="dialog" aria-modal="true" aria-label={isGerman ? "Repository klonen" : "Clone repository"} tabindex="-1">
    <header class="dialog-header clone-dialog-header">
      <div><h2>{isGerman ? "Repository klonen" : "Clone a Repository"}</h2></div>
      <button class="dialog-close" type="button" onclick={onClose} disabled={isBusy} title={isGerman ? "Schließen" : "Close"} aria-label={isGerman ? "Klonen schließen" : "Close clone dialog"}><X size={18} /></button>
    </header>

    <form class="clone-dialog-form" onsubmit={submit}>
      <div class="clone-dialog-layout">
        <aside class="clone-source-nav" aria-label={isGerman ? "Repository-Quellen" : "Repository sources"}>
          <div class="clone-source-heading">{isGerman ? "Quelle" : "Source"}</div>
          <div class="clone-source-list" role="tablist" aria-label={isGerman ? "Repository-Quelle" : "Repository source"}>
            <button type="button" role="tab" aria-selected={source === "url"} class:active={source === "url"} onclick={showUrlInput}><Globe2 size={15} /><span>{isGerman ? "Mit URL klonen" : "Clone with URL"}</span></button>
            {#each configuredSources as integrationSource}
              <button type="button" role="tab" aria-selected={source === "integrations" && selectedSourceId === integrationSource.id} class:active={source === "integrations" && selectedSourceId === integrationSource.id} onclick={() => selectIntegrationSource(integrationSource)}>
                {#if integrationSource.provider === "azure-devops"}<Cloud size={15} />{:else}<GitBranch size={15} />{/if}
                <span>{integrationSource.provider === "azure-devops" ? `Azure · ${integrationSource.label}` : integrationSource.label}</span>
              </button>
            {/each}
          </div>
          {#if configuredSources.length === 0}<p>{isGerman ? "Integrationen kannst du in den Einstellungen einrichten." : "Set up integrations in Settings."}</p>{/if}
        </aside>

        <section class="clone-dialog-content">
          <div class="clone-dialog-title">
            <span>{source === "integrations" ? (activeSource?.label ?? "Integration") : "URL"}</span>
            <h3>{isGerman ? "Repository klonen" : "Clone a Repo"}</h3>
          </div>

          <div class="clone-target-grid">
            <label class="clone-dialog-field"><span>{isGerman ? "Klonen nach" : "Where to clone to"}</span><div class="clone-dialog-path-field"><input bind:value={parentPath} autocomplete="off" spellcheck="false" placeholder={isGerman ? "Übergeordneten Ordner auswählen" : "Choose parent folder"} disabled={isBusy} /><button class="btn-secondary" type="button" onclick={chooseParentFolder} disabled={isBusy}><FolderOpen size={14} />{isGerman ? "Durchsuchen" : "Browse"}</button></div></label>
            <label class="clone-dialog-field"><span>{isGerman ? "Ordnername" : "Folder name"}</span><input bind:value={directoryName} oninput={handleDirectoryInput} autocomplete="off" spellcheck="false" placeholder={directorySuggestion || "Optional"} disabled={isBusy} /></label>
          </div>

          <section class="clone-options" aria-label={isGerman ? "Clone-Optionen" : "Clone options"}>
            <div class="clone-option-card" class:expanded={shallowClone}>
              <label class="clone-option-toggle">
                <input type="checkbox" bind:checked={shallowClone} disabled={isBusy} />
                <span><strong>Shallow Clone</strong><small>{isGerman ? "Nur die neuesten Commits laden" : "Download only the latest commits"}</small></span>
              </label>
              {#if shallowClone}
                <div class="clone-option-body">
                  <label class="clone-option-detail"><span>{isGerman ? "Zu klonender Branch" : "Branch to clone"}</span><input bind:value={branchToClone} autocomplete="off" spellcheck="false" placeholder={isGerman ? "Standard-Branch" : "Default branch"} disabled={isBusy} /></label>
                  <fieldset class="clone-history-mode">
                    <legend>{isGerman ? "Historie begrenzen nach" : "Limit history by"}</legend>
                    <label><input type="radio" bind:group={shallowLimitMode} value="depth" disabled={isBusy} />{isGerman ? "Commit-Tiefe" : "Commit depth"}</label>
                    <label><input type="radio" bind:group={shallowLimitMode} value="since" disabled={isBusy} />{isGerman ? "Seit Datum" : "Since date"}</label>
                  </fieldset>
                  {#if shallowLimitMode === "depth"}
                    <label class="clone-option-detail"><span>{isGerman ? "Tiefe" : "Depth"}</span><input type="number" min="1" step="1" bind:value={shallowDepth} disabled={isBusy} aria-invalid={!Number.isInteger(shallowDepth) || shallowDepth < 1} /></label>
                  {:else}
                    <label class="clone-option-detail"><span>{isGerman ? "Seit" : "Since"}</span><input type="date" bind:value={shallowSince} disabled={isBusy} aria-invalid={!shallowSince.trim()} /></label>
                  {/if}
                  <label class="clone-option-detail"><span>{isGerman ? "Zusätzliche Flags" : "Custom flags"}</span><input bind:value={customFlags} autocomplete="off" spellcheck="false" placeholder="--recurse-submodules --single-branch" disabled={isBusy} /></label>
                  <small class="clone-option-help">{isGerman ? "Flags wie in der Git-Kommandozeile; verwaltete oder unsichere Flags werden abgewiesen." : "Enter flags as on the Git command line; managed or unsafe flags are rejected."}</small>
                </div>
              {/if}
            </div>
            <div class="clone-option-card" class:expanded={sparseCheckout}>
              <label class="clone-option-toggle">
                <input type="checkbox" bind:checked={sparseCheckout} disabled={isBusy} />
                <span><strong>Sparse Checkout</strong><small>{isGerman ? "Nur ausgewählte Pfade auschecken" : "Check out only selected paths"}</small></span>
              </label>
              {#if sparseCheckout}
                <div class="clone-option-body">
                  <label class="clone-blobless-toggle"><input type="checkbox" bind:checked={bloblessClone} disabled={isBusy} /><span><strong>Blobless Clone</strong><small>{isGerman ? "Lädt zunächst Bäume und Commits ohne Dateiinhalte. Blobs werden bei Bedarf nachgeladen." : "Fetch trees and commits without file contents initially. Blobs are downloaded on demand."}</small></span></label>
                  <label class="clone-option-detail clone-sparse-paths">
                    <span>{isGerman ? "Pfade einschließen" : "Paths to include"}</span>
                    <textarea bind:value={sparsePathInput} rows="3" spellcheck="false" placeholder={"src/\ndocs/\nREADME.md"} disabled={isBusy}></textarea>
                    <small class="clone-option-help">{isGerman ? "Ein Pfad pro Zeile. Nur diese Pfade werden im Arbeitsverzeichnis ausgecheckt. Ohne Pfade werden nur Dateien im Repository-Root ausgecheckt." : "Enter one path per line. Only these paths will be checked out in the working directory. With no paths, only files in the repository root are checked out."}</small>
                  </label>
                </div>
              {/if}
            </div>
          </section>

          {#if source === "url"}
            <label class="clone-dialog-field clone-url-field">
              <span>{isGerman ? "Repository-URL" : "Repository URL"}</span>
              <!-- svelte-ignore a11y_autofocus -->
              <input value={remoteUrl} oninput={handleRemoteInput} autocomplete="off" spellcheck="false" placeholder="https://gitlab.com/org/project.git" disabled={isBusy} autofocus />
            </label>
          {:else}
            <section class="integration-browser" aria-label={isGerman ? "Repositories aus Integrationen" : "Repositories from integrations"}>
          {#if configuredSources.length === 0}
            <div class="integration-empty"><Cloud size={26} /><strong>{isGerman ? "Keine aktive Integration" : "No active integration"}</strong><p>{isGerman ? "Richte unter Einstellungen → Integrationen zuerst GitHub, GitLab, Azure DevOps oder Gitea ein." : "Set up GitHub, GitLab, Azure DevOps, or Gitea under Settings → Integrations first."}</p></div>
          {:else}
            <div class="repository-toolbar">
              <label><Search size={14} /><input bind:value={repositorySearch} placeholder={isGerman ? "Repositories durchsuchen…" : "Search repositories…"} aria-label={isGerman ? "Repositories durchsuchen" : "Search repositories"} /></label>
              <button type="button" onclick={() => activeSource && loadRepositories(activeSource, true)} disabled={!activeSource || loadingSourceId.length > 0} title={isGerman ? "Neu laden" : "Refresh"} aria-label={isGerman ? "Repository-Liste neu laden" : "Refresh repository list"}><RefreshCw class={loadingSourceId ? "spin" : ""} size={14} /></button>
            </div>
            <div class="repository-list-shell">
              <div id="integration-repository-list" class="repository-list" bind:this={repositoryListElement} onscroll={updateRepositoryScrollbar} aria-live="polite">
                {#if loadingSourceId}
                  <div class="repository-state"><LoaderCircle class="spin" size={20} /><span>{isGerman ? "Repositories werden geladen…" : "Loading repositories…"}</span></div>
                {:else if repositoryError}
                  <div class="repository-state repository-state-error"><strong>{isGerman ? "Repositories konnten nicht geladen werden" : "Could not load repositories"}</strong><span>{repositoryError}</span></div>
                {:else if filteredRepositories.length === 0}
                  <div class="repository-state"><GitBranch size={20} /><span>{repositorySearch ? (isGerman ? "Keine passenden Repositories." : "No matching repositories.") : (isGerman ? "Keine Repositories gefunden." : "No repositories found.")}</span></div>
                {:else if activeSource?.provider === "azure-devops"}
                  {#each azureRepositoryGroups as group (group.project)}
                    <section class="repository-project-group" aria-label={group.project}>
                      <div class="repository-project-header"><span>{group.project}</span><em>{group.repositories.length}</em></div>
                      {#each group.repositories as repository (repository.id)}
                        <button type="button" class="repository-option" class:selected={selectedRepositoryId === repository.id} onclick={() => selectRepository(repository)}>
                          <span class="repository-option-icon"><GitBranch size={15} /></span>
                          <span class="repository-option-copy"><strong>{repository.name}</strong><small>{repository.description || repository.cloneUrl}</small></span>
                          <span class="repository-option-meta">{#if repository.private}<LockKeyhole size={12} aria-label={isGerman ? "Privat" : "Private"} />{/if}{formatUpdatedAt(repository.updatedAt)}</span>
                        </button>
                      {/each}
                    </section>
                  {/each}
                {:else}
                  {#each filteredRepositories as repository (repository.id)}
                    <button type="button" class="repository-option" class:selected={selectedRepositoryId === repository.id} onclick={() => selectRepository(repository)}>
                      <span class="repository-option-icon"><GitBranch size={16} /></span>
                      <span class="repository-option-copy"><strong>{repository.fullName}</strong><small>{repository.description || repository.cloneUrl}</small></span>
                      <span class="repository-option-meta">{#if repository.private}<LockKeyhole size={12} aria-label={isGerman ? "Privat" : "Private"} />{/if}{formatUpdatedAt(repository.updatedAt)}</span>
                    </button>
                  {/each}
                {/if}
              </div>
              <div
                class="repository-scrollbar"
                class:visible={repositoryScrollbarVisible}
                class:dragging={repositoryScrollbarPointerId !== undefined}
                bind:this={repositoryScrollbarElement}
                role="scrollbar"
                tabindex={repositoryScrollbarVisible ? 0 : -1}
                aria-controls="integration-repository-list"
                aria-label={isGerman ? "Repository-Liste scrollen" : "Scroll repository list"}
                aria-orientation="vertical"
                aria-valuemin="0"
                aria-valuemax={repositoryScrollMax}
                aria-valuenow={repositoryScrollTop}
                onclick={jumpRepositoryScrollbar}
                onkeydown={handleRepositoryScrollbarKey}
              >
                <div
                  class="repository-scrollbar-thumb"
                  role="presentation"
                  style={`height:${repositoryScrollbarHeight}px;transform:translateY(${repositoryScrollbarTop}px)`}
                  onpointerdown={startRepositoryScrollbarDrag}
                  onpointermove={dragRepositoryScrollbar}
                  onpointerup={stopRepositoryScrollbarDrag}
                  onpointercancel={stopRepositoryScrollbarDrag}
                ></div>
              </div>
            </div>
            {#if selectedRepository}<div class="selected-repository"><span>{isGerman ? "Ausgewählt" : "Selected"}</span><strong>{selectedRepository.fullName}</strong><code>{selectedRepository.cloneUrl}</code></div>{/if}
          {/if}
            </section>
          {/if}

          {#if visibleError}<div class="clone-dialog-error" role="alert">{visibleError}</div>{/if}
        </section>
      </div>
      <div class="clone-dialog-actions"><button class="btn-secondary" type="button" onclick={onClose} disabled={isBusy}>{isGerman ? "Abbrechen" : "Cancel"}</button><button class="btn-primary" type="submit" disabled={!canSubmit}>{#if isBusy}<LoaderCircle class="spin" size={16} />{:else}<Download size={16} />{/if}{isGerman ? "Klonen" : "Clone"}</button></div>
    </form>
  </div>
</div>

<style>
  .clone-repository-dialog { width: min(900px, calc(100vw - 32px)); height: min(660px, calc(100vh - 32px)); }
  .clone-dialog-header { min-height: 52px; padding: 0 16px 0 20px; }
  .clone-dialog-header h2 { margin: 0; color: var(--color-ink); font-size: 15px; font-weight: 650; }
  .clone-dialog-form { grid-template-rows: minmax(0, 1fr) auto; gap: 0; height: calc(100% - 53px); padding: 0; }
  .clone-dialog-layout { display: grid; grid-template-columns: 205px minmax(0, 1fr); min-height: 0; }
  .clone-source-nav { min-width: 0; padding: 12px 0; border-right: 1px solid var(--color-border-subtle); background: var(--app-dialog-chrome); }
  .clone-source-heading { padding: 2px 14px 8px; color: var(--color-ink-faint); font-size: 8.5px; font-weight: 850; text-transform: uppercase; letter-spacing: .08em; }
  .clone-source-list { display: grid; gap: 2px; }
  .clone-source-list button { display: grid; grid-template-columns: 18px minmax(0, 1fr); align-items: center; gap: 8px; width: 100%; min-height: 38px; padding: 0 14px; border: 0; border-radius: 0; color: var(--color-ink-dim); background: transparent; box-shadow: none; font-size: 10.5px; font-weight: 650; text-align: left; }
  .clone-source-list button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .clone-source-list button :global(svg) { color: var(--color-ink-faint); }
  .clone-source-list button:hover { color: var(--color-ink); background: var(--color-surface-hover); }
  .clone-source-list button.active { color: var(--color-ink); background: color-mix(in srgb, var(--color-accent) 18%, var(--color-surface-hover)); box-shadow: inset 3px 0 0 var(--color-accent); }
  .clone-source-list button.active :global(svg) { color: var(--color-accent); }
  .clone-source-nav > p { margin: 12px 14px 0; color: var(--color-ink-faint); font-size: 9px; line-height: 1.45; }
  .clone-dialog-content { display: grid; grid-template-rows: auto auto auto minmax(0, 1fr); grid-auto-rows: auto; align-content: stretch; gap: 12px; min-width: 0; min-height: 0; padding: 16px 18px; overflow: hidden; }
  .clone-dialog-title { display: grid; gap: 3px; }
  .clone-dialog-title > span { color: var(--color-accent); font-size: 8.5px; font-weight: 850; text-transform: uppercase; letter-spacing: .07em; }
  .clone-dialog-title h3 { margin: 0; color: var(--color-ink); font-size: 16px; font-weight: 650; }
  .clone-url-field { align-self: start; margin-top: 2px; }
  .clone-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 8px; }
  .clone-option-card { min-width: 0; border: 1px solid var(--color-border-subtle); border-radius: 6px; background: color-mix(in srgb, var(--color-surface-raised) 70%, transparent); }
  .clone-option-card.expanded { border-color: color-mix(in srgb, var(--color-accent) 34%, var(--color-border)); }
  .clone-option-toggle { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 8px; min-height: 42px; padding: 6px 9px; cursor: pointer; }
  .clone-option-toggle > input { width: 14px; height: 14px; margin: 0; accent-color: var(--color-accent); }
  .clone-option-toggle > span { display: grid; min-width: 0; gap: 2px; }
  .clone-option-toggle strong { color: var(--color-ink); font-size: 10.5px; }
  .clone-option-toggle small { overflow: hidden; color: var(--color-ink-faint); font-size: 8.5px; text-overflow: ellipsis; white-space: nowrap; }
  .clone-option-body { display: grid; gap: 8px; padding: 1px 9px 9px 31px; }
  .clone-option-detail { display: grid; grid-template-columns: minmax(82px, auto) minmax(64px, 1fr); align-items: center; gap: 8px; color: var(--color-ink-faint); font-size: 8.5px; font-weight: 750; text-transform: uppercase; letter-spacing: .035em; }
  .clone-option-detail input { height: 28px; min-width: 0; font-size: 10px; }
  .clone-history-mode { display: flex; flex-wrap: wrap; align-items: center; gap: 7px 12px; min-width: 0; margin: 0; padding: 0; border: 0; color: var(--color-ink-dim); font-size: 9.5px; }
  .clone-history-mode legend { float: left; min-width: 100%; margin-bottom: 1px; color: var(--color-ink-faint); font-size: 8.5px; font-weight: 750; text-transform: uppercase; letter-spacing: .035em; }
  .clone-history-mode label, .clone-blobless-toggle { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
  .clone-history-mode input, .clone-blobless-toggle > input { width: 13px; height: 13px; margin: 0; accent-color: var(--color-accent); }
  .clone-option-help { color: var(--color-ink-faint); font-size: 8px; line-height: 1.35; }
  .clone-blobless-toggle { align-items: start; }
  .clone-blobless-toggle > span { display: grid; gap: 2px; }
  .clone-blobless-toggle strong { color: var(--color-ink); font-size: 9.5px; }
  .clone-blobless-toggle small { color: var(--color-ink-faint); font-size: 8px; }
  .clone-sparse-paths { grid-template-columns: 1fr; gap: 5px; }
  .clone-sparse-paths textarea { min-height: 54px; max-height: 72px; resize: vertical; font: 9.5px/1.35 var(--font-mono); }
  .integration-browser { display: grid; grid-template-rows: auto minmax(0, 1fr); grid-auto-rows: auto; gap: 8px; min-height: 0; }
  .repository-toolbar { display: grid; grid-template-columns: minmax(0, 1fr) 32px; gap: 6px; }
  .repository-toolbar label { position: relative; min-width: 0; }
  .repository-toolbar label > :global(svg) { position: absolute; z-index: 1; top: 10px; left: 10px; color: var(--color-ink-faint); }
  .repository-toolbar input { height: 34px; padding-left: 31px; font-size: 11px; }
  .repository-toolbar button { min-height: 32px; padding: 0; }
  .repository-list-shell { position: relative; min-height: 0; height: 100%; overflow: hidden; border: 1px solid var(--color-border-input); border-radius: 7px; background: var(--color-surface-raised); box-shadow: 0 8px 18px rgba(0,0,0,.13); }
  .repository-list { min-height: 0; height: 100%; padding-right: 8px; overflow: auto; scrollbar-width: none; border-radius: inherit; background: transparent; }
  .repository-list::-webkit-scrollbar { display: none; width: 0; height: 0; }
  .repository-scrollbar { position: absolute; z-index: 2; top: 3px; right: 1px; bottom: 3px; width: 7px; border-radius: 4px; opacity: 0; pointer-events: none; touch-action: none; transition: opacity 120ms ease; }
  .repository-scrollbar.visible { opacity: 1; pointer-events: auto; }
  .repository-scrollbar-thumb { position: absolute; top: 0; right: 2px; width: 3px; min-height: 28px; border-radius: 3px; background: var(--app-scrollbar-thumb); cursor: pointer; transition: width 100ms ease, background 100ms ease; }
  .repository-scrollbar:hover .repository-scrollbar-thumb,
  .repository-scrollbar:focus-visible .repository-scrollbar-thumb,
  .repository-scrollbar.dragging .repository-scrollbar-thumb { right: 1px; width: 4px; background: var(--app-scrollbar-thumb-hover); }
  .repository-scrollbar:focus-visible { outline: 1px solid color-mix(in srgb, var(--color-accent) 70%, transparent); outline-offset: 1px; }
  .repository-project-group + .repository-project-group { border-top: 1px solid var(--color-border-subtle); }
  .repository-project-header { position: sticky; z-index: 1; top: 0; display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 34px; padding: 7px 10px 6px 12px; color: var(--color-accent); background: color-mix(in srgb, var(--color-surface-raised) 96%, transparent); font-size: 9.5px; font-weight: 900; text-transform: uppercase; letter-spacing: .045em; }
  .repository-project-header span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .repository-project-header em { display: grid; flex: 0 0 auto; place-items: center; min-width: 19px; height: 16px; padding: 0 5px; color: var(--color-ink); background: color-mix(in srgb, var(--color-ink) 12%, transparent); font-size: 8px; font-style: normal; line-height: 1; letter-spacing: 0; }
  .repository-option { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 8px; width: 100%; min-height: 44px; padding: 6px 10px 6px 12px; border: 0; border-bottom: 1px solid var(--color-border-subtle); border-radius: 0; color: var(--color-ink-dim); background: transparent; text-align: left; }
  .repository-option:last-child { border-bottom: 0; }
  .repository-option:hover { color: var(--color-ink); background: var(--color-surface-hover); }
  .repository-option.selected { color: var(--color-ink); background: color-mix(in srgb, var(--color-accent) 15%, var(--color-surface-hover)); box-shadow: inset 3px 0 0 var(--color-accent); }
  .repository-option-icon { display: grid; place-items: center; width: 22px; height: 22px; color: var(--color-ink-faint); }
  .repository-option.selected .repository-option-icon { color: var(--color-accent); }
  .repository-option-copy { display: grid; min-width: 0; gap: 3px; }
  .repository-option-copy strong, .repository-option-copy small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .repository-option-copy strong { color: inherit; font-size: 10.5px; }
  .repository-option-copy small { color: var(--color-ink-faint); font-size: 9px; }
  .repository-project-group .repository-option { min-height: 38px; padding-block: 5px; }
  .repository-project-group .repository-option-copy { gap: 0; }
  .repository-project-group .repository-option-copy strong { color: var(--color-ink); font-size: 11.5px; font-weight: 800; }
  .repository-project-group .repository-option-copy small { display: none; }
  .repository-option-meta { display: flex; align-items: center; gap: 5px; color: var(--color-ink-faint); font-size: 8.5px; }
  .repository-state, .integration-empty { display: grid; place-items: center; align-content: center; min-height: 188px; padding: 20px; color: var(--color-ink-faint); text-align: center; }
  .repository-state { gap: 7px; font-size: 10.5px; }
  .repository-state strong, .integration-empty strong { color: var(--color-ink); font-size: 11px; }
  .repository-state-error strong { color: #e86060; }
  .repository-state-error span { max-width: 520px; line-height: 1.45; }
  .integration-empty { min-height: 260px; gap: 8px; }
  .integration-empty :global(svg) { color: var(--color-accent); }
  .integration-empty p { max-width: 400px; margin: 0; color: var(--color-ink-faint); font-size: 10px; line-height: 1.5; }
  .selected-repository { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 3px 8px; min-width: 0; padding: 8px 9px; border-left: 2px solid var(--color-accent); background: color-mix(in srgb, var(--color-accent) 6%, transparent); }
  .selected-repository span { color: var(--color-accent); font-size: 8.5px; font-weight: 850; text-transform: uppercase; letter-spacing: .04em; }
  .selected-repository strong { overflow: hidden; color: var(--color-ink); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
  .selected-repository code { grid-column: 2; overflow: hidden; color: var(--color-ink-faint); font: 8.5px var(--font-mono); text-overflow: ellipsis; white-space: nowrap; }
  .clone-target-grid { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(150px, .72fr); gap: 10px; }
  .clone-dialog-actions { min-height: 54px; align-items: center; padding: 9px 16px; border-top: 1px solid var(--color-border-subtle); background: var(--app-dialog-chrome); }
  @media (max-width: 700px) {
    .clone-repository-dialog { width: min(660px, calc(100vw - 20px)); }
    .clone-dialog-layout { grid-template-columns: 155px minmax(0, 1fr); }
    .clone-source-list button { padding-inline: 10px; }
    .clone-target-grid { grid-template-columns: 1fr; }
    .repository-option-meta { display: none; }
  }
  @media (max-width: 500px) {
    .dialog-backdrop { padding: 10px; }
    .clone-repository-dialog { width: calc(100vw - 20px); height: min(660px, calc(100vh - 20px)); }
    .clone-dialog-layout { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
    .clone-source-nav { padding: 6px 0; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--color-border-subtle); }
    .clone-source-heading, .clone-source-nav > p { display: none; }
    .clone-source-list { display: flex; width: max-content; min-width: 100%; padding: 0 6px; }
    .clone-source-list button { width: auto; min-height: 34px; padding-inline: 9px; border-radius: 5px; }
    .clone-source-list button.active { box-shadow: inset 0 -2px 0 var(--color-accent); }
    .clone-options { grid-template-columns: 1fr; }
    .clone-dialog-content { grid-template-rows: auto; grid-auto-rows: auto; align-content: start; padding: 13px 12px; overflow: auto; }
    .integration-browser { min-height: 260px; }
  }
</style>
