import { describe, it, expect, beforeEach, vi } from "vitest";
import { initLogger, logError, ERROR_LOG_FILE } from "./logger";
import * as fs from "node:fs/promises";

vi.mock("fs/promises", () => ({
  readFile: vi.fn(),
  appendFile: vi.fn(),
  access: vi.fn(),
  mkdir: vi.fn(),
  stat: vi.fn(),
}));

describe("Logger", () => {
  let consoleErrorSpy: ReturnType<typeof vi.spyOn>;

  beforeEach(() => {
    consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});

    vi.mocked(fs.stat).mockResolvedValue({
      size: 100,
    } as any);
  });

  afterEach(() => {
    consoleErrorSpy.mockRestore();
  });

  describe("initLogger", () => {
    it("should create log directory if it does not exist", async () => {
      await initLogger("test");
      expect(fs.mkdir).toHaveBeenCalledWith(
        expect.stringContaining(
          process.platform === "win32"
            ? ".embeddable\\logs"
            : ".embeddable/logs",
        ),
        { recursive: true },
      );
    });

    it("should handle errors when creating log directory", async () => {
      vi.mocked(fs.mkdir).mockRejectedValueOnce(
        new Error("Failed to create directory"),
      );

      await initLogger("test");

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to create log directory:",
        expect.any(Error),
      );
    });
  });

  describe("logError", () => {
    it("should append error message to log file", async () => {
      const error = new Error("Test error");
      await logError({
        command: "test",
        breadcrumbs: ["step1", "step2"],
        error,
      });

      expect(fs.appendFile).toHaveBeenCalledWith(
        ERROR_LOG_FILE,
        expect.stringContaining(
          "Command: test\nBreadcrumbs: step1 > step2\nError: Error: Test error",
        ),
      );
    });

    it("should handle non-Error objects", async () => {
      await logError({
        command: "test",
        breadcrumbs: ["step1"],
        error: "String error",
      });

      expect(fs.appendFile).toHaveBeenCalledWith(
        ERROR_LOG_FILE,
        expect.stringContaining(
          "Command: test\nBreadcrumbs: step1\nError: String error",
        ),
      );
    });

    it("should log to console when error occurs", async () => {
      const consoleErrorSpy = vi
        .spyOn(console, "error")
        .mockImplementation(() => {});
      await logError({ command: "test", breadcrumbs: [], error: "Test error" });

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        expect.stringContaining("An error occurred during test"),
      );
    });

    it("should handle errors when writing to log file", async () => {
      const consoleErrorSpy = vi
        .spyOn(console, "error")
        .mockImplementation(() => {});
      vi.mocked(fs.appendFile).mockRejectedValueOnce(
        new Error("Failed to write"),
      );

      await logError({ command: "test", breadcrumbs: [], error: "Test error" });

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        "Failed to write to log file:",
        expect.any(Error),
      );
    });
  });
});
