<script lang="ts">
  import { AlertTriangle, ArrowDown, ArrowUp, GitBranch, LoaderCircle, Play, X } from "@lucide/svelte";
  import type { GitBranch as GitBranchInfo, RebaseAction, RebaseCommit, RebasePlanItem } from "../types";

  interface PlanRow extends RebaseCommit {
    action: RebaseAction;
    message: string;
  }

  interface Props {
    branches: GitBranchInfo[];
    currentBranch: string;
    base: string;
    commits: RebaseCommit[];
    isLoading: boolean;
    isBusy: boolean;
    operation: string;
    error: string;
    onBaseChange: (base: string) => void;
    onStart: (plan: RebasePlanItem[]) => void;
    onClose: () => void;
  }

  let {
    branches = [], currentBranch = "", base = "", commits = [], isLoading = false,
    isBusy = false, operation = "", error = "", onBaseChange = () => {},
    onStart = () => {}, onClose = () => {},
  }: Props = $props();

  let rows = $state<PlanRow[]>([]);

  $effect(() => {
    rows = commits.map((commit) => ({ ...commit, action: "pick", message: commit.summary }));
  });

  let availableBases = $derived(branches.filter((branch) => !branch.current));
  let keptCount = $derived(rows.filter((row) => row.action !== "drop").length);
  let invalidSquash = $derived(rows.some((row, index) =>
    (row.action === "squash" || row.action === "fixup")
      && rows.slice(0, index).every((previous) => previous.action === "drop")
  ));
  let invalidReword = $derived(rows.some((row) => row.action === "reword" && !row.message.trim()));
  let canStart = $derived(Boolean(base) && rows.length > 0 && keptCount > 0 && !invalidSquash && !invalidReword && !isLoading && !isBusy);

  function updateAction(index: number, action: RebaseAction) {
    rows = rows.map((row, rowIndex) => rowIndex === index ? { ...row, action } : row);
  }

  function updateMessage(index: number, message: string) {
    rows = rows.map((row, rowIndex) => rowIndex === index ? { ...row, message } : row);
  }

  function move(index: number, direction: -1 | 1) {
    const target = index + direction;
    if (target < 0 || target >= rows.length) return;
    const next = [...rows];
    [next[index], next[target]] = [next[target], next[index]];
    rows = next;
  }

  function start() {
    if (!canStart) return;
    onStart(rows.map((row) => ({
      hash: row.hash,
      action: row.action,
      message: row.action === "reword" ? row.message.trim() : null,
    })));
  }
</script>

<div class="dialog-backdrop app-chrome-backdrop" role="presentation">
  <div class="dialog interactive-rebase-dialog" role="dialog" aria-modal="true" aria-label="Interactive rebase" tabindex="-1">
    <header class="dialog-header">
      <div>
        <span class="eyebrow">Rewrite local history</span>
        <h2 class="dialog-title">Interactive rebase</h2>
      </div>
      <button class="dialog-close" type="button" onclick={onClose} disabled={isBusy} title="Close"><X size={18} aria-hidden="true" /></button>
    </header>

    <div class="interactive-rebase-body">
      <section class="rebase-base-bar">
        <label>
          <span>Rebase <strong>{currentBranch || "current branch"}</strong> onto</span>
          <select value={base} onchange={(event) => onBaseChange((event.target as HTMLSelectElement).value)} disabled={isBusy || isLoading}>
            <option value="" disabled>Select a base branch</option>
            {#each availableBases as branch (branch.name)}
              <option value={branch.name}>{branch.remote ? "Remote · " : "Local · "}{branch.name}</option>
            {/each}
          </select>
        </label>
        <p>Oldest commit first. Reorder commits, then choose how each one should be replayed.</p>
      </section>

      {#if error}
        <div class="rebase-warning error"><AlertTriangle size={16} aria-hidden="true" /><span>{error}</span></div>
      {/if}

      {#if isLoading}
        <div class="blank-state"><LoaderCircle class="spin" size={18} aria-hidden="true" /> Loading rebase range…</div>
      {:else if !base}
        <div class="blank-state">Select the branch or commit that should become the new base.</div>
      {:else if rows.length === 0}
        <div class="blank-state">No linear commits are available above this base.</div>
      {:else}
        <div class="rebase-plan" role="list" aria-label="Interactive rebase plan">
          {#each rows as row, index (row.hash)}
            <article class:drop={row.action === "drop"} class="rebase-plan-row" role="listitem">
              <div class="rebase-order-actions">
                <button type="button" onclick={() => move(index, -1)} disabled={isBusy || index === 0} title="Move up"><ArrowUp size={14} aria-hidden="true" /></button>
                <button type="button" onclick={() => move(index, 1)} disabled={isBusy || index === rows.length - 1} title="Move down"><ArrowDown size={14} aria-hidden="true" /></button>
              </div>
              <select class={`rebase-action ${row.action}`} value={row.action} onchange={(event) => updateAction(index, (event.target as HTMLSelectElement).value as RebaseAction)} disabled={isBusy} aria-label={`Action for ${row.short_hash}`}>
                <option value="pick">pick</option><option value="reword">reword</option><option value="squash">squash</option><option value="fixup">fixup</option><option value="drop">drop</option>
              </select>
              <code>{row.short_hash}</code>
              <div class="rebase-commit-copy">
                {#if row.action === "reword"}
                  <input value={row.message} oninput={(event) => updateMessage(index, (event.target as HTMLInputElement).value)} disabled={isBusy} aria-label={`New message for ${row.short_hash}`} maxlength="240" />
                {:else}
                  <strong>{row.summary}</strong>
                {/if}
                <span>{row.author_name} · {new Date(row.date).toLocaleString()}</span>
              </div>
            </article>
          {/each}
        </div>
      {/if}

      {#if invalidSquash}
        <div class="rebase-warning"><AlertTriangle size={15} aria-hidden="true" /> Squash and fixup need an earlier commit that is not dropped.</div>
      {:else if invalidReword}
        <div class="rebase-warning"><AlertTriangle size={15} aria-hidden="true" /> Reword messages cannot be empty.</div>
      {/if}
    </div>

    <footer class="dialog-footer">
      <span class="dialog-footer-info">{keptCount} of {rows.length} commits kept</span>
      <div class="rebase-footer-actions">
        <button class="btn-secondary" type="button" onclick={onClose} disabled={isBusy}>Cancel</button>
        <button class="btn-primary" type="button" onclick={start} disabled={!canStart}>
          {#if operation === "Starting interactive rebase"}<LoaderCircle class="spin" size={16} aria-hidden="true" />{:else}<Play size={16} aria-hidden="true" />{/if}
          Start rebase
        </button>
      </div>
    </footer>
  </div>
</div>
