<script lang="ts">
  import { get } from "svelte/store";
  import { open } from "@tauri-apps/plugin-dialog";
  import { invoke } from "@tauri-apps/api/core";
  import {
    taskLoopStore,
    getReadyTasks,
    computeWaves,
    isTaskBlocked,
    buildTaskPrompt,
    buildAutoCommitPrompt,
    normalizeToUnixPath,
    type TaskLoopTask,
  } from "$lib/stores/taskLoop";
  import { claudeStore } from "$lib/stores/claude";
  import { configStore } from "$lib/stores/config";
  import { PROJECT_CONTEXT_SYSTEM_ADDENDUM } from "$lib/stores/projectContext";
  import type { CharacterState } from "$lib/types/states";

  interface Props {
    onClose: () => void;
    onBackToWorkflow?: () => void;
  }

  const { onClose, onBackToWorkflow }: Props = $props();

  const tasks = $derived(taskLoopStore.tasks);
  const loopStatus = $derived(taskLoopStore.loopStatus);
  const sourceFile = $derived(taskLoopStore.sourceFile);
  const conversations = $derived(claudeStore.conversations);
  const concurrencyLimit = $derived(taskLoopStore.concurrencyLimit);
  const config = $derived(configStore.config);

  // Per-task orchestration phases (panel-local, not persisted)
  type LoopPhase = "waiting_for_connection" | "waiting_for_completion" | "waiting_for_auto_commit";
  let activePhases = $state<Record<number, LoopPhase>>({});
  let taskEverStartedMap = $state<Record<number, boolean>>({});
  let commitEverStartedMap = $state<Record<number, boolean>>({});
  let isLoading = $state(false);
  let errorMessage = $state<string | null>(null);
  let sessionTimestamp = $state("");
  let showSettings = $state(false);

  const completedCount = $derived($tasks.filter((t) => t.status === "completed").length);
  const failedCount = $derived($tasks.filter((t) => t.status === "failed").length);
  const blockedCount = $derived($tasks.filter((t) => t.status === "blocked").length);
  const runningCount = $derived($tasks.filter((t) => t.status === "running").length);
  const totalCount = $derived($tasks.length);
  const waves = $derived(computeWaves($tasks));
  const multiWave = $derived(waves.length > 1);

  const workingStates: CharacterState[] = ["thinking", "typing", "coding", "searching", "mcp"];

  // Watch all active tasks' conversations for state transitions
  $effect(() => {
    for (const [idxStr, phase] of Object.entries(activePhases)) {
      const taskIdx = Number(idxStr);
      const taskList = $tasks;
      if (taskIdx < 0 || taskIdx >= taskList.length) continue;

      const currentTask = taskList[taskIdx];
      if (!currentTask.conversationId) continue;

      const conv = $conversations.get(currentTask.conversationId);
      if (!conv) continue;

      if (phase === "waiting_for_connection" && conv.connectionStatus === "connected") {
        activePhases = { ...activePhases, [taskIdx]: "waiting_for_completion" };
        taskEverStartedMap = { ...taskEverStartedMap, [taskIdx]: false };
        void sendTaskPrompt(currentTask, taskIdx, taskList.length);
        continue;
      }

      if (phase === "waiting_for_completion") {
        if (workingStates.includes(conv.characterState)) {
          taskEverStartedMap = { ...taskEverStartedMap, [taskIdx]: true };
        }
        if (taskEverStartedMap[taskIdx] && conv.characterState === "idle") {
          taskEverStartedMap = Object.fromEntries(
            Object.entries(taskEverStartedMap).filter(([k]) => Number(k) !== taskIdx)
          );

          const autoCommit = get(configStore.config).task_loop_auto_commit;
          if (autoCommit) {
            activePhases = { ...activePhases, [taskIdx]: "waiting_for_auto_commit" };
            commitEverStartedMap = { ...commitEverStartedMap, [taskIdx]: false };
            void sendAutoCommitPrompt(currentTask, taskIdx);
          } else {
            activePhases = Object.fromEntries(
              Object.entries(activePhases).filter(([k]) => Number(k) !== taskIdx)
            );
            void onTaskCompleted(taskIdx, "completed");
          }
        }
      }

      if (phase === "waiting_for_auto_commit") {
        if (workingStates.includes(conv.characterState)) {
          commitEverStartedMap = { ...commitEverStartedMap, [taskIdx]: true };
        }
        if (commitEverStartedMap[taskIdx] && conv.characterState === "idle") {
          activePhases = Object.fromEntries(
            Object.entries(activePhases).filter(([k]) => Number(k) !== taskIdx)
          );
          commitEverStartedMap = Object.fromEntries(
            Object.entries(commitEverStartedMap).filter(([k]) => Number(k) !== taskIdx)
          );
          void onTaskCompleted(taskIdx, "completed");
        }
      }
    }
  });

  async function sendTaskPrompt(task: TaskLoopTask, taskIdx: number, total: number): Promise<void> {
    const prompt = buildTaskPrompt(task, taskIdx + 1, total);
    try {
      await invoke("send_prompt", {
        conversationId: task.conversationId,
        message: prompt,
      });
    } catch (error) {
      console.error("Failed to send task prompt:", error);
      activePhases = Object.fromEntries(
        Object.entries(activePhases).filter(([k]) => Number(k) !== taskIdx)
      );
      void onTaskCompleted(taskIdx, "failed");
    }
  }

  async function sendAutoCommitPrompt(task: TaskLoopTask, taskIdx: number): Promise<void> {
    const cfg = get(configStore.config);
    const prompt = buildAutoCommitPrompt(
      task,
      cfg.task_loop_commit_prefix || "feat",
      cfg.task_loop_include_summary,
      sessionTimestamp
    );
    try {
      await invoke("send_prompt", {
        conversationId: task.conversationId,
        message: prompt,
      });
    } catch (error) {
      console.error("Failed to send auto-commit prompt:", error);
      // Non-blocking: still mark task as completed even if commit prompt fails
      activePhases = Object.fromEntries(
        Object.entries(activePhases).filter(([k]) => Number(k) !== taskIdx)
      );
      void onTaskCompleted(taskIdx, "completed");
    }
  }

  async function onTaskCompleted(taskIdx: number, status: "completed" | "failed"): Promise<void> {
    taskLoopStore.setTaskStatus(taskIdx, status);

    const currentLoopStatus = get(taskLoopStore.loopStatus);
    if (currentLoopStatus !== "running") return;

    // If any tasks are still active, wait for them
    if (Object.keys(activePhases).length > 0) return;

    await advanceToNextWave();
  }

  async function advanceToNextWave(): Promise<void> {
    const currentLoopStatus = get(taskLoopStore.loopStatus);
    if (currentLoopStatus !== "running") return;

    // Mark any newly-blocked tasks
    const taskList = get(taskLoopStore.tasks);
    taskList.forEach((task, i) => {
      if (task.status === "pending" && isTaskBlocked(task, taskList)) {
        taskLoopStore.setTaskStatus(i, "blocked");
      }
    });

    const updatedTaskList = get(taskLoopStore.tasks);
    const limit = get(taskLoopStore.concurrencyLimit);
    const readyIndices = getReadyTasks(updatedTaskList, limit);

    if (readyIndices.length === 0) {
      taskLoopStore.setLoopStatus("stopped");
      return;
    }

    await Promise.all(readyIndices.map((i) => startTask(i, updatedTaskList)));
  }

  async function startTask(taskIdx: number, taskList: TaskLoopTask[]): Promise<void> {
    const task = taskList[taskIdx];
    const cfg = get(configStore.config);
    const allAllowedTools = [
      ...new Set([...get(claudeStore.grantedTools), ...(cfg.auto_granted_tools ?? [])]),
    ];

    const filePath = get(taskLoopStore.sourceFile);
    const workingDir = filePath.split("/").slice(0, -1).join("/");

    const conversationId = claudeStore.createConversation(task.title);
    void claudeStore.switchConversation(conversationId);

    taskLoopStore.setTaskConversationId(taskIdx, conversationId);
    taskLoopStore.setTaskStatus(taskIdx, "running");

    activePhases = { ...activePhases, [taskIdx]: "waiting_for_connection" };
    taskEverStartedMap = { ...taskEverStartedMap, [taskIdx]: false };

    try {
      await invoke("start_claude", {
        conversationId,
        options: {
          working_dir: workingDir,
          model: cfg.model ?? null,
          api_key: cfg.api_key ?? null,
          custom_instructions: (cfg.custom_instructions ?? "") + PROJECT_CONTEXT_SYSTEM_ADDENDUM,
          mcp_servers_json: cfg.mcp_servers_json ?? null,
          allowed_tools: allAllowedTools,
          use_worktree: cfg.use_worktree ?? false,
          disable_1m_context: cfg.disable_1m_context ?? false,
          max_output_tokens: cfg.max_output_tokens ?? null,
        },
      });
    } catch (error) {
      console.error("Failed to start Claude for task:", error);
      activePhases = Object.fromEntries(
        Object.entries(activePhases).filter(([k]) => Number(k) !== taskIdx)
      );
      void onTaskCompleted(taskIdx, "failed");
    }
  }

  async function handleImportFile(): Promise<void> {
    const selected = await open({
      title: "Select hikari-tasks.json",
      filters: [{ name: "Hikari Tasks", extensions: ["json"] }],
      multiple: false,
    });
    if (!selected || typeof selected !== "string") return;

    isLoading = true;
    errorMessage = null;
    try {
      await taskLoopStore.loadFile(normalizeToUnixPath(selected));
    } catch (error) {
      errorMessage = `Failed to load file: ${error instanceof Error ? error.message : String(error)}`;
    } finally {
      isLoading = false;
    }
  }

  async function handleStart(): Promise<void> {
    const taskList = get(taskLoopStore.tasks);
    const limit = get(taskLoopStore.concurrencyLimit);
    const readyIndices = getReadyTasks(taskList, limit);
    if (readyIndices.length === 0) return;

    sessionTimestamp = new Date().toISOString();
    taskLoopStore.setLoopStatus("running");
    await Promise.all(readyIndices.map((i) => startTask(i, taskList)));
  }

  function handlePause(): void {
    taskLoopStore.setLoopStatus("paused");
  }

  async function handleResume(): Promise<void> {
    taskLoopStore.setLoopStatus("running");
    if (Object.keys(activePhases).length === 0) {
      await advanceToNextWave();
    }
  }

  async function handleStop(): Promise<void> {
    taskLoopStore.setLoopStatus("stopped");

    // Stop all active Claude processes
    const taskList = get(taskLoopStore.tasks);
    const stopPromises = Object.keys(activePhases).map(async (idxStr) => {
      const taskIdx = Number(idxStr);
      const task = taskList[taskIdx];
      if (task?.conversationId) {
        try {
          await invoke("stop_claude", { conversationId: task.conversationId });
        } catch (error) {
          console.error("Failed to stop Claude for task:", error);
        }
        if (task.status === "running") {
          taskLoopStore.setTaskStatus(taskIdx, "failed");
        }
      }
    });
    await Promise.all(stopPromises);

    activePhases = {};
    taskEverStartedMap = {};
    commitEverStartedMap = {};
  }

  function handleReset(): void {
    taskLoopStore.reset();
    activePhases = {};
    taskEverStartedMap = {};
    commitEverStartedMap = {};
    errorMessage = null;
    sessionTimestamp = "";
  }

  function statusColour(status: TaskLoopTask["status"]): string {
    switch (status) {
      case "pending":
        return "text-[var(--text-tertiary)]";
      case "running":
        return "text-blue-400";
      case "completed":
        return "text-green-400";
      case "failed":
        return "text-red-400";
      case "blocked":
        return "text-[var(--text-tertiary)] opacity-50";
    }
  }

  function statusIcon(status: TaskLoopTask["status"]): string {
    switch (status) {
      case "pending":
        return "○";
      case "running":
        return "⟳";
      case "completed":
        return "✓";
      case "failed":
        return "✗";
      case "blocked":
        return "⊘";
    }
  }

  function priorityColour(priority: TaskLoopTask["priority"]): string {
    switch (priority) {
      case "high":
        return "bg-red-500/20 text-red-400 border-red-500/30";
      case "medium":
        return "bg-amber-500/20 text-amber-400 border-amber-500/30";
      case "low":
        return "bg-green-500/20 text-green-400 border-green-500/30";
    }
  }

  const hasPendingTasks = $derived($tasks.some((t) => t.status === "pending"));

  async function toggleAutoCommit(): Promise<void> {
    await configStore.updateConfig({ task_loop_auto_commit: !$config.task_loop_auto_commit });
  }

  async function toggleIncludeSummary(): Promise<void> {
    await configStore.updateConfig({
      task_loop_include_summary: !$config.task_loop_include_summary,
    });
  }

  async function updateCommitPrefix(value: string): Promise<void> {
    await configStore.updateConfig({ task_loop_commit_prefix: value });
  }
</script>

<div
  class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
  onclick={onClose}
  role="button"
  tabindex="0"
  onkeydown={(e) => e.key === "Escape" && onClose()}
>
  <div
    class="bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] flex flex-col"
    onclick={(e) => e.stopPropagation()}
    onkeydown={(e) => e.stopPropagation()}
    role="dialog"
    aria-labelledby="task-loop-panel-title"
    tabindex="-1"
  >
    <!-- Header -->
    <div class="flex items-center justify-between p-6 pb-4 border-b border-[var(--border-color)]">
      <div class="flex items-center gap-3">
        <h2 id="task-loop-panel-title" class="text-xl font-semibold text-[var(--text-primary)]">
          Task Loop
        </h2>
        {#if $loopStatus === "running"}
          <span
            class="text-xs px-2 py-0.5 rounded-full bg-blue-500/20 text-blue-400 border border-blue-500/30 animate-pulse"
          >
            {runningCount} running · {completedCount}/{totalCount} done
          </span>
        {:else if $loopStatus === "paused"}
          <span
            class="text-xs px-2 py-0.5 rounded-full bg-amber-500/20 text-amber-400 border border-amber-500/30"
          >
            Paused
          </span>
        {:else if $loopStatus === "stopped" && totalCount > 0}
          <span
            class="text-xs px-2 py-0.5 rounded-full bg-[var(--bg-secondary)] text-[var(--text-tertiary)] border border-[var(--border-color)]"
          >
            {completedCount}/{totalCount} completed{failedCount > 0
              ? `, ${failedCount} failed`
              : ""}{blockedCount > 0 ? `, ${blockedCount} blocked` : ""}
          </span>
        {/if}
      </div>
      <div class="flex items-center gap-2">
        {#if onBackToWorkflow}
          <button
            onclick={onBackToWorkflow}
            class="px-2 py-1 text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded-md transition-colors"
          >
            ← Workflow
          </button>
        {/if}
        <button
          onclick={() => (showSettings = !showSettings)}
          class="p-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
          aria-label="Toggle settings"
          aria-pressed={showSettings}
        >
          <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="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
            />
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
            />
          </svg>
        </button>
        <button
          onclick={onClose}
          class="p-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
          aria-label="Close"
        >
          <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>
    </div>

    <!-- Settings panel (collapsible) -->
    {#if showSettings}
      <div
        class="px-6 py-4 border-b border-[var(--border-color)] bg-[var(--bg-secondary)] flex flex-col gap-3"
      >
        <p class="text-xs font-semibold text-[var(--text-tertiary)] uppercase tracking-wide">
          Auto-commit Settings
        </p>

        <!-- Auto-commit toggle -->
        <label class="flex items-center gap-3 cursor-pointer">
          <div
            class="relative w-9 h-5 rounded-full transition-colors {$config.task_loop_auto_commit
              ? 'bg-[var(--accent-primary)]'
              : 'bg-[var(--bg-tertiary)] border border-[var(--border-color)]'}"
          >
            <div
              class="absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform {$config.task_loop_auto_commit
                ? 'left-4'
                : 'left-0.5'}"
            ></div>
            <input
              type="checkbox"
              class="sr-only"
              checked={$config.task_loop_auto_commit}
              onchange={toggleAutoCommit}
            />
          </div>
          <span class="text-sm text-[var(--text-primary)]">Auto-commit on task completion</span>
        </label>

        {#if $config.task_loop_auto_commit}
          <!-- Commit prefix -->
          <div class="flex items-center gap-3">
            <label
              class="text-sm text-[var(--text-secondary)] shrink-0 w-28"
              for="commit-prefix-input"
            >
              Commit prefix
            </label>
            <input
              id="commit-prefix-input"
              type="text"
              value={$config.task_loop_commit_prefix}
              onchange={(e) => updateCommitPrefix((e.target as HTMLInputElement).value)}
              placeholder="feat"
              class="flex-1 px-2 py-1 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent-primary)]"
            />
            <span class="text-xs text-[var(--text-tertiary)]">: task title</span>
          </div>

          <!-- Include SUMMARY.md toggle -->
          <label class="flex items-center gap-3 cursor-pointer">
            <div
              class="relative w-9 h-5 rounded-full transition-colors {$config.task_loop_include_summary
                ? 'bg-[var(--accent-primary)]'
                : 'bg-[var(--bg-tertiary)] border border-[var(--border-color)]'}"
            >
              <div
                class="absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform {$config.task_loop_include_summary
                  ? 'left-4'
                  : 'left-0.5'}"
              ></div>
              <input
                type="checkbox"
                class="sr-only"
                checked={$config.task_loop_include_summary}
                onchange={toggleIncludeSummary}
              />
            </div>
            <span class="text-sm text-[var(--text-primary)]">Generate SUMMARY.md before commit</span
            >
          </label>
        {/if}
      </div>
    {/if}

    <!-- Body -->
    <div class="flex-1 overflow-y-auto p-4 min-h-0">
      {#if isLoading}
        <div class="flex items-center justify-center py-16 gap-3 text-[var(--text-secondary)]">
          <div class="animate-spin text-2xl">⚙️</div>
          <span class="text-sm">Loading tasks...</span>
        </div>
      {:else if errorMessage}
        <div class="flex flex-col items-center justify-center py-16 gap-4 text-center">
          <p class="text-sm text-red-400">{errorMessage}</p>
          <button
            onclick={handleImportFile}
            class="px-4 py-1.5 text-sm btn-trans-gradient rounded-lg transition-colors"
          >
            Try Again
          </button>
        </div>
      {:else if totalCount === 0}
        <!-- Empty state -->
        <div class="flex flex-col items-center justify-center py-16 gap-4 text-center">
          <div class="text-4xl">📋</div>
          <h3 class="text-lg font-semibold text-[var(--text-primary)]">No Tasks Loaded</h3>
          <p class="text-sm text-[var(--text-secondary)] max-w-sm">
            Import a <span class="font-mono text-xs">hikari-tasks.json</span> file created by the PRD
            Creator to run tasks automatically.
          </p>
          <button
            onclick={handleImportFile}
            class="px-4 py-1.5 text-sm btn-trans-gradient rounded-lg transition-colors"
          >
            Import hikari-tasks.json
          </button>
        </div>
      {:else}
        <!-- Source file path -->
        <div class="text-xs text-[var(--text-tertiary)] font-mono mb-3 truncate">
          {$sourceFile}
        </div>

        <!-- Wave-grouped task list -->
        <div class="flex flex-col gap-4">
          {#each waves as waveIndices, waveIdx (waveIdx)}
            <div>
              {#if multiWave}
                <div class="flex items-center gap-2 mb-2">
                  <span
                    class="text-xs font-semibold text-[var(--text-tertiary)] uppercase tracking-wide"
                  >
                    Wave {waveIdx + 1}
                  </span>
                  {#if waveIndices.length > 1}
                    <span class="text-xs text-[var(--text-tertiary)]">
                      ({waveIndices.length} parallel)
                    </span>
                  {/if}
                  <div class="flex-1 border-t border-[var(--border-color)]"></div>
                </div>
              {/if}
              <div class="flex flex-col gap-2">
                {#each waveIndices as taskIdx (taskIdx)}
                  {@const task = $tasks[taskIdx]}
                  {#if task}
                    <div
                      class="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg p-3 flex items-start gap-3 {task.status ===
                      'running'
                        ? 'border-blue-500/40 bg-blue-500/5'
                        : task.status === 'blocked'
                          ? 'opacity-50'
                          : ''}"
                    >
                      <!-- Status icon -->
                      <span
                        class="text-sm font-mono mt-0.5 w-4 text-center shrink-0 {statusColour(
                          task.status
                        )} {task.status === 'running' ? 'animate-spin' : ''}"
                      >
                        {statusIcon(task.status)}
                      </span>
                      <!-- Task info -->
                      <div class="flex-1 min-w-0">
                        <div class="flex items-center gap-2 flex-wrap">
                          <span class="text-sm font-medium text-[var(--text-primary)] truncate">
                            {task.title}
                          </span>
                          <span
                            class="text-xs px-1.5 py-0.5 rounded-full border shrink-0 {priorityColour(
                              task.priority
                            )}"
                          >
                            {task.priority}
                          </span>
                          {#if task.status === "running"}
                            {#if activePhases[taskIdx] === "waiting_for_auto_commit"}
                              <span class="text-xs text-violet-400 animate-pulse shrink-0"
                                >● committing</span
                              >
                            {:else}
                              <span class="text-xs text-blue-400 animate-pulse shrink-0"
                                >● running</span
                              >
                            {/if}
                          {:else if task.status === "blocked"}
                            <span class="text-xs text-[var(--text-tertiary)] shrink-0">blocked</span
                            >
                          {/if}
                        </div>
                        <p
                          class="text-xs text-[var(--text-tertiary)] mt-0.5 line-clamp-2 font-mono"
                        >
                          {task.prompt}
                        </p>
                      </div>
                      <!-- Task number -->
                      <span class="text-xs text-[var(--text-tertiary)] font-mono shrink-0"
                        >#{taskIdx + 1}</span
                      >
                    </div>
                  {/if}
                {/each}
              </div>
            </div>
          {/each}
        </div>
      {/if}
    </div>

    <!-- Footer -->
    <div
      class="flex items-center justify-between p-4 pt-2 border-t border-[var(--border-color)] gap-3"
    >
      <div class="flex items-center gap-2">
        {#if totalCount > 0 && $loopStatus === "idle"}
          <button
            onclick={handleImportFile}
            class="px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded-lg transition-colors"
          >
            Change File
          </button>
          <button
            onclick={handleReset}
            class="px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded-lg transition-colors"
          >
            Reset
          </button>
        {:else if $loopStatus === "stopped"}
          <button
            onclick={handleReset}
            class="px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded-lg transition-colors"
          >
            Reset
          </button>
        {/if}

        <!-- Concurrency limit control -->
        {#if totalCount > 0}
          <div class="flex items-center gap-1 ml-2">
            <span class="text-xs text-[var(--text-tertiary)]">Parallel:</span>
            <button
              onclick={() => taskLoopStore.setConcurrencyLimit($concurrencyLimit - 1)}
              class="w-5 h-5 flex items-center justify-center text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded transition-colors"
              aria-label="Decrease concurrency limit"
            >
              −
            </button>
            <span class="text-xs font-mono text-[var(--text-primary)] w-4 text-center"
              >{$concurrencyLimit}</span
            >
            <button
              onclick={() => taskLoopStore.setConcurrencyLimit($concurrencyLimit + 1)}
              class="w-5 h-5 flex items-center justify-center text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded transition-colors"
              aria-label="Increase concurrency limit"
            >
              +
            </button>
          </div>
        {/if}
      </div>

      <div class="flex items-center gap-2">
        {#if totalCount === 0}
          <!-- no actions until tasks are loaded -->
        {:else if $loopStatus === "idle" || $loopStatus === "stopped"}
          {#if hasPendingTasks}
            <button
              onclick={handleStart}
              class="px-4 py-1.5 text-sm btn-trans-gradient rounded-lg transition-colors"
            >
              Start Loop
            </button>
          {:else}
            <span class="text-xs text-[var(--text-tertiary)]">All tasks complete</span>
          {/if}
        {:else if $loopStatus === "running"}
          <button
            onclick={handlePause}
            class="px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)] border border-[var(--border-color)] rounded-lg transition-colors"
          >
            Pause
          </button>
          <button
            onclick={handleStop}
            class="px-3 py-1.5 text-sm text-red-400 hover:text-red-300 bg-red-500/10 hover:bg-red-500/20 border border-red-500/30 rounded-lg transition-colors"
          >
            Stop
          </button>
        {:else if $loopStatus === "paused"}
          <button
            onclick={handleResume}
            class="px-4 py-1.5 text-sm btn-trans-gradient rounded-lg transition-colors"
          >
            Resume
          </button>
          <button
            onclick={handleStop}
            class="px-3 py-1.5 text-sm text-red-400 hover:text-red-300 bg-red-500/10 hover:bg-red-500/20 border border-red-500/30 rounded-lg transition-colors"
          >
            Stop
          </button>
        {/if}
      </div>
    </div>
  </div>
</div>

<style>
  [role="dialog"] {
    animation: slideIn 0.2s ease-out;
  }

  @keyframes slideIn {
    from {
      opacity: 0;
      transform: scale(0.95) translateY(10px);
    }
    to {
      opacity: 1;
      transform: scale(1) translateY(0);
    }
  }

  .line-clamp-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
</style>
