/**
 * Clipboard Store Tests
 *
 * Tests the pure helper functions and store actions from the clipboard store:
 * - detectLanguage: identifies programming language from code content
 * - formatTimestamp: converts an ISO timestamp to a relative time string
 * - Store actions: loadEntries, captureClipboard, deleteEntry, togglePin, etc.
 * - Derived stores: filteredEntries (language + search filtering), languages
 */

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { get } from "svelte/store";
import { clipboardStore } from "$lib/stores/clipboard";
import { setMockInvokeResult } from "../../../vitest.setup";

describe("detectLanguage", () => {
  describe("TypeScript detection", () => {
    it("detects import statements", () => {
      expect(clipboardStore.detectLanguage("import React from 'react';")).toBe("typescript");
    });

    it("detects export statements", () => {
      expect(clipboardStore.detectLanguage("export function foo() {}")).toBe("typescript");
    });

    it("detects const declarations", () => {
      expect(clipboardStore.detectLanguage("const x = 1;")).toBe("typescript");
    });

    it("detects interface declarations", () => {
      expect(clipboardStore.detectLanguage("interface Foo {\n  bar: string;\n}")).toBe(
        "typescript"
      );
    });

    it("detects type aliases", () => {
      expect(clipboardStore.detectLanguage("type MyType = string | number;")).toBe("typescript");
    });
  });

  describe("Python detection", () => {
    it("detects def statements", () => {
      expect(clipboardStore.detectLanguage("def foo():\n  pass")).toBe("python");
    });

    it("detects async def statements", () => {
      expect(clipboardStore.detectLanguage("async def bar():\n  pass")).toBe("python");
    });

    it("detects from imports", () => {
      expect(clipboardStore.detectLanguage("from os import path")).toBe("python");
    });

    it("detects the __name__ guard", () => {
      expect(clipboardStore.detectLanguage("if __name__ == '__main__':\n  main()")).toBe("python");
    });
  });

  describe("Rust detection", () => {
    it("detects fn declarations", () => {
      expect(clipboardStore.detectLanguage('fn main() {\n  println!("hello");\n}')).toBe("rust");
    });

    it("detects impl blocks", () => {
      expect(clipboardStore.detectLanguage("impl Foo {\n  pub fn new() -> Self {}\n}")).toBe(
        "rust"
      );
    });

    it("detects struct declarations", () => {
      expect(clipboardStore.detectLanguage("struct Point {\n  x: f64,\n  y: f64,\n}")).toBe("rust");
    });

    it("detects enum declarations", () => {
      expect(clipboardStore.detectLanguage("enum Direction {\n  North,\n  South,\n}")).toBe("rust");
    });

    it("detects mod declarations", () => {
      expect(clipboardStore.detectLanguage("mod utils;")).toBe("rust");
    });

    it("detects pub visibility", () => {
      expect(clipboardStore.detectLanguage("pub fn exported() {}")).toBe("rust");
    });
  });

  describe("Go detection", () => {
    it("detects package declarations", () => {
      expect(clipboardStore.detectLanguage("package main")).toBe("go");
    });

    it("detects func declarations", () => {
      expect(clipboardStore.detectLanguage("func main() {}")).toBe("go");
    });
  });

  describe("PHP detection", () => {
    it("detects the PHP open tag", () => {
      expect(clipboardStore.detectLanguage("<?php\necho 'hello';")).toBe("php");
    });
  });

  describe("SQL detection", () => {
    it("detects SELECT statements", () => {
      expect(clipboardStore.detectLanguage("SELECT * FROM users WHERE id = 1")).toBe("sql");
    });

    it("detects INSERT statements", () => {
      expect(clipboardStore.detectLanguage("INSERT INTO users (name) VALUES ('Alice')")).toBe(
        "sql"
      );
    });

    it("detects CREATE statements", () => {
      expect(clipboardStore.detectLanguage("CREATE TABLE users (id INT PRIMARY KEY)")).toBe("sql");
    });

    it("detects SQL case-insensitively", () => {
      expect(clipboardStore.detectLanguage("select * from users")).toBe("sql");
    });
  });

  describe("HTML detection", () => {
    it("detects DOCTYPE declarations", () => {
      expect(clipboardStore.detectLanguage("<!DOCTYPE html>")).toBe("html");
    });

    it("detects html tags", () => {
      expect(clipboardStore.detectLanguage("<html><body></body></html>")).toBe("html");
    });

    it("detects div tags", () => {
      expect(clipboardStore.detectLanguage("<div class='foo'>bar</div>")).toBe("html");
    });

    it("detects span tags", () => {
      expect(clipboardStore.detectLanguage("<span>text</span>")).toBe("html");
    });
  });

  describe("JSON detection", () => {
    it("detects JSON object syntax", () => {
      expect(clipboardStore.detectLanguage('{"name": "test", "value": 42}')).toBe("json");
    });

    it("detects JSON with hyphenated keys", () => {
      expect(clipboardStore.detectLanguage('{"my-key": "value"}')).toBe("json");
    });
  });

  describe("YAML detection", () => {
    it("detects YAML document separator", () => {
      expect(clipboardStore.detectLanguage("---\nkey: value\nother: 123")).toBe("yaml");
    });
  });

  describe("C detection", () => {
    it("detects #include directives", () => {
      expect(clipboardStore.detectLanguage("#include <stdio.h>\nint main() {}")).toBe("c");
    });

    it("detects #define directives", () => {
      expect(clipboardStore.detectLanguage("#define MAX 100")).toBe("c");
    });

    it("detects #ifdef directives", () => {
      expect(clipboardStore.detectLanguage("#ifdef DEBUG\n// debug code\n#endif")).toBe("c");
    });
  });

  describe("Java detection", () => {
    it("detects public class declarations", () => {
      expect(clipboardStore.detectLanguage("public class Foo {\n  // ...\n}")).toBe("java");
    });

    it("detects private static methods", () => {
      expect(clipboardStore.detectLanguage("private static void helper() {}")).toBe("java");
    });

    it("detects protected interface declarations", () => {
      expect(clipboardStore.detectLanguage("protected interface Bar {}")).toBe("java");
    });
  });

  describe("Bash detection", () => {
    it("detects shell variable assignments", () => {
      expect(clipboardStore.detectLanguage("$HOME=/usr/local")).toBe("bash");
    });

    it("detects variable assignments with underscores", () => {
      expect(clipboardStore.detectLanguage("$MY_VAR=some_value")).toBe("bash");
    });
  });

  describe("unknown content", () => {
    it("returns null for plain text", () => {
      expect(clipboardStore.detectLanguage("Hello, world!")).toBeNull();
    });

    it("returns null for empty string", () => {
      expect(clipboardStore.detectLanguage("")).toBeNull();
    });

    it("returns null for mathematical expressions", () => {
      expect(clipboardStore.detectLanguage("1 + 1 = 2")).toBeNull();
    });

    it("returns null for a markdown heading", () => {
      expect(clipboardStore.detectLanguage("# My Heading\nSome text")).toBeNull();
    });
  });
});

describe("formatTimestamp", () => {
  const NOW = new Date("2026-03-03T12:00:00.000Z");

  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(NOW);
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe("'Just now' threshold (< 1 minute)", () => {
    it("returns 'Just now' for a timestamp 30 seconds ago", () => {
      const ts = new Date("2026-03-03T11:59:30.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("Just now");
    });

    it("returns 'Just now' for the current moment", () => {
      const ts = NOW.toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("Just now");
    });

    it("returns 'Just now' for a timestamp 59 seconds ago", () => {
      const ts = new Date("2026-03-03T11:59:01.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("Just now");
    });
  });

  describe("'Xm ago' threshold (1–59 minutes)", () => {
    it("returns '1m ago' at exactly 1 minute", () => {
      const ts = new Date("2026-03-03T11:59:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("1m ago");
    });

    it("returns '5m ago' for a timestamp 5 minutes ago", () => {
      const ts = new Date("2026-03-03T11:55:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("5m ago");
    });

    it("returns '59m ago' just before the 1-hour threshold", () => {
      const ts = new Date("2026-03-03T11:01:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("59m ago");
    });
  });

  describe("'Xh ago' threshold (1–23 hours)", () => {
    it("returns '1h ago' at exactly 1 hour", () => {
      const ts = new Date("2026-03-03T11:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("1h ago");
    });

    it("returns '2h ago' for a timestamp 2 hours ago", () => {
      const ts = new Date("2026-03-03T10:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("2h ago");
    });

    it("returns '23h ago' just before the 1-day threshold", () => {
      const ts = new Date("2026-03-02T13:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("23h ago");
    });
  });

  describe("'Xd ago' threshold (1–6 days)", () => {
    it("returns '1d ago' at exactly 1 day", () => {
      const ts = new Date("2026-03-02T12:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("1d ago");
    });

    it("returns '3d ago' for a timestamp 3 days ago", () => {
      const ts = new Date("2026-02-28T12:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("3d ago");
    });

    it("returns '6d ago' just before the 7-day threshold", () => {
      const ts = new Date("2026-02-25T12:00:00.000Z").toISOString();
      expect(clipboardStore.formatTimestamp(ts)).toBe("6d ago");
    });
  });

  describe("locale date string (7+ days ago)", () => {
    it("returns a locale date string for a 2-week-old timestamp", () => {
      const ts = new Date("2026-02-17T12:00:00.000Z").toISOString();
      const result = clipboardStore.formatTimestamp(ts);
      // Should not be a relative time string
      expect(result).not.toContain("m ago");
      expect(result).not.toContain("h ago");
      expect(result).not.toContain("d ago");
      expect(result).not.toBe("Just now");
    });

    it("returns a locale date string for a 1-month-old timestamp", () => {
      const ts = new Date("2026-02-03T12:00:00.000Z").toISOString();
      const result = clipboardStore.formatTimestamp(ts);
      expect(result).not.toContain("ago");
    });
  });
});

describe("clipboardStore - derived stores", () => {
  const makeEntry = (overrides: Record<string, unknown> = {}) => ({
    id: "entry-1",
    content: "const x = 1;",
    language: "typescript",
    source: "test.ts",
    timestamp: "2026-03-03T12:00:00.000Z",
    is_pinned: false,
    ...overrides,
  });

  beforeEach(() => {
    clipboardStore.entries.set([]);
    clipboardStore.searchQuery.set("");
    clipboardStore.languageFilter.set(null);
  });

  describe("filteredEntries - language filter", () => {
    it("returns all entries when no language filter is set", () => {
      clipboardStore.entries.set([
        makeEntry({ id: "1", language: "typescript" }),
        makeEntry({ id: "2", language: "python" }),
      ]);
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(2);
    });

    it("filters entries by language", () => {
      clipboardStore.entries.set([
        makeEntry({ id: "1", language: "typescript" }),
        makeEntry({ id: "2", language: "python" }),
        makeEntry({ id: "3", language: "typescript" }),
      ]);
      clipboardStore.languageFilter.set("typescript");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(2);
      expect(filtered.every((e) => e.language === "typescript")).toBe(true);
    });

    it("returns empty array when filter matches nothing", () => {
      clipboardStore.entries.set([makeEntry({ language: "typescript" })]);
      clipboardStore.languageFilter.set("rust");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(0);
    });
  });

  describe("filteredEntries - search query", () => {
    it("filters by content (case-insensitive)", () => {
      clipboardStore.entries.set([
        makeEntry({ id: "1", content: "const HELLO = 1;" }),
        makeEntry({ id: "2", content: "let world = 2;" }),
      ]);
      clipboardStore.searchQuery.set("hello");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(1);
      expect(filtered[0].id).toBe("1");
    });

    it("filters by language field", () => {
      clipboardStore.entries.set([
        makeEntry({ id: "1", language: "typescript", content: "unrelated" }),
        makeEntry({ id: "2", language: "python", content: "also unrelated" }),
      ]);
      clipboardStore.searchQuery.set("python");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(1);
      expect(filtered[0].id).toBe("2");
    });

    it("filters by source field", () => {
      clipboardStore.entries.set([
        makeEntry({ id: "1", source: "main.rs", content: "fn main() {}" }),
        makeEntry({ id: "2", source: "app.ts", content: "const x = 1;" }),
      ]);
      clipboardStore.searchQuery.set("main.rs");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(1);
      expect(filtered[0].id).toBe("1");
    });

    it("returns all entries when search query is empty", () => {
      clipboardStore.entries.set([makeEntry({ id: "1" }), makeEntry({ id: "2" })]);
      clipboardStore.searchQuery.set("");
      const filtered = get(clipboardStore.filteredEntries);
      expect(filtered).toHaveLength(2);
    });
  });

  describe("languages derived store", () => {
    it("returns empty array when no entries", () => {
      clipboardStore.entries.set([]);
      expect(get(clipboardStore.languages)).toHaveLength(0);
    });

    it("returns unique sorted languages", () => {
      clipboardStore.entries.set([
        makeEntry({ language: "typescript" }),
        makeEntry({ language: "python" }),
        makeEntry({ language: "typescript" }),
        makeEntry({ language: "rust" }),
      ]);
      const langs = get(clipboardStore.languages);
      expect(langs).toEqual(["python", "rust", "typescript"]);
    });

    it("excludes entries with null language", () => {
      clipboardStore.entries.set([
        makeEntry({ language: "typescript" }),
        makeEntry({ language: null }),
      ]);
      const langs = get(clipboardStore.languages);
      expect(langs).toEqual(["typescript"]);
    });
  });
});

describe("clipboardStore - setSearchQuery and setLanguageFilter", () => {
  it("setSearchQuery updates the searchQuery store", () => {
    clipboardStore.setSearchQuery("hello world");
    expect(get(clipboardStore.searchQuery)).toBe("hello world");
    clipboardStore.setSearchQuery("");
  });

  it("setLanguageFilter updates the languageFilter store", () => {
    clipboardStore.setLanguageFilter("python");
    expect(get(clipboardStore.languageFilter)).toBe("python");
    clipboardStore.setLanguageFilter(null);
  });

  it("setLanguageFilter can be reset to null", () => {
    clipboardStore.setLanguageFilter("rust");
    clipboardStore.setLanguageFilter(null);
    expect(get(clipboardStore.languageFilter)).toBeNull();
  });
});

describe("clipboardStore - store actions", () => {
  const mockEntry = {
    id: "entry-1",
    content: "const x = 1;",
    language: "typescript",
    source: "test.ts",
    timestamp: "2026-03-03T12:00:00.000Z",
    is_pinned: false,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    clipboardStore.entries.set([]);
  });

  describe("loadEntries", () => {
    it("loads entries from backend and updates the store", async () => {
      setMockInvokeResult("list_clipboard_entries", [mockEntry]);
      await clipboardStore.loadEntries();
      expect(get(clipboardStore.entries)).toEqual([mockEntry]);
    });

    it("handles errors gracefully without crashing", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("list_clipboard_entries", new Error("Backend unavailable"));
      await clipboardStore.loadEntries();
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to load clipboard entries:",
        expect.any(Error)
      );
      consoleErrorSpy.mockRestore();
    });

    it("sets isLoading to false after completion", async () => {
      setMockInvokeResult("list_clipboard_entries", []);
      await clipboardStore.loadEntries();
      expect(get(clipboardStore.isLoading)).toBe(false);
    });
  });

  describe("captureClipboard", () => {
    it("captures clipboard entry with provided language", async () => {
      setMockInvokeResult("capture_clipboard", mockEntry);
      setMockInvokeResult("list_clipboard_entries", [mockEntry]);
      const result = await clipboardStore.captureClipboard("const x = 1;", "typescript", "test.ts");
      expect(result).toEqual(mockEntry);
    });

    it("auto-detects language when none provided", async () => {
      setMockInvokeResult("capture_clipboard", mockEntry);
      setMockInvokeResult("list_clipboard_entries", [mockEntry]);
      const result = await clipboardStore.captureClipboard("const x = 1;");
      expect(result).toEqual(mockEntry);
    });

    it("returns null on error", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("capture_clipboard", new Error("Failed"));
      const result = await clipboardStore.captureClipboard("const x = 1;");
      expect(result).toBeNull();
      consoleErrorSpy.mockRestore();
    });
  });

  describe("deleteEntry", () => {
    it("removes entry from store on success", async () => {
      clipboardStore.entries.set([mockEntry, { ...mockEntry, id: "entry-2" }]);
      setMockInvokeResult("delete_clipboard_entry", undefined);
      await clipboardStore.deleteEntry("entry-1");
      expect(get(clipboardStore.entries)).toHaveLength(1);
      expect(get(clipboardStore.entries)[0].id).toBe("entry-2");
    });

    it("handles errors gracefully", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("delete_clipboard_entry", new Error("Failed"));
      await clipboardStore.deleteEntry("entry-1");
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to delete clipboard entry:",
        expect.any(Error)
      );
      consoleErrorSpy.mockRestore();
    });
  });

  describe("togglePin", () => {
    it("updates entry pin status and sorts pinned first", async () => {
      const unpinned1 = { ...mockEntry, id: "entry-1", is_pinned: false };
      const unpinned2 = { ...mockEntry, id: "entry-2", is_pinned: false };
      clipboardStore.entries.set([unpinned1, unpinned2]);
      const pinned = { ...unpinned2, is_pinned: true };
      setMockInvokeResult("toggle_pin_clipboard_entry", pinned);
      await clipboardStore.togglePin("entry-2");
      const entries = get(clipboardStore.entries);
      expect(entries[0].id).toBe("entry-2");
      expect(entries[0].is_pinned).toBe(true);
    });

    it("handles errors gracefully", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("toggle_pin_clipboard_entry", new Error("Failed"));
      await clipboardStore.togglePin("entry-1");
      expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to toggle pin:", expect.any(Error));
      consoleErrorSpy.mockRestore();
    });

    it("exercises the unpinned-before-pinned sort branch", async () => {
      // Three entries so sort compares (entry-1 unpinned, entry-2 pinned) — hits line 144
      const e1 = { ...mockEntry, id: "e1", is_pinned: false };
      const e2 = { ...mockEntry, id: "e2", is_pinned: false };
      const e3 = { ...mockEntry, id: "e3", is_pinned: false };
      clipboardStore.entries.set([e1, e2, e3]);
      const pinned = { ...e2, is_pinned: true };
      setMockInvokeResult("toggle_pin_clipboard_entry", pinned);
      await clipboardStore.togglePin("e2");
      expect(get(clipboardStore.entries)[0].id).toBe("e2");
    });

    it("sorts same-pin-status entries by timestamp descending", async () => {
      // Toggle entry to pinned while two others remain unpinned — sort compares unpinned pair by timestamp
      const older = {
        ...mockEntry,
        id: "older",
        is_pinned: false,
        timestamp: "2026-01-01T00:00:00.000Z",
      };
      const newer = {
        ...mockEntry,
        id: "newer",
        is_pinned: false,
        timestamp: "2026-01-02T00:00:00.000Z",
      };
      const pinned = {
        ...mockEntry,
        id: "pinned",
        is_pinned: false,
        timestamp: "2026-01-03T00:00:00.000Z",
      };
      clipboardStore.entries.set([older, newer, pinned]);
      const pinnedResult = { ...pinned, is_pinned: true };
      setMockInvokeResult("toggle_pin_clipboard_entry", pinnedResult);
      await clipboardStore.togglePin("pinned");
      const entries = get(clipboardStore.entries);
      expect(entries[0].id).toBe("pinned");
      expect(entries[1].id).toBe("newer");
      expect(entries[2].id).toBe("older");
    });
  });

  describe("clearHistory", () => {
    it("removes unpinned entries and keeps pinned ones", async () => {
      const pinned = { ...mockEntry, id: "pinned", is_pinned: true };
      const unpinned = { ...mockEntry, id: "unpinned", is_pinned: false };
      clipboardStore.entries.set([pinned, unpinned]);
      setMockInvokeResult("clear_clipboard_history", undefined);
      await clipboardStore.clearHistory();
      const entries = get(clipboardStore.entries);
      expect(entries).toHaveLength(1);
      expect(entries[0].id).toBe("pinned");
    });

    it("handles errors gracefully", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("clear_clipboard_history", new Error("Failed"));
      await clipboardStore.clearHistory();
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to clear clipboard history:",
        expect.any(Error)
      );
      consoleErrorSpy.mockRestore();
    });
  });

  describe("updateLanguage", () => {
    it("updates entry language in the store", async () => {
      clipboardStore.entries.set([mockEntry]);
      const updated = { ...mockEntry, language: "javascript" };
      setMockInvokeResult("update_clipboard_language", updated);
      await clipboardStore.updateLanguage("entry-1", "javascript");
      expect(get(clipboardStore.entries)[0].language).toBe("javascript");
    });

    it("leaves non-matching entries unchanged", async () => {
      const other = { ...mockEntry, id: "other", language: "python" };
      clipboardStore.entries.set([mockEntry, other]);
      const updated = { ...mockEntry, language: "javascript" };
      setMockInvokeResult("update_clipboard_language", updated);
      await clipboardStore.updateLanguage("entry-1", "javascript");
      const entries = get(clipboardStore.entries);
      expect(entries[1].language).toBe("python");
    });

    it("handles errors gracefully", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("update_clipboard_language", new Error("Failed"));
      await clipboardStore.updateLanguage("entry-1", "javascript");
      expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to update language:", expect.any(Error));
      consoleErrorSpy.mockRestore();
    });
  });

  describe("copyToClipboard", () => {
    it("returns true and copies text on success", async () => {
      Object.assign(navigator, {
        clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },
      });
      const result = await clipboardStore.copyToClipboard("hello world");
      expect(result).toBe(true);
      expect(navigator.clipboard.writeText).toHaveBeenCalledWith("hello world");
    });

    it("returns false and logs error on failure", async () => {
      const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      Object.assign(navigator, {
        clipboard: { writeText: vi.fn().mockRejectedValue(new Error("Clipboard denied")) },
      });
      const result = await clipboardStore.copyToClipboard("hello world");
      expect(result).toBe(false);
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to copy to clipboard:",
        expect.any(Error)
      );
      consoleErrorSpy.mockRestore();
    });
  });
});
