<script lang="ts">
  import { api } from "../lib/api";
  import {
    agents,
    testCases,
    testCaseRecords,
    currentAgentId,
    currentAgent,
    agentGraph,
    isRunning,
    currentView,
    startRun,
    refreshAgent,
  } from "../lib/stores";
  import type { TestCase, TestCaseRecord } from "../lib/types";
  import ListPanel from "./ListPanel.svelte";
  import Modal from "./Modal.svelte";

  let newTest = $state<Partial<TestCase>>({
    name: "",
    user_prompt: "",
    metrics: [],
    dynamic_variables: {},
    tool_mocks: [],
    type: "llm",
    llm_model: undefined,
    includes: [],
    excludes: [],
    patterns: [],
  });
  let metricInput = $state("");
  let includeInput = $state("");
  let excludeInput = $state("");
  let patternInput = $state("");
  let editingId = $state<string | null>(null);
  let jsonImport = $state("");
  let importError = $state("");
  let saving = $state(false);
  let showNewTestModal = $state(false);
  let showImportModal = $state(false);
  let showExportModal = $state(false);
  let selectedTestIds = $state<string[]>([]);
  let runError = $state("");
  let exportFormat = $state("retell");
  let exportSelection = $state<"all" | "selected">("all");
  let exporting = $state(false);
  let exportError = $state("");

  // Import modal tab state
  let showJsonEditor = $state(false);
  let importTab = $state<"import" | "link">("import");
  let linkPath = $state("");
  let linkError = $state("");
  let linking = $state(false);

  // Import progress tracking
  let importProgress = $state<{ current: number; total: number } | null>(null);

  // Sorting state
  type SortColumn = "index" | "id" | "name" | "type" | "prompt";
  type SortDirection = "asc" | "desc";
  let sortColumn = $state<SortColumn>("index");
  let sortDirection = $state<SortDirection>("asc");

  let sortedRecords = $derived.by(() => {
    const records = [...$testCaseRecords];
    records.sort((a, b) => {
      if (sortColumn === "index") {
        const aIdx = a.source_index ?? Number.MAX_SAFE_INTEGER;
        const bIdx = b.source_index ?? Number.MAX_SAFE_INTEGER;
        const cmp = aIdx - bIdx;
        return sortDirection === "asc" ? cmp : -cmp;
      }
      let aVal: string;
      let bVal: string;
      switch (sortColumn) {
        case "id":
          aVal = a.id;
          bVal = b.id;
          break;
        case "name":
          aVal = a.name.toLowerCase();
          bVal = b.name.toLowerCase();
          break;
        case "type":
          aVal = a.type || "";
          bVal = b.type || "";
          break;
        case "prompt":
          aVal = a.user_prompt.toLowerCase();
          bVal = b.user_prompt.toLowerCase();
          break;
        default:
          return 0;
      }
      const cmp = aVal.localeCompare(bVal);
      return sortDirection === "asc" ? cmp : -cmp;
    });
    return records;
  });

  function handleSort(column: SortColumn) {
    if (sortColumn === column) {
      sortDirection = sortDirection === "asc" ? "desc" : "asc";
    } else {
      sortColumn = column;
      sortDirection = "asc";
    }
  }

  function handleKeydown(event: KeyboardEvent) {
    if (event.key === "Escape") {
      if (showNewTestModal) {
        closeNewTestModal();
      } else if (showImportModal) {
        closeImportModal();
      } else if (showExportModal) {
        closeExportModal();
      }
    }
  }

  function safeJsonParse<T>(value: string | T | null | undefined, fallback: T): T {
    if (value === null || value === undefined) return fallback;
    if (typeof value !== "string") return value;
    try {
      return JSON.parse(value);
    } catch {
      console.warn(`Failed to parse JSON: ${value.slice(0, 50)}`);
      return fallback;
    }
  }

  function parseRecord(record: TestCaseRecord): TestCase {
    return {
      name: record.name,
      user_prompt: record.user_prompt,
      metrics: safeJsonParse(record.metrics, []),
      dynamic_variables: safeJsonParse(record.dynamic_variables, {}),
      tool_mocks: safeJsonParse(record.tool_mocks, []),
      type: record.type || "llm",
      llm_model: record.llm_model ?? undefined,
      includes: safeJsonParse(record.includes, []),
      excludes: safeJsonParse(record.excludes, []),
      patterns: safeJsonParse(record.patterns, []),
    };
  }

  async function refreshTests() {
    if (!$currentAgentId) return;
    const records = await api.listTestsForAgent($currentAgentId);
    testCaseRecords.set(records);
    testCases.set(records.map(parseRecord));
  }

  function addMetric() {
    if (metricInput.trim()) {
      newTest.metrics = [...(newTest.metrics || []), metricInput.trim()];
      metricInput = "";
    }
  }

  function removeMetric(index: number) {
    newTest.metrics = newTest.metrics?.filter((_, i) => i !== index) || [];
  }

  function addInclude() {
    if (includeInput.trim()) {
      newTest.includes = [...(newTest.includes || []), includeInput.trim()];
      includeInput = "";
    }
  }

  function removeInclude(index: number) {
    newTest.includes = newTest.includes?.filter((_, i) => i !== index) || [];
  }

  function addExclude() {
    if (excludeInput.trim()) {
      newTest.excludes = [...(newTest.excludes || []), excludeInput.trim()];
      excludeInput = "";
    }
  }

  function removeExclude(index: number) {
    newTest.excludes = newTest.excludes?.filter((_, i) => i !== index) || [];
  }

  function addPattern() {
    if (patternInput.trim()) {
      newTest.patterns = [...(newTest.patterns || []), patternInput.trim()];
      patternInput = "";
    }
  }

  function removePattern(index: number) {
    newTest.patterns = newTest.patterns?.filter((_, i) => i !== index) || [];
  }

  async function saveTest() {
    if (!newTest.name || !newTest.user_prompt || !$currentAgentId) return;

    saving = true;
    try {
      const testData = {
        name: newTest.name,
        user_prompt: newTest.user_prompt,
        metrics: newTest.metrics || [],
        dynamic_variables: newTest.dynamic_variables || {},
        tool_mocks: newTest.tool_mocks || [],
        type: newTest.type || "llm",
        llm_model: newTest.llm_model,
        includes: newTest.includes || [],
        excludes: newTest.excludes || [],
        patterns: newTest.patterns || [],
      };

      if (editingId) {
        await api.updateTestCase(editingId, testData);
      } else {
        await api.createTestCase($currentAgentId, testData);
      }

      await refreshTests();
      closeNewTestModal();
    } catch (e) {
      importError = e instanceof Error ? e.message : String(e);
    }
    saving = false;
  }

  function editTest(record: TestCaseRecord) {
    newTest = parseRecord(record);
    editingId = record.id;
    showNewTestModal = true;
  }

  async function deleteTest(id: string) {
    if (!confirm("Are you sure you want to delete this test?")) return;
    try {
      await api.deleteTestCase(id);
      await refreshTests();
      selectedTestIds = selectedTestIds.filter((x) => x !== id);
    } catch (e) {
      importError = e instanceof Error ? e.message : String(e);
    }
  }

  async function deleteSelectedTests() {
    if (selectedTestIds.length === 0) return;
    if (!confirm(`Are you sure you want to delete ${selectedTestIds.length} tests?`)) return;

    const idsToDelete = [...selectedTestIds];
    for (const id of idsToDelete) {
      try {
        await api.deleteTestCase(id);
      } catch (e) {
        console.error("Failed to delete test:", id, e);
      }
    }
    selectedTestIds = [];
    await refreshTests();
  }

  function resetForm() {
    newTest = {
      name: "",
      user_prompt: "",
      metrics: [],
      dynamic_variables: {},
      tool_mocks: [],
      type: "llm",
      llm_model: undefined,
      includes: [],
      excludes: [],
      patterns: [],
    };
    editingId = null;
    importError = "";
  }

  function openNewTestModal() {
    resetForm();
    showNewTestModal = true;
  }

  function closeNewTestModal() {
    showNewTestModal = false;
    resetForm();
  }

  function openImportModal() {
    jsonImport = "";
    importError = "";
    importTab = "import";
    showJsonEditor = false;
    linkPath = "";
    linkError = "";
    showImportModal = true;
  }

  function closeImportModal() {
    showImportModal = false;
    jsonImport = "";
    importError = "";
    linkPath = "";
    linkError = "";
  }

  function normalizeTestType(type: string | undefined): string {
    if (type === "simulation") return "llm";
    if (type === "unit") return "rule";
    return type || "llm";
  }

  async function importFromJson() {
    if (!$currentAgentId) return;

    importError = "";
    try {
      const parsed = JSON.parse(jsonImport);
      const tests = Array.isArray(parsed) ? parsed : [parsed];

      // Close modal immediately and start import with progress
      closeImportModal();
      await importTestsWithProgress(tests);
    } catch (e) {
      importError = e instanceof Error ? e.message : String(e);
    }
  }

  async function importTestsWithProgress(tests: Record<string, unknown>[]) {
    if (!$currentAgentId) return;

    importProgress = { current: 0, total: tests.length };

    for (const test of tests) {
      try {
        await api.createTestCase($currentAgentId, {
          name: test.name as string,
          user_prompt: test.user_prompt as string,
          metrics: (test.metrics as string[]) || [],
          dynamic_variables: (test.dynamic_variables as Record<string, string>) || {},
          tool_mocks: (test.tool_mocks as Record<string, unknown>[]) || [],
          type: normalizeTestType(test.type as string),
          llm_model: test.llm_model as string | undefined,
          includes: (test.includes as string[]) || [],
          excludes: (test.excludes as string[]) || [],
          patterns: (test.patterns as string[]) || [],
        });
        // Refresh after each test to show incremental progress
        await refreshTests();
      } catch (e) {
        console.error("Failed to import test:", test.name, e);
      }
      importProgress = { current: importProgress.current + 1, total: tests.length };
    }

    importProgress = null;
  }

  async function handleFileUpload(event: Event) {
    const input = event.target as HTMLInputElement;
    const file = input.files?.[0];
    if (!file) return;

    importError = "";

    try {
      const content = await file.text();
      // Just load into textarea, don't auto-import
      jsonImport = content;
      input.value = "";
    } catch (e) {
      importError = e instanceof Error ? e.message : String(e);
    }
  }

  function toggleTestSelection(id: string) {
    if (selectedTestIds.includes(id)) {
      selectedTestIds = selectedTestIds.filter((x) => x !== id);
    } else {
      selectedTestIds = [...selectedTestIds, id];
    }
  }

  function selectAllTests() {
    if (selectedTestIds.length === $testCaseRecords.length && $testCaseRecords.length > 0) {
      selectedTestIds = [];
    } else {
      selectedTestIds = $testCaseRecords.map((r) => r.id);
    }
  }

  function truncatePrompt(prompt: string, maxLength: number = 150): string {
    if (prompt.length <= maxLength) return prompt;
    return prompt.substring(0, maxLength) + "...";
  }

  async function runAllTests() {
    if (!$currentAgentId || $testCases.length === 0) return;

    runError = "";
    currentView.set("runs");

    try {
      await startRun($currentAgentId);
    } catch (e) {
      runError = e instanceof Error ? e.message : String(e);
    }
  }

  async function runSelectedTests() {
    if (!$currentAgentId || selectedTestIds.length === 0) return;

    currentView.set("runs");
    runError = "";

    try {
      await startRun($currentAgentId, selectedTestIds);
    } catch (e) {
      runError = e instanceof Error ? e.message : String(e);
    }
  }

  function openExportModal() {
    exportFormat = "retell";
    exportSelection = "all";
    exportError = "";
    showExportModal = true;
  }

  function closeExportModal() {
    showExportModal = false;
    exportError = "";
  }

  async function doExport() {
    if (!$currentAgentId) return;

    exporting = true;
    exportError = "";

    try {
      const testIds = exportSelection === "selected" ? selectedTestIds : undefined;
      const data = await api.exportTests($currentAgentId, exportFormat, testIds);

      const agentName = $currentAgent?.name || "tests";
      const safeName = agentName.replace(/[^a-zA-Z0-9_-]/g, "_");
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `${safeName}_tests_${exportFormat}.json`;
      a.click();
      URL.revokeObjectURL(url);

      closeExportModal();
    } catch (e) {
      exportError = e instanceof Error ? e.message : String(e);
    }

    exporting = false;
  }

  async function linkFile() {
    if (!$currentAgentId || !linkPath.trim()) return;

    linking = true;
    linkError = "";

    try {
      await api.linkTestFile($currentAgentId, linkPath.trim());

      // Refresh agent record (for tests_paths) and tests list
      const updatedAgent = await api.getAgent($currentAgentId);
      agents.update((list) =>
        list.map((a) => (a.id === updatedAgent.id ? updatedAgent : a)),
      );
      await refreshAgent($currentAgentId);
      closeImportModal();
    } catch (e) {
      linkError = e instanceof Error ? e.message : String(e);
    }

    linking = false;
  }

  async function unlinkFile(filePath: string) {
    if (!$currentAgentId) return;
    if (!confirm(`Unlink "${filePath.split("/").pop()}"? The file will not be deleted.`)) return;

    try {
      await api.unlinkTestFile($currentAgentId, filePath);

      const updatedAgent = await api.getAgent($currentAgentId);
      agents.update((list) =>
        list.map((a) => (a.id === updatedAgent.id ? updatedAgent : a)),
      );
      await refreshAgent($currentAgentId);
    } catch (e) {
      console.error("Failed to unlink file:", e);
    }
  }

  function basename(filePath: string): string {
    return filePath.split("/").pop() || filePath;
  }
</script>

<svelte:window onkeydown={handleKeydown} />

<div class="tests-view">
  <div class="header">
    <h2>Tests for {$currentAgent?.name}</h2>
    {#if $currentAgentId}
      <div class="header-actions">
        <button
          class="run-btn"
          onclick={runAllTests}
          disabled={$isRunning || !$agentGraph || $testCases.length === 0}
        >
          {$isRunning ? "Running..." : "Run All"}
        </button>
        <button
          class="run-btn secondary"
          onclick={runSelectedTests}
          disabled={$isRunning || !$agentGraph || selectedTestIds.length === 0}
        >
          Run Selected ({selectedTestIds.length})
        </button>
        <button class="secondary" onclick={openNewTestModal}>+ New Test</button>
        <button class="secondary" onclick={openImportModal}>Import</button>
        <button
          class="secondary"
          onclick={openExportModal}
          disabled={$testCaseRecords.length === 0}
        >Export</button>
        {#if selectedTestIds.length > 1}
          <button class="danger" onclick={deleteSelectedTests}>
            Delete ({selectedTestIds.length})
          </button>
        {/if}
      </div>
    {/if}
  </div>

  {#if importProgress}
    <div class="import-progress">
      <div class="progress-text">
        Importing tests... {importProgress.current} / {importProgress.total}
      </div>
      <div class="progress-bar">
        <div
          class="progress-fill"
          style="width: {(importProgress.current / importProgress.total) * 100}%"
        ></div>
      </div>
    </div>
  {/if}

  {#if !$currentAgentId}
    <p class="placeholder">No agent selected.</p>
  {:else}
    <div class="layout">
      {#if $currentAgent?.tests_paths && $currentAgent.tests_paths.length > 0}
        <section class="linked-files-section">
          <h3 class="linked-files-heading">Linked Files</h3>
          {#each $currentAgent.tests_paths as filePath}
            <div class="linked-file-row">
              <div class="linked-file-info">
                <span class="linked-file-name">{basename(filePath)}</span>
                <span class="linked-file-path">{filePath}</span>
              </div>
              <button class="small danger" onclick={() => unlinkFile(filePath)}>Unlink</button>
            </div>
          {/each}
        </section>
      {/if}

      <ListPanel>
        {#if $testCaseRecords.length === 0}
          <p class="empty">No test cases yet. Click "New Test" or "Import" to add tests.</p>
        {:else}
          <table class="test-table">
            <thead>
              <tr>
                <th class="col-select">
                  <input
                    type="checkbox"
                    checked={selectedTestIds.length === $testCaseRecords.length && $testCaseRecords.length > 0}
                    onchange={selectAllTests}
                  />
                </th>
                <th class="col-index sortable" onclick={() => handleSort("index")}>
                  #
                  {#if sortColumn === "index"}
                    <span class="sort-indicator">{sortDirection === "asc" ? "▲" : "▼"}</span>
                  {/if}
                </th>
                <th class="col-name sortable" onclick={() => handleSort("name")}>
                  Name
                  {#if sortColumn === "name"}
                    <span class="sort-indicator">{sortDirection === "asc" ? "▲" : "▼"}</span>
                  {/if}
                </th>
                <th class="col-type sortable" onclick={() => handleSort("type")}>
                  Type
                  {#if sortColumn === "type"}
                    <span class="sort-indicator">{sortDirection === "asc" ? "▲" : "▼"}</span>
                  {/if}
                </th>
                <th class="col-prompt sortable" onclick={() => handleSort("prompt")}>
                  Prompt
                  {#if sortColumn === "prompt"}
                    <span class="sort-indicator">{sortDirection === "asc" ? "▲" : "▼"}</span>
                  {/if}
                </th>
                <th class="col-actions">Actions</th>
              </tr>
            </thead>
            <tbody>
              {#each sortedRecords as record}
                <tr class:selected={selectedTestIds.includes(record.id)}>
                  <td class="col-select">
                    <input
                      type="checkbox"
                      checked={selectedTestIds.includes(record.id)}
                      onchange={() => toggleTestSelection(record.id)}
                    />
                  </td>
                  <td class="col-index">{record.source_index ?? "—"}</td>
                  <td class="col-name">
                    {record.name}
                    {#if record.source_path}
                      <span class="badge-linked">linked</span>
                    {/if}
                  </td>
                  <td class="col-type">
                    <span class="tag {record.type}">{record.type}</span>
                  </td>
                  <td class="col-prompt">
                    <span class="prompt-preview">{truncatePrompt(record.user_prompt)}</span>
                  </td>
                  <td class="col-actions">
                    <button class="small" onclick={() => editTest(record)}>Edit</button>
                    <button class="small danger" onclick={() => deleteTest(record.id)}>Delete</button>
                  </td>
                </tr>
              {/each}
            </tbody>
          </table>
        {/if}

        {#if runError}
          <p class="error-message">{runError}</p>
        {/if}
      </ListPanel>
    </div>
  {/if}
</div>

<Modal bind:open={showNewTestModal} title={editingId ? "Edit Test" : "New Test"} onclose={closeNewTestModal}>
      <div class="modal-body">
        <div class="form-group">
          <label for="test-name">Name</label>
          <input
            id="test-name"
            type="text"
            bind:value={newTest.name}
            placeholder="Test name"
          />
        </div>

        <div class="form-group">
          <label for="test-prompt">User Prompt</label>
          <textarea
            id="test-prompt"
            bind:value={newTest.user_prompt}
            placeholder="The initial prompt to start the conversation..."
            rows={4}
          ></textarea>
        </div>

        <div class="form-group">
          <label for="test-type">Type</label>
          <select id="test-type" bind:value={newTest.type}>
            <option value="llm">LLM (semantic evaluation)</option>
            <option value="rule">Rule (pattern matching)</option>
          </select>
        </div>

        <div class="form-group">
          <label for="test-llm-model">LLM Model (optional)</label>
          <input
            id="test-llm-model"
            type="text"
            bind:value={newTest.llm_model}
            placeholder="e.g., groq/llama-3.1-8b-instant (leave empty for default)"
          />
          <span class="field-hint">Override the agent model for this specific test</span>
        </div>

        {#if newTest.type === "llm" || newTest.type === "simulation"}
          <div class="form-group">
            <label for="metric-input">Metrics</label>
            <div class="input-row">
              <input
                id="metric-input"
                type="text"
                bind:value={metricInput}
                placeholder="Add evaluation criteria..."
                onkeydown={(e) => e.key === "Enter" && addMetric()}
              />
              <button type="button" onclick={addMetric}>Add</button>
            </div>
            {#if newTest.metrics && newTest.metrics.length > 0}
              <ul class="tag-list metrics">
                {#each newTest.metrics as metric, i}
                  <li>
                    <textarea
                      rows="1"
                      value={metric}
                      oninput={(e) => {
                        if (newTest.metrics) {
                          newTest.metrics[i] = e.currentTarget.value;
                          newTest.metrics = [...newTest.metrics];
                        }
                      }}
                    ></textarea>
                    <button class="small danger" onclick={() => removeMetric(i)}>x</button>
                  </li>
                {/each}
              </ul>
            {/if}
          </div>
        {:else}
          <div class="form-group">
            <label for="include-input">Must Include</label>
            <div class="input-row">
              <input
                id="include-input"
                type="text"
                bind:value={includeInput}
                placeholder="Substring that must be present..."
                onkeydown={(e) => e.key === "Enter" && addInclude()}
              />
              <button type="button" onclick={addInclude}>Add</button>
            </div>
            {#if newTest.includes && newTest.includes.length > 0}
              <ul class="tag-list includes">
                {#each newTest.includes as item, i}
                  <li>
                    <span>{item}</span>
                    <button class="small danger" onclick={() => removeInclude(i)}>x</button>
                  </li>
                {/each}
              </ul>
            {/if}
          </div>

          <div class="form-group">
            <label for="exclude-input">Must Exclude</label>
            <div class="input-row">
              <input
                id="exclude-input"
                type="text"
                bind:value={excludeInput}
                placeholder="Substring that must NOT be present..."
                onkeydown={(e) => e.key === "Enter" && addExclude()}
              />
              <button type="button" onclick={addExclude}>Add</button>
            </div>
            {#if newTest.excludes && newTest.excludes.length > 0}
              <ul class="tag-list excludes">
                {#each newTest.excludes as item, i}
                  <li>
                    <span>{item}</span>
                    <button class="small danger" onclick={() => removeExclude(i)}>x</button>
                  </li>
                {/each}
              </ul>
            {/if}
          </div>

          <div class="form-group">
            <label for="pattern-input">Regex Patterns</label>
            <div class="input-row">
              <input
                id="pattern-input"
                type="text"
                bind:value={patternInput}
                placeholder="Regex pattern to match..."
                onkeydown={(e) => e.key === "Enter" && addPattern()}
              />
              <button type="button" onclick={addPattern}>Add</button>
            </div>
            {#if newTest.patterns && newTest.patterns.length > 0}
              <ul class="tag-list patterns">
                {#each newTest.patterns as item, i}
                  <li>
                    <code>{item}</code>
                    <button class="small danger" onclick={() => removePattern(i)}>x</button>
                  </li>
                {/each}
              </ul>
            {/if}
          </div>
        {/if}

        {#if importError}
          <p class="error-message">{importError}</p>
        {/if}
      </div>
      <div class="modal-footer">
        <button class="secondary" onclick={closeNewTestModal}>Cancel</button>
        <button
          onclick={saveTest}
          disabled={!newTest.name || !newTest.user_prompt || saving}
        >
          {saving ? "Saving..." : editingId ? "Update Test" : "Create Test"}
        </button>
      </div>
</Modal>

<Modal bind:open={showImportModal} title="Add Tests" onclose={closeImportModal}>
      <div class="modal-tabs">
        <button
          class="modal-tab"
          class:active={importTab === "import"}
          onclick={() => { importTab = "import"; }}
        >Import</button>
        <button
          class="modal-tab"
          class:active={importTab === "link"}
          onclick={() => { importTab = "link"; }}
        >Link File</button>
      </div>
      <div class="modal-body">
        {#if importTab === "import"}
          <div class="import-fields">
            <div class="drop-zone">
              <label for="file-input" class="file-label">
                Choose File
                <input
                  id="file-input"
                  type="file"
                  accept=".json,application/json"
                  onchange={handleFileUpload}
                />
              </label>
              {#if jsonImport}
                <span class="file-status">File loaded, {jsonImport.length.toLocaleString()} chars</span>
              {/if}
            </div>
            <button class="link-toggle" onclick={() => showJsonEditor = !showJsonEditor}>
              {showJsonEditor ? "▼" : "▶"} {jsonImport ? "Edit JSON" : "Paste JSON"}
            </button>
            {#if showJsonEditor}
              <textarea
                class="json-input"
                bind:value={jsonImport}
                placeholder="Paste test case JSON (single or array)..."
                rows={10}
              ></textarea>
            {/if}
          </div>
          {#if importError}
            <p class="error-message">{importError}</p>
          {/if}
        {:else}
          <div class="form-group">
            <label for="link-path">Server File Path</label>
            <input
              id="link-path"
              type="text"
              bind:value={linkPath}
              placeholder="/path/to/tests.json"
            />
            <span class="field-hint">
              Absolute path to a JSON test file on the server. Tests stay in sync — edits write back to the file.
            </span>
          </div>
          {#if linkError}
            <p class="error-message">{linkError}</p>
          {/if}
        {/if}
      </div>
      <div class="modal-footer">
        <button class="secondary" onclick={closeImportModal}>Cancel</button>
        {#if importTab === "import"}
          <button onclick={importFromJson} disabled={!jsonImport || saving}>
            {saving ? "Importing..." : "Import"}
          </button>
        {:else}
          <button onclick={linkFile} disabled={!linkPath.trim() || linking}>
            {linking ? "Linking..." : "Link"}
          </button>
        {/if}
      </div>
</Modal>

<Modal bind:open={showExportModal} title="Export Tests" onclose={closeExportModal}>
      <div class="modal-body">
        <div class="form-group">
          <label for="export-format">Format</label>
          <select id="export-format" bind:value={exportFormat}>
            <option value="retell">Retell</option>
          </select>
        </div>

        <div class="form-group">
          <label for="export-selection">Tests</label>
          <select id="export-selection" bind:value={exportSelection}>
            <option value="all">All tests ({$testCaseRecords.length})</option>
            <option value="selected" disabled={selectedTestIds.length === 0}>
              Selected tests ({selectedTestIds.length})
            </option>
          </select>
        </div>

        {#if exportError}
          <p class="error-message">{exportError}</p>
        {/if}
      </div>
      <div class="modal-footer">
        <button class="secondary" onclick={closeExportModal}>Cancel</button>
        <button onclick={doExport} disabled={exporting}>
          {exporting ? "Exporting..." : "Export"}
        </button>
      </div>
</Modal>

<style>
  .tests-view {
    max-width: 1400px;
    overflow-y: auto;
    flex: 1;
  }

  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1.5rem;
  }

  h2 {
    margin: 0;
  }

  h3 {
    margin: 0;
    font-size: 1rem;
    color: var(--text-secondary);
  }

  .header-actions {
    display: flex;
    gap: 0.5rem;
  }

  .run-btn {
    min-width: 100px;
    background: var(--accent);
    color: #ffffff;
    border-color: var(--accent);
  }

  .run-btn:hover:not(:disabled) {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
  }

  .run-btn.secondary {
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    border-color: var(--border-color);
  }

  .run-btn.secondary:hover:not(:disabled) {
    background: var(--bg-hover);
    color: var(--text-primary);
  }

  .placeholder {
    color: var(--text-secondary);
    font-style: italic;
  }

  .layout {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
  }

  .test-table th {
    color: var(--text-secondary);
    font-weight: 500;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }

  .test-table th.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
  }

  .test-table th.sortable:hover {
    color: var(--text-primary);
  }

  .sort-indicator {
    margin-left: 0.25rem;
    font-size: 0.6rem;
    opacity: 0.7;
  }


  .col-select {
    width: 40px;
  }

  .col-index {
    width: 40px;
    text-align: center;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
  }

  .col-name {
    width: 180px;
    font-weight: 500;
  }

  .col-type {
    width: 70px;
  }

  .col-prompt {
    width: auto;
  }

  .col-actions {
    width: 120px;
    text-align: right;
  }

  .prompt-preview {
    color: var(--text-secondary);
    font-size: 0.85rem;
    word-break: break-word;
  }

  .tag {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    border-radius: 9999px;
    font-size: var(--text-xs);
    font-weight: 500;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
  }

  .tag.llm {
    background: rgba(136, 87, 229, 0.15);
    color: #a78bfa;
    border-color: rgba(136, 87, 229, 0.3);
  }

  .tag.rule {
    background: rgba(63, 185, 80, 0.15);
    color: var(--color-pass);
    border-color: rgba(63, 185, 80, 0.3);
  }

  .small {
    padding: 0.2rem 0.4rem;
    font-size: var(--text-xs);
  }

  .secondary {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
  }

  .secondary:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
  }

  .danger {
    background: transparent;
    color: var(--danger-text);
    border: 1px solid var(--border-color);
  }

  .danger:hover {
    background: var(--danger-bg-hover);
    border-color: var(--danger-border);
  }

  .empty {
    color: var(--text-muted);
    font-style: italic;
    padding: 2rem;
    text-align: center;
  }

  .error-message {
    color: #f87171;
    background: rgba(248, 113, 113, 0.1);
    border: 1px solid rgba(248, 113, 113, 0.4);
    border-radius: var(--radius-md);
    padding: 0.75rem 1rem;
    margin: 0.5rem 0 0 0;
    font-size: var(--text-sm);
  }

  .modal {
    width: 90%;
    max-width: 900px;
  }

  .form-group {
    margin-bottom: 1rem;
  }

  .form-group label {
    display: block;
    margin-bottom: 0.25rem;
    color: var(--text-secondary);
    font-size: 0.85rem;
  }

  .form-group input,
  .form-group textarea,
  .form-group select {
    width: 100%;
  }

  .form-group select {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 0.5rem;
    border-radius: 4px;
  }

  .field-hint {
    display: block;
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 0.25rem;
  }

  .input-row {
    display: flex;
    gap: 0.5rem;
  }

  .input-row input {
    flex: 1;
  }

  .tag-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  .tag-list li {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.5rem;
    background: var(--bg-primary);
    padding: 0.35rem 0.5rem;
    border-radius: 4px;
    font-size: 0.85rem;
  }

  .tag-list li textarea {
    flex: 1;
    background: transparent;
    border: none;
    color: inherit;
    font-size: inherit;
    font-family: inherit;
    padding: 0;
    outline: none;
    line-height: 1.5;
    word-break: break-word;
    overflow-wrap: anywhere;
    resize: none;
    field-sizing: content;
    min-height: 1.5em;
  }

  .tag-list.includes li {
    border-left: 3px solid #4ade80;
  }

  .tag-list.excludes li {
    border-left: 3px solid #f87171;
  }

  .tag-list.patterns li {
    border-left: 3px solid #60a5fa;
  }

  .tag-list code {
    font-family: monospace;
    font-size: 0.8rem;
  }

  /* Linked files section */
  .linked-files-section {
    background: var(--bg-secondary);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-color);
  }

  .linked-files-heading {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-secondary);
    margin: 0 0 var(--space-2) 0;
  }

  .linked-file-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) 0;
    border-top: 1px solid var(--border-color);
  }

  .linked-file-row:first-of-type {
    border-top: none;
  }

  .linked-file-info {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
  }

  .linked-file-name {
    font-weight: 500;
    font-size: var(--text-sm);
  }

  .linked-file-path {
    font-family: monospace;
    font-size: var(--text-xs);
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Linked badge */
  .badge-linked {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border-radius: 9999px;
    font-size: 0.65rem;
    font-weight: 500;
    background: rgba(56, 189, 248, 0.15);
    color: #38bdf8;
    border: 1px solid rgba(56, 189, 248, 0.3);
    vertical-align: middle;
  }

  /* Modal tabs */
  .modal-tabs {
    display: flex;
    border-bottom: 1px solid var(--border-color);
    background: var(--bg-tertiary);
    padding: 0 var(--space-4);
  }

  .modal-tab {
    padding: var(--space-2) var(--space-3);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: color 80ms ease-out, border-color 80ms ease-out;
  }

  .modal-tab:hover {
    color: var(--text-primary);
    background: transparent;
  }

  .modal-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
  }

  /* Drop zone file upload area */
  .drop-zone {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.75rem;
    padding: var(--space-3);
    border: 2px dashed var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-primary);
  }

  .file-label {
    display: inline-block;
    padding: 0.5rem 1rem;
    background: #3b82f6;
    color: white;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
  }

  .file-label:hover {
    background: #2563eb;
  }

  .file-label input {
    display: none;
  }

  .file-hint {
    color: var(--text-muted);
    font-size: 0.85rem;
  }

  .link-toggle {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 0.25rem 0;
    font-size: 0.85rem;
  }

  .link-toggle:hover {
    color: var(--text-primary);
  }

  .file-status {
    font-size: 0.85rem;
    color: var(--text-secondary);
  }

  .import-fields {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  .json-input {
    width: 100%;
    box-sizing: border-box;
    font-family: monospace;
    font-size: var(--text-sm);
  }

  /* Import progress bar */
  .import-progress {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    margin-bottom: 1rem;
  }

  .progress-text {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-bottom: var(--space-2);
  }

  .progress-bar {
    height: 4px;
    background: var(--bg-tertiary);
    border-radius: 2px;
    overflow: hidden;
  }

  .progress-fill {
    height: 100%;
    background: var(--accent);
    transition: width 150ms ease-out;
  }

</style>
