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 { snippetsStore, type Snippet } from "./snippets";

describe("Snippet interface", () => {
  it("defines all required fields", () => {
    const snippet: Snippet = {
      id: "snippet-123",
      name: "Git Status",
      content: "git status",
      category: "Git",
      is_default: false,
      created_at: "2024-01-01T00:00:00Z",
      updated_at: "2024-01-01T00:00:00Z",
    };

    expect(snippet.id).toBe("snippet-123");
    expect(snippet.name).toBe("Git Status");
    expect(snippet.content).toBe("git status");
    expect(snippet.category).toBe("Git");
    expect(snippet.is_default).toBe(false);
    expect(snippet.created_at).toBe("2024-01-01T00:00:00Z");
    expect(snippet.updated_at).toBe("2024-01-01T00:00:00Z");
  });

  it("supports default snippets", () => {
    const defaultSnippet: Snippet = {
      id: "default-git-status",
      name: "Git Status",
      content: "git status --short",
      category: "Git",
      is_default: true,
      created_at: "2024-01-01T00:00:00Z",
      updated_at: "2024-01-01T00:00:00Z",
    };

    expect(defaultSnippet.is_default).toBe(true);
  });
});

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

  describe("store structure", () => {
    it("has all expected methods", () => {
      expect(typeof snippetsStore.loadSnippets).toBe("function");
      expect(typeof snippetsStore.saveSnippet).toBe("function");
      expect(typeof snippetsStore.createSnippet).toBe("function");
      expect(typeof snippetsStore.updateSnippet).toBe("function");
      expect(typeof snippetsStore.deleteSnippet).toBe("function");
      expect(typeof snippetsStore.resetDefaults).toBe("function");
      expect(typeof snippetsStore.setSelectedCategory).toBe("function");
    });

    it("has subscribable stores", () => {
      expect(typeof snippetsStore.snippets.subscribe).toBe("function");
      expect(typeof snippetsStore.categories.subscribe).toBe("function");
      expect(typeof snippetsStore.filteredSnippets.subscribe).toBe("function");
      expect(typeof snippetsStore.isLoading.subscribe).toBe("function");
      expect(typeof snippetsStore.selectedCategory.subscribe).toBe("function");
    });
  });

  describe("loadSnippets", () => {
    it("loads snippets and categories from backend", async () => {
      const mockSnippets: Snippet[] = [
        {
          id: "snippet-1",
          name: "Snippet 1",
          content: "content 1",
          category: "Git",
          is_default: false,
          created_at: "2024-01-01T00:00:00Z",
          updated_at: "2024-01-01T00:00:00Z",
        },
      ];
      const mockCategories = ["Git", "Shell", "Docker"];

      setMockInvokeResult("list_snippets", mockSnippets);
      setMockInvokeResult("get_snippet_categories", mockCategories);

      await snippetsStore.loadSnippets();

      expect(invoke).toHaveBeenCalledWith("list_snippets");
      expect(invoke).toHaveBeenCalledWith("get_snippet_categories");
    });

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

      await snippetsStore.loadSnippets();

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

  describe("saveSnippet", () => {
    it("saves snippet and reloads list", async () => {
      const snippet: Snippet = {
        id: "snippet-123",
        name: "Test",
        content: "test content",
        category: "Test",
        is_default: false,
        created_at: "2024-01-01T00:00:00Z",
        updated_at: "2024-01-01T00:00:00Z",
      };

      setMockInvokeResult("save_snippet", undefined);
      setMockInvokeResult("list_snippets", [snippet]);
      setMockInvokeResult("get_snippet_categories", ["Test"]);

      const result = await snippetsStore.saveSnippet(snippet);

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith("save_snippet", { snippet });
    });

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

      const snippet: Snippet = {
        id: "snippet-123",
        name: "Test",
        content: "test content",
        category: "Test",
        is_default: false,
        created_at: "2024-01-01T00:00:00Z",
        updated_at: "2024-01-01T00:00:00Z",
      };

      const result = await snippetsStore.saveSnippet(snippet);

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

  describe("createSnippet", () => {
    it("creates new snippet with generated ID and timestamps", async () => {
      setMockInvokeResult("save_snippet", undefined);
      setMockInvokeResult("list_snippets", []);
      setMockInvokeResult("get_snippet_categories", ["Shell"]);

      const result = await snippetsStore.createSnippet("My Snippet", "echo hello", "Shell");

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith(
        "save_snippet",
        expect.objectContaining({
          snippet: expect.objectContaining({
            name: "My Snippet",
            content: "echo hello",
            category: "Shell",
            is_default: false,
          }),
        })
      );
    });
  });

  describe("updateSnippet", () => {
    it("updates existing snippet preserving created_at", async () => {
      const existingSnippet: Snippet = {
        id: "snippet-123",
        name: "Old Name",
        content: "old content",
        category: "Old Category",
        is_default: false,
        created_at: "2024-01-01T00:00:00Z",
        updated_at: "2024-01-01T00:00:00Z",
      };

      setMockInvokeResult("list_snippets", [existingSnippet]);
      setMockInvokeResult("save_snippet", undefined);
      setMockInvokeResult("get_snippet_categories", ["New Category"]);

      const result = await snippetsStore.updateSnippet(
        "snippet-123",
        "New Name",
        "new content",
        "New Category"
      );

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith(
        "save_snippet",
        expect.objectContaining({
          snippet: expect.objectContaining({
            id: "snippet-123",
            name: "New Name",
            content: "new content",
            category: "New Category",
            created_at: "2024-01-01T00:00:00Z",
          }),
        })
      );
    });

    it("returns false for non-existent snippet", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("list_snippets", []);

      const result = await snippetsStore.updateSnippet(
        "non-existent",
        "Name",
        "content",
        "Category"
      );

      expect(result).toBe(false);
      expect(consoleSpy).toHaveBeenCalledWith("Snippet not found for update");
      consoleSpy.mockRestore();
    });
  });

  describe("deleteSnippet", () => {
    it("deletes snippet by ID", async () => {
      setMockInvokeResult("delete_snippet", undefined);
      setMockInvokeResult("list_snippets", []);
      setMockInvokeResult("get_snippet_categories", []);

      const result = await snippetsStore.deleteSnippet("snippet-123");

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith("delete_snippet", { snippetId: "snippet-123" });
    });

    it("handles delete errors gracefully", async () => {
      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
      setMockInvokeResult("delete_snippet", new Error("Cannot delete default snippet"));

      const result = await snippetsStore.deleteSnippet("default-1");

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

  describe("resetDefaults", () => {
    it("resets default snippets", async () => {
      setMockInvokeResult("reset_default_snippets", undefined);
      setMockInvokeResult("list_snippets", []);
      setMockInvokeResult("get_snippet_categories", []);

      const result = await snippetsStore.resetDefaults();

      expect(result).toBe(true);
      expect(invoke).toHaveBeenCalledWith("reset_default_snippets");
    });

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

      const result = await snippetsStore.resetDefaults();

      expect(result).toBe(false);
      expect(consoleSpy).toHaveBeenCalledWith(
        "Failed to reset default snippets:",
        expect.any(Error)
      );
      consoleSpy.mockRestore();
    });
  });

  describe("setSelectedCategory", () => {
    it("updates selected category", () => {
      snippetsStore.setSelectedCategory("Git");
      expect(get(snippetsStore.selectedCategory)).toBe("Git");
    });

    it("can be cleared with null", () => {
      snippetsStore.setSelectedCategory("Git");
      snippetsStore.setSelectedCategory(null);
      expect(get(snippetsStore.selectedCategory)).toBeNull();
    });
  });
});

describe("snippet ID generation", () => {
  it("generates unique custom snippet IDs", () => {
    const generateId = () => `custom-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;

    const id1 = generateId();
    const id2 = generateId();

    expect(id1).toMatch(/^custom-\d+-[a-z0-9]+$/);
    expect(id2).toMatch(/^custom-\d+-[a-z0-9]+$/);
    expect(id1).not.toBe(id2);
  });
});

describe("snippet validation", () => {
  it("requires non-empty name", () => {
    const snippet = { name: "" };
    const isValid = snippet.name.trim().length > 0;
    expect(isValid).toBe(false);
  });

  it("requires non-empty content", () => {
    const snippet = { content: "   " };
    const isValid = snippet.content.trim().length > 0;
    expect(isValid).toBe(false);
  });

  it("requires non-empty category", () => {
    const snippet = { category: "Git" };
    const isValid = snippet.category.trim().length > 0;
    expect(isValid).toBe(true);
  });
});

describe("snippet content types", () => {
  it("supports multiline content", () => {
    const snippet = {
      content: `git add .
git commit -m "message"
git push`,
    };

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

  it("supports content with special characters", () => {
    const snippet = {
      content: "echo \"Hello, World!\" && echo 'Single quotes'",
    };

    expect(snippet.content).toContain('"');
    expect(snippet.content).toContain("'");
    expect(snippet.content).toContain("&&");
  });

  it("supports content with variables", () => {
    const snippet = {
      content: "docker run -v $PWD:/app ${IMAGE_NAME}:${TAG}",
    };

    expect(snippet.content).toContain("$PWD");
    expect(snippet.content).toContain("${IMAGE_NAME}");
  });
});
