import { describe, it, expect, vi, beforeEach } from "vitest";
import { get } from "svelte/store";
import { invoke } from "@tauri-apps/api/core";
import { setMockInvokeResult } from "../../../vitest.setup";
import { draftsStore, type Draft } from "./drafts";

const makeDraft = (id: string, content: string, saved_at: string): Draft => ({
  id,
  content,
  saved_at,
});

describe("Draft interface", () => {
  it("defines all required fields", () => {
    const draft: Draft = {
      id: "draft-123",
      content: "Hello world",
      saved_at: "2026-01-01T00:00:00+00:00",
    };

    expect(draft.id).toBe("draft-123");
    expect(draft.content).toBe("Hello world");
    expect(draft.saved_at).toBe("2026-01-01T00:00:00+00:00");
  });

  it("supports multiline content", () => {
    const draft: Draft = {
      id: "multi",
      content: "Line 1\nLine 2\nLine 3",
      saved_at: "2026-01-01T00:00:00+00:00",
    };

    expect(draft.content.includes("\n")).toBe(true);
    expect(draft.content.split("\n")).toHaveLength(3);
  });
});

describe("draftsStore", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("store structure", () => {
    it("has all expected methods", () => {
      expect(typeof draftsStore.loadDrafts).toBe("function");
      expect(typeof draftsStore.saveDraft).toBe("function");
      expect(typeof draftsStore.deleteDraft).toBe("function");
      expect(typeof draftsStore.deleteAllDrafts).toBe("function");
      expect(typeof draftsStore.formatTimestamp).toBe("function");
    });

    it("has subscribable stores", () => {
      expect(typeof draftsStore.drafts.subscribe).toBe("function");
      expect(typeof draftsStore.isLoading.subscribe).toBe("function");
    });
  });

  describe("loadDrafts", () => {
    it("loads drafts from backend", async () => {
      const mockDrafts: Draft[] = [
        makeDraft("draft-1", "Hello world", "2026-01-01T00:00:00+00:00"),
      ];

      setMockInvokeResult("list_drafts", mockDrafts);

      await draftsStore.loadDrafts();

      expect(invoke).toHaveBeenCalledWith("list_drafts");
      expect(get(draftsStore.drafts)).toEqual(mockDrafts);
    });

    it("handles load errors gracefully", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("list_drafts", new Error("Failed to load"));

      await draftsStore.loadDrafts();

      expect(consoleSpy).toHaveBeenCalledWith("Failed to load drafts:", expect.any(Error));
      consoleSpy.mockRestore();
    });

    it("sets isLoading during load", async () => {
      const loadingStates: boolean[] = [];
      const unsubscribe = draftsStore.isLoading.subscribe((val) => loadingStates.push(val));

      setMockInvokeResult("list_drafts", []);
      await draftsStore.loadDrafts();

      unsubscribe();
      expect(loadingStates).toContain(true);
      expect(loadingStates.at(-1)).toBe(false);
    });
  });

  describe("saveDraft", () => {
    it("saves draft and reloads list", async () => {
      const mockDraft = makeDraft("new-id", "My draft content", "2026-01-01T00:00:00+00:00");

      setMockInvokeResult("save_draft", mockDraft);
      setMockInvokeResult("list_drafts", [mockDraft]);

      const result = await draftsStore.saveDraft("My draft content");

      expect(result).toEqual(mockDraft);
      expect(invoke).toHaveBeenCalledWith("save_draft", { content: "My draft content" });
    });

    it("returns null on error", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("save_draft", new Error("Save failed"));

      const result = await draftsStore.saveDraft("content");

      expect(result).toBeNull();
      expect(consoleSpy).toHaveBeenCalledWith("Failed to save draft:", expect.any(Error));
      consoleSpy.mockRestore();
    });
  });

  describe("deleteDraft", () => {
    it("deletes draft by ID and reloads", async () => {
      setMockInvokeResult("delete_draft", undefined);
      setMockInvokeResult("list_drafts", []);

      const result = await draftsStore.deleteDraft("draft-123");

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith("delete_draft", { draftId: "draft-123" });
    });

    it("handles delete errors gracefully", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("delete_draft", new Error("Delete failed"));

      const result = await draftsStore.deleteDraft("draft-123");

      expect(result).toBe(false);
      expect(consoleSpy).toHaveBeenCalledWith("Failed to delete draft:", expect.any(Error));
      consoleSpy.mockRestore();
    });
  });

  describe("deleteAllDrafts", () => {
    it("deletes all drafts and clears store", async () => {
      // First populate the store
      setMockInvokeResult("list_drafts", [makeDraft("d1", "Draft 1", "2026-01-01T00:00:00+00:00")]);
      await draftsStore.loadDrafts();
      expect(get(draftsStore.drafts)).toHaveLength(1);

      setMockInvokeResult("delete_all_drafts", undefined);
      const result = await draftsStore.deleteAllDrafts();

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith("delete_all_drafts");
      expect(get(draftsStore.drafts)).toHaveLength(0);
    });

    it("handles delete-all errors gracefully", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("delete_all_drafts", new Error("Delete all failed"));

      const result = await draftsStore.deleteAllDrafts();

      expect(result).toBe(false);
      expect(consoleSpy).toHaveBeenCalledWith("Failed to delete all drafts:", expect.any(Error));
      consoleSpy.mockRestore();
    });
  });

  describe("formatTimestamp", () => {
    it("formats a valid ISO timestamp", () => {
      const result = draftsStore.formatTimestamp("2026-01-15T14:30:00+00:00");
      // Should produce a human-readable string (locale-dependent)
      expect(typeof result).toBe("string");
      expect(result.length).toBeGreaterThan(0);
    });

    it("falls back to raw string on invalid timestamp", () => {
      const invalid = "not-a-date";
      const result = draftsStore.formatTimestamp(invalid);
      expect(result).toBe(invalid);
    });

    it("falls back to raw string when toLocaleString throws", () => {
      const spy = vi.spyOn(Date.prototype, "toLocaleString").mockImplementation(() => {
        throw new Error("Locale not supported");
      });
      const ts = "2026-01-15T14:30:00.000Z";
      const result = draftsStore.formatTimestamp(ts);
      expect(result).toBe(ts);
      spy.mockRestore();
    });
  });
});

describe("draft content handling", () => {
  it("supports content with special characters", () => {
    const draft = makeDraft(
      "special",
      "echo \"Hello\" && echo 'World'",
      "2026-01-01T00:00:00+00:00"
    );
    expect(draft.content).toContain('"');
    expect(draft.content).toContain("'");
    expect(draft.content).toContain("&&");
  });

  it("supports very long content", () => {
    const longContent = "a".repeat(1000);
    const draft = makeDraft("long", longContent, "2026-01-01T00:00:00+00:00");
    expect(draft.content).toHaveLength(1000);
  });
});
