import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { invoke } from "@tauri-apps/api/core";
import { isPermissionGranted } from "@tauri-apps/plugin-notification";
import { setMockInvokeResult } from "../../../vitest.setup";
import { notificationManager } from "./notificationManager";
import { sendTerminalNotification } from "./terminalNotifier";
import { NotificationType, NOTIFICATION_SOUNDS } from "./types";

vi.mock("./soundPlayer", () => ({
  soundPlayer: { play: vi.fn().mockResolvedValue(undefined) },
}));

vi.mock("./terminalNotifier", () => ({
  sendTerminalNotification: vi.fn(),
}));

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

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

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

  describe("notify()", () => {
    it("calls the first notification method by default", async () => {
      await notificationManager.notify(NotificationType.SUCCESS);
      expect(invoke).toHaveBeenCalledWith(
        "send_windows_notification",
        expect.objectContaining({
          title: NOTIFICATION_SOUNDS[NotificationType.SUCCESS].phrase,
        })
      );
    });

    it("passes a custom message to the notification", async () => {
      await notificationManager.notify(NotificationType.ERROR, "Custom error message");
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.ERROR].phrase,
        body: "Custom error message",
      });
    });

    it("falls back to the next method when the first fails", async () => {
      setMockInvokeResult("send_windows_notification", new Error("Method 1 failed"));
      await notificationManager.notify(NotificationType.SUCCESS);
      expect(invoke).toHaveBeenCalledWith("send_windows_toast", expect.any(Object));
    });

    it("falls back to terminal notification when all methods fail", async () => {
      setMockInvokeResult("send_windows_notification", new Error("failed"));
      setMockInvokeResult("send_windows_toast", new Error("failed"));
      setMockInvokeResult("send_wsl_notification", new Error("failed"));
      setMockInvokeResult("send_notify_send", new Error("failed"));
      vi.mocked(isPermissionGranted).mockRejectedValueOnce(new Error("Permission check failed"));

      await notificationManager.notify(NotificationType.SUCCESS);

      expect(sendTerminalNotification).toHaveBeenCalledWith(
        NotificationType.SUCCESS,
        "Task completed successfully!"
      );
    });

    it("uses the default SUCCESS message when none is provided", async () => {
      await notificationManager.notify(NotificationType.SUCCESS);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Task completed successfully!",
      });
    });

    it("uses the default ERROR message", async () => {
      await notificationManager.notify(NotificationType.ERROR);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Something went wrong...",
      });
    });

    it("uses the default PERMISSION message", async () => {
      await notificationManager.notify(NotificationType.PERMISSION);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Permission needed to continue",
      });
    });

    it("uses the default CONNECTION message", async () => {
      await notificationManager.notify(NotificationType.CONNECTION);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Successfully connected to Claude Code",
      });
    });

    it("uses the default TASK_START message", async () => {
      await notificationManager.notify(NotificationType.TASK_START);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Starting task...",
      });
    });

    it("uses the default COST_ALERT message", async () => {
      await notificationManager.notify(NotificationType.COST_ALERT);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "You've exceeded your cost threshold!",
      });
    });

    it("uses the fallback default message for unhandled types", async () => {
      await notificationManager.notify(NotificationType.ACHIEVEMENT);
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "Notification",
      });
    });
  });

  describe("helper methods", () => {
    it("notifySuccess calls notify with SUCCESS type and default message", async () => {
      await notificationManager.notifySuccess();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.SUCCESS].phrase,
        body: "Task completed successfully!",
      });
    });

    it("notifySuccess passes a custom message", async () => {
      await notificationManager.notifySuccess("All done!");
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: expect.any(String),
        body: "All done!",
      });
    });

    it("notifyError calls notify with ERROR type", async () => {
      await notificationManager.notifyError();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.ERROR].phrase,
        body: "Something went wrong...",
      });
    });

    it("notifyPermission calls notify with PERMISSION type", async () => {
      await notificationManager.notifyPermission();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.PERMISSION].phrase,
        body: "Permission needed to continue",
      });
    });

    it("notifyConnection calls notify with CONNECTION type", async () => {
      await notificationManager.notifyConnection();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.CONNECTION].phrase,
        body: "Successfully connected to Claude Code",
      });
    });

    it("notifyTaskStart calls notify with TASK_START type", async () => {
      await notificationManager.notifyTaskStart();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.TASK_START].phrase,
        body: "Starting task...",
      });
    });

    it("notifyCostAlert calls notify with COST_ALERT type", async () => {
      await notificationManager.notifyCostAlert();
      expect(invoke).toHaveBeenCalledWith("send_windows_notification", {
        title: NOTIFICATION_SOUNDS[NotificationType.COST_ALERT].phrase,
        body: "You've exceeded your cost threshold!",
      });
    });
  });

  describe("Tauri plugin notification method (Method 4)", () => {
    it("sends notification when permission is already granted", async () => {
      const { isPermissionGranted, sendNotification } =
        await import("@tauri-apps/plugin-notification");
      setMockInvokeResult("send_windows_notification", new Error("failed"));
      setMockInvokeResult("send_windows_toast", new Error("failed"));
      setMockInvokeResult("send_wsl_notification", new Error("failed"));
      vi.mocked(isPermissionGranted).mockResolvedValueOnce(true);

      await notificationManager.notify(NotificationType.SUCCESS);

      expect(sendNotification).toHaveBeenCalledWith(
        expect.objectContaining({
          title: NOTIFICATION_SOUNDS[NotificationType.SUCCESS].phrase,
        })
      );
    });

    it("requests permission and sends notification when not yet granted", async () => {
      const { isPermissionGranted, requestPermission, sendNotification } =
        await import("@tauri-apps/plugin-notification");
      setMockInvokeResult("send_windows_notification", new Error("failed"));
      setMockInvokeResult("send_windows_toast", new Error("failed"));
      setMockInvokeResult("send_wsl_notification", new Error("failed"));
      vi.mocked(isPermissionGranted).mockResolvedValueOnce(false);
      vi.mocked(requestPermission).mockResolvedValueOnce("granted");

      await notificationManager.notify(NotificationType.SUCCESS);

      expect(requestPermission).toHaveBeenCalledWith();
      expect(sendNotification).toHaveBeenCalledWith(
        expect.objectContaining({ body: "Task completed successfully!" })
      );
    });

    it("falls through to next method when permission is denied", async () => {
      const { isPermissionGranted, requestPermission } =
        await import("@tauri-apps/plugin-notification");
      setMockInvokeResult("send_windows_notification", new Error("failed"));
      setMockInvokeResult("send_windows_toast", new Error("failed"));
      setMockInvokeResult("send_wsl_notification", new Error("failed"));
      vi.mocked(isPermissionGranted).mockResolvedValueOnce(false);
      vi.mocked(requestPermission).mockResolvedValueOnce("denied");
      setMockInvokeResult("send_notify_send", new Error("failed"));

      await notificationManager.notify(NotificationType.SUCCESS);

      expect(sendTerminalNotification).toHaveBeenCalledWith(
        NotificationType.SUCCESS,
        "Task completed successfully!"
      );
    });
  });
});
