<script lang="ts">
  import { GitCommitHorizontal, LoaderCircle, Sparkles, X } from "@lucide/svelte";
  import type { AiCommitPlan } from "../types";
  import SelectMenu from "./SelectMenu.svelte";

  interface Props {
    plan: AiCommitPlan;
    isApplying: boolean;
    onApply: (plan: AiCommitPlan) => void;
    onClose: () => void;
  }

  let { plan, isApplying = false, onApply, onClose }: Props = $props();
  let draft = $state<AiCommitPlan>({ summary: "", groups: [] });
  $effect.pre(() => {
    if (draft.groups.length === 0) draft = structuredClone(plan);
  });
  let valid = $derived(draft.groups.length > 1 && draft.groups.every((group) => group.message.trim() && group.files.length));
  let validationIssue = $derived(
    draft.groups.findIndex((group) => group.files.length === 0) >= 0
      ? "Every commit needs at least one file."
      : draft.groups.findIndex((group) => !group.message.trim()) >= 0
        ? "Every commit needs a message."
        : "",
  );

  function setMessage(index: number, message: string) {
    draft.groups[index].message = message;
  }

  function moveFile(file: string, from: number, to: number) {
    if (from === to) return;
    draft.groups[from].files = draft.groups[from].files.filter((path) => path !== file);
    draft.groups[to].files = [...draft.groups[to].files, file];
  }

  function applyDraft() {
    // `draft` is a deeply reactive Svelte proxy. `structuredClone(draft)`
    // throws a DataCloneError before the callback runs, which made the button
    // appear to do nothing. A state snapshot is a plain, cloneable object.
    onApply($state.snapshot(draft));
  }
</script>

<svelte:window onkeydown={(event) => { if (event.key === "Escape" && !isApplying) onClose(); }} />

<div class="dialog-backdrop" role="presentation">
  <div class="dialog split-dialog" role="dialog" aria-modal="true" aria-label="AI commit split">
    <header class="dialog-header">
      <div><span class="eyebrow">Staged changes</span><h2>Split into logical commits</h2></div>
      <button class="dialog-close" type="button" onclick={onClose} disabled={isApplying} aria-label="Close"><X size={18} /></button>
    </header>
    <div class="split-intro"><Sparkles size={18} /><p>{draft.summary}</p></div>
    <div class="split-groups">
      {#each draft.groups as group, groupIndex}
        <article class:empty={group.files.length === 0}>
          <header><GitCommitHorizontal size={17} /><strong>Commit {groupIndex + 1}</strong><span>{group.files.length} files</span></header>
          <label>
            <span>Commit message <em>AI-generated · editable</em></span>
            <input value={group.message} oninput={(event) => setMessage(groupIndex, event.currentTarget.value)} disabled={isApplying} />
          </label>
          {#if group.reason}<p>{group.reason}</p>{/if}
          <div class="split-files">
            {#each group.files as file}
              <div><code>{file}</code>
                <SelectMenu class="split-file-target" value={String(groupIndex)} options={draft.groups.map((_, target) => ({ value: String(target), label: `Commit ${target + 1}` }))} disabled={isApplying} onChange={(value) => moveFile(file, groupIndex, Number(value))} />
              </div>
            {/each}
          </div>
        </article>
      {/each}
    </div>
    <footer class="dialog-footer">
      <p class:invalid={!!validationIssue}>{validationIssue || "Messages are generated automatically. All commits are created in this order."}</p>
      <div><button class="btn-secondary" type="button" onclick={onClose} disabled={isApplying}>Cancel</button>
        <button class="btn-primary" type="button" onclick={applyDraft} disabled={!valid || isApplying}>
          {#if isApplying}<LoaderCircle class="spin" size={14} />{/if}Commit all ({draft.groups.length})
        </button></div>
    </footer>
  </div>
</div>

<style>
  .split-dialog{width:min(820px,calc(100vw - 32px));max-height:min(820px,calc(100vh - 32px));display:flex;flex-direction:column}
  .split-intro{display:flex;gap:10px;align-items:flex-start;padding:14px 18px;border-bottom:1px solid var(--color-border-subtle);color:var(--color-ink-muted)}
  .split-intro p{margin:0;line-height:1.5}
  .split-groups{display:grid;gap:10px;padding:14px 18px;overflow:auto}
  article{display:grid;gap:10px;padding:13px;border:1px solid var(--color-border-subtle);border-radius:9px;background:var(--color-surface-raised)}
  article.empty{border-color:#d88a45}
  article>header{display:flex;align-items:center;gap:8px;color:var(--color-ink)}
  article>header span{margin-left:auto;color:var(--color-ink-faint);font-size:11px}
  label{display:grid;gap:5px;color:var(--color-ink-faint);font-size:10px;font-weight:800;text-transform:uppercase}
  label span{display:flex;align-items:center;justify-content:space-between;gap:8px}
  label em{color:var(--color-accent);font-size:9px;font-style:normal;font-weight:700;text-transform:none}
  input{height:34px;padding:0 10px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface);color:var(--color-ink);font-family:var(--font-mono)}
  article>p{margin:0;color:var(--color-ink-muted);font-size:12px;line-height:1.45}
  .split-files{display:grid;gap:5px}
  .split-files>div{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:6px;background:var(--color-surface)}
  code{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;color:var(--color-ink-muted);font-size:11px;white-space:nowrap}
  :global(.split-file-target){width:110px;flex:0 0 110px}
  :global(.split-file-target .select-menu-trigger){height:28px;min-height:28px;font-size:11px}
  .dialog-footer p.invalid{color:#e0a040}
</style>
