import { describe, it, expect, beforeEach, vi } from "vitest";

// Mock WebSocketGateway before importing the service
vi.mock("../websocket/websocket.gateway", () => ({
  WebSocketGateway: vi.fn().mockImplementation(() => ({
    emitCronExecuted: vi.fn(),
  })),
}));

// Mock PrismaService
const mockPrisma = {
  cronSchedule: {
    findMany: vi.fn(),
    findUnique: vi.fn(),
    update: vi.fn(),
  },
};

vi.mock("../prisma/prisma.service", () => ({
  PrismaService: vi.fn().mockImplementation(() => mockPrisma),
}));

// Now import the service
import { CronSchedulerService } from "./cron.scheduler";

describe("CronSchedulerService", () => {
  let service: CronSchedulerService;

  beforeEach(async () => {
    vi.clearAllMocks();

    // Create service with mocked dependencies
    service = new CronSchedulerService(mockPrisma as any, { emitCronExecuted: vi.fn() } as any);
  });

  it("should be defined", () => {
    expect(service).toBeDefined();
  });

  describe("getStatus", () => {
    it("should return running status", () => {
      const status = service.getStatus();
      expect(status).toHaveProperty("running");
      expect(status).toHaveProperty("checkIntervalMs");
    });
  });

  describe("processDueSchedules", () => {
    it("should find due schedules with null nextRun", async () => {
      mockPrisma.cronSchedule.findMany.mockResolvedValue([]);

      await service.processDueSchedules();

      // Verify the call was made with correct structure
      const call = mockPrisma.cronSchedule.findMany.mock.calls[0]?.[0];
      expect(call).toBeDefined();
      expect(call?.where?.enabled).toBe(true);
      expect(call?.where?.OR).toHaveLength(2);
      expect(call?.where?.OR?.[0]).toEqual({ nextRun: null });
      expect(call?.where?.OR?.[1]?.nextRun?.lte).toBeInstanceOf(Date);
    });

    it("should return empty array when no schedules are due", async () => {
      mockPrisma.cronSchedule.findMany.mockResolvedValue([]);

      const result = await service.processDueSchedules();

      expect(result).toEqual([]);
    });

    it("should handle errors gracefully", async () => {
      mockPrisma.cronSchedule.findMany.mockRejectedValue(new Error("DB error"));

      const result = await service.processDueSchedules();

      expect(result).toEqual([]);
    });
  });

  describe("triggerManual", () => {
    it("should return null for non-existent schedule", async () => {
      mockPrisma.cronSchedule.findUnique.mockResolvedValue(null);

      const result = await service.triggerManual("cron-999");

      expect(result).toBeNull();
    });

    it("should return null for disabled schedule", async () => {
      mockPrisma.cronSchedule.findUnique.mockResolvedValue({
        id: "cron-1",
        enabled: false,
        command: "test",
        workspaceId: "ws-123",
      });

      const result = await service.triggerManual("cron-1");

      expect(result).toBeNull();
    });
  });

  describe("startScheduler / stopScheduler", () => {
    it("should start and stop the scheduler", () => {
      expect(service.getStatus().running).toBe(false);

      service.startScheduler();
      expect(service.getStatus().running).toBe(true);

      service.stopScheduler();
      expect(service.getStatus().running).toBe(false);
    });

    it("should not start multiple schedulers", () => {
      service.startScheduler();
      const firstInterval = service.getStatus().checkIntervalMs;

      service.startScheduler();
      expect(service.getStatus().checkIntervalMs).toBe(firstInterval);

      service.stopScheduler();
    });
  });
});
