/**
 * Terminal Component Tests
 *
 * Tests the pure helper functions extracted from the Terminal component:
 * - getLineClass: maps line types to CSS class names
 * - getLinePrefix: maps line types to display prefixes
 * - formatTime: formats a Date as "HH:MM AM/PM"
 * - isToolContentLong: checks if tool content exceeds collapse threshold
 * - truncateToolContent: truncates long tool content with ellipsis
 *
 * Manual testing checklist:
 * - [ ] rate-limit lines appear in amber
 * - [ ] error lines appear in red
 * - [ ] tool lines appear in purple
 * - [ ] system lines appear in grey italic
 * - [ ] user lines appear in cyan
 * - [ ] assistant lines appear in primary text colour
 * - [ ] long tool content is collapsed by default with a toggle button
 */

import { describe, it, expect } from "vitest";

// Mirror functions from Terminal.svelte for isolated testing

function getLineClass(type: string): string {
  switch (type) {
    case "user":
      return "terminal-user";
    case "assistant":
      return "terminal-assistant";
    case "system":
      return "terminal-system italic";
    case "tool":
      return "terminal-tool";
    case "error":
      return "terminal-error";
    case "thinking":
      return "terminal-thinking";
    case "rate-limit":
      return "terminal-rate-limit";
    case "compact-prompt":
      return "terminal-compact-prompt";
    case "worktree":
      return "terminal-worktree";
    case "config-change":
      return "terminal-config-change";
    default:
      return "terminal-default";
  }
}

function getLinePrefix(type: string): string {
  switch (type) {
    case "user":
      return ">";
    case "assistant":
      return "";
    case "system":
      return "[system]";
    case "tool":
      return "[tool]";
    case "error":
      return "[error]";
    case "rate-limit":
      return "[rate-limit]";
    case "worktree":
      return "[worktree]";
    case "config-change":
      return "[config]";
    default:
      return "";
  }
}

function formatTime(date: Date): string {
  return date.toLocaleTimeString("en-US", {
    hour: "2-digit",
    minute: "2-digit",
  });
}

const TOOL_COLLAPSE_THRESHOLD = 60;

function isToolContentLong(content: string): boolean {
  return content.length > TOOL_COLLAPSE_THRESHOLD;
}

function truncateToolContent(content: string): string {
  return content.slice(0, TOOL_COLLAPSE_THRESHOLD) + "…";
}

// Virtual windowing helpers — mirror the logic in Terminal.svelte

const WINDOW_SIZE = 150;
const LOAD_CHUNK = 50;
const AVG_LINE_HEIGHT = 60;

/** Returns the windowStart index when auto-scrolling to the bottom. */
function autoScrollWindowStart(linesLength: number, windowSize: number): number {
  return Math.max(0, linesLength - windowSize);
}

/** Returns the new windowStart after loading LOAD_CHUNK older messages. */
function olderWindowStart(currentStart: number, chunkSize: number): number {
  return Math.max(0, currentStart - chunkSize);
}

/** Returns the height in pixels of the invisible top spacer. */
function topSpacerHeight(windowStart: number, avgLineHeight: number): number {
  return windowStart * avgLineHeight;
}

// ---

describe("getLineClass", () => {
  it("returns terminal-user for user lines", () => {
    expect(getLineClass("user")).toBe("terminal-user");
  });

  it("returns terminal-assistant for assistant lines", () => {
    expect(getLineClass("assistant")).toBe("terminal-assistant");
  });

  it("returns terminal-system italic for system lines", () => {
    expect(getLineClass("system")).toBe("terminal-system italic");
  });

  it("returns terminal-tool for tool lines", () => {
    expect(getLineClass("tool")).toBe("terminal-tool");
  });

  it("returns terminal-error for error lines", () => {
    expect(getLineClass("error")).toBe("terminal-error");
  });

  it("returns terminal-thinking for thinking lines", () => {
    expect(getLineClass("thinking")).toBe("terminal-thinking");
  });

  it("returns terminal-rate-limit for rate-limit lines", () => {
    expect(getLineClass("rate-limit")).toBe("terminal-rate-limit");
  });

  it("returns terminal-compact-prompt for compact-prompt lines", () => {
    expect(getLineClass("compact-prompt")).toBe("terminal-compact-prompt");
  });

  it("returns terminal-worktree for worktree lines", () => {
    expect(getLineClass("worktree")).toBe("terminal-worktree");
  });

  it("returns terminal-config-change for config-change lines", () => {
    expect(getLineClass("config-change")).toBe("terminal-config-change");
  });

  it("returns terminal-default for unknown line types", () => {
    expect(getLineClass("unknown")).toBe("terminal-default");
    expect(getLineClass("")).toBe("terminal-default");
    expect(getLineClass("random-future-type")).toBe("terminal-default");
  });
});

describe("getLinePrefix", () => {
  it("returns > for user lines", () => {
    expect(getLinePrefix("user")).toBe(">");
  });

  it("returns empty string for assistant lines", () => {
    expect(getLinePrefix("assistant")).toBe("");
  });

  it("returns [system] for system lines", () => {
    expect(getLinePrefix("system")).toBe("[system]");
  });

  it("returns [tool] for tool lines", () => {
    expect(getLinePrefix("tool")).toBe("[tool]");
  });

  it("returns [error] for error lines", () => {
    expect(getLinePrefix("error")).toBe("[error]");
  });

  it("returns [rate-limit] for rate-limit lines", () => {
    expect(getLinePrefix("rate-limit")).toBe("[rate-limit]");
  });

  it("returns empty string for compact-prompt lines (button renders instead)", () => {
    expect(getLinePrefix("compact-prompt")).toBe("");
  });

  it("returns [worktree] for worktree lines", () => {
    expect(getLinePrefix("worktree")).toBe("[worktree]");
  });

  it("returns [config] for config-change lines", () => {
    expect(getLinePrefix("config-change")).toBe("[config]");
  });

  it("returns empty string for thinking lines (no prefix)", () => {
    expect(getLinePrefix("thinking")).toBe("");
  });

  it("returns empty string for unknown line types", () => {
    expect(getLinePrefix("unknown")).toBe("");
    expect(getLinePrefix("")).toBe("");
  });
});

describe("formatTime", () => {
  it("formats time in 12-hour format with AM/PM", () => {
    const date = new Date(2026, 1, 7, 14, 35);
    const formatted = formatTime(date);
    expect(formatted).toMatch(/\d{2}:\d{2}\s?(AM|PM)/i);
  });

  it("formats afternoon times correctly", () => {
    const date = new Date(2026, 1, 7, 14, 35);
    const formatted = formatTime(date);
    expect(formatted).toContain("02:35");
    expect(formatted.toUpperCase()).toContain("PM");
  });

  it("formats morning times correctly", () => {
    const date = new Date(2026, 1, 7, 9, 5);
    const formatted = formatTime(date);
    expect(formatted).toContain("09:05");
    expect(formatted.toUpperCase()).toContain("AM");
  });

  it("formats midnight correctly", () => {
    const date = new Date(2026, 1, 7, 0, 0);
    const formatted = formatTime(date);
    expect(formatted).toContain("12:00");
    expect(formatted.toUpperCase()).toContain("AM");
  });

  it("formats noon correctly", () => {
    const date = new Date(2026, 1, 7, 12, 0);
    const formatted = formatTime(date);
    expect(formatted).toContain("12:00");
    expect(formatted.toUpperCase()).toContain("PM");
  });
});

describe("isToolContentLong", () => {
  it("returns false for content at or below the threshold", () => {
    const exactThreshold = "x".repeat(TOOL_COLLAPSE_THRESHOLD);
    expect(isToolContentLong(exactThreshold)).toBe(false);
  });

  it("returns true for content exceeding the threshold", () => {
    const overThreshold = "x".repeat(TOOL_COLLAPSE_THRESHOLD + 1);
    expect(isToolContentLong(overThreshold)).toBe(true);
  });

  it("returns false for short content", () => {
    expect(isToolContentLong("short")).toBe(false);
  });

  it("returns false for empty content", () => {
    expect(isToolContentLong("")).toBe(false);
  });
});

describe("truncateToolContent", () => {
  it("truncates content to the threshold length with an ellipsis", () => {
    const long = "x".repeat(100);
    const result = truncateToolContent(long);
    expect(result).toBe("x".repeat(TOOL_COLLAPSE_THRESHOLD) + "…");
  });

  it("keeps content shorter than threshold unchanged (plus ellipsis)", () => {
    const short = "hello";
    const result = truncateToolContent(short);
    expect(result).toBe("hello…");
  });

  it("uses the unicode ellipsis character (not three dots)", () => {
    const long = "x".repeat(100);
    const result = truncateToolContent(long);
    expect(result.endsWith("…")).toBe(true);
    expect(result.endsWith("...")).toBe(false);
  });
});

describe("autoScrollWindowStart", () => {
  it("returns 0 when lines fit within the window", () => {
    expect(autoScrollWindowStart(50, WINDOW_SIZE)).toBe(0);
  });

  it("returns 0 when lines exactly fill the window", () => {
    expect(autoScrollWindowStart(WINDOW_SIZE, WINDOW_SIZE)).toBe(0);
  });

  it("advances when lines exceed the window size", () => {
    expect(autoScrollWindowStart(200, WINDOW_SIZE)).toBe(50);
  });

  it("never returns a negative value", () => {
    expect(autoScrollWindowStart(0, WINDOW_SIZE)).toBe(0);
  });

  it("keeps last WINDOW_SIZE lines visible for large collections", () => {
    expect(autoScrollWindowStart(500, WINDOW_SIZE)).toBe(350);
  });
});

describe("olderWindowStart", () => {
  it("subtracts the chunk size from the current start", () => {
    expect(olderWindowStart(100, LOAD_CHUNK)).toBe(50);
  });

  it("never returns a negative value when chunk is larger than start", () => {
    expect(olderWindowStart(20, LOAD_CHUNK)).toBe(0);
  });

  it("returns 0 when current start is 0", () => {
    expect(olderWindowStart(0, LOAD_CHUNK)).toBe(0);
  });

  it("returns 0 when current start exactly equals the chunk size", () => {
    expect(olderWindowStart(LOAD_CHUNK, LOAD_CHUNK)).toBe(0);
  });

  it("correctly loads a partial chunk near the beginning", () => {
    expect(olderWindowStart(30, LOAD_CHUNK)).toBe(0);
  });
});

describe("topSpacerHeight", () => {
  it("returns 0 when windowStart is 0", () => {
    expect(topSpacerHeight(0, AVG_LINE_HEIGHT)).toBe(0);
  });

  it("multiplies windowStart by avgLineHeight", () => {
    expect(topSpacerHeight(10, AVG_LINE_HEIGHT)).toBe(600);
  });

  it("scales linearly with windowStart", () => {
    expect(topSpacerHeight(50, AVG_LINE_HEIGHT)).toBe(3000);
    expect(topSpacerHeight(100, AVG_LINE_HEIGHT)).toBe(6000);
    expect(topSpacerHeight(150, AVG_LINE_HEIGHT)).toBe(9000);
  });

  it("uses the provided avgLineHeight rather than a hard-coded value", () => {
    expect(topSpacerHeight(5, 100)).toBe(500);
    expect(topSpacerHeight(5, 80)).toBe(400);
  });
});

describe("virtual windowing constants", () => {
  it("WINDOW_SIZE is 150", () => {
    expect(WINDOW_SIZE).toBe(150);
  });

  it("LOAD_CHUNK is 50", () => {
    expect(LOAD_CHUNK).toBe(50);
  });

  it("LOAD_CHUNK is smaller than WINDOW_SIZE", () => {
    expect(LOAD_CHUNK).toBeLessThan(WINDOW_SIZE);
  });

  it("AVG_LINE_HEIGHT is a positive number", () => {
    expect(AVG_LINE_HEIGHT).toBeGreaterThan(0);
  });
});
