import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { CronService } from "./cron.service";
import { PrismaService } from "../prisma/prisma.service";

describe("CronService", () => {
  let service: CronService;
  let prisma: PrismaService;

  const mockPrisma = {
    cronSchedule: {
      create: vi.fn(),
      findMany: vi.fn(),
      findUnique: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
    },
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CronService,
        {
          provide: PrismaService,
          useValue: mockPrisma,
        },
      ],
    }).compile();

    service = module.get<CronService>(CronService);
    prisma = module.get<PrismaService>(PrismaService);
    vi.clearAllMocks();
  });

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

  describe("create", () => {
    it("should create a cron schedule", async () => {
      const createDto = {
        workspaceId: "ws-123",
        expression: "0 9 * * *",
        command: "morning briefing",
      };

      const expectedSchedule = {
        id: "cron-1",
        ...createDto,
        enabled: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockPrisma.cronSchedule.create.mockResolvedValue(expectedSchedule);

      const result = await service.create(createDto);

      expect(result).toEqual(expectedSchedule);
      expect(mockPrisma.cronSchedule.create).toHaveBeenCalledWith({
        data: {
          workspaceId: createDto.workspaceId,
          expression: createDto.expression,
          command: createDto.command,
          enabled: true,
        },
      });
    });

    it("should reject invalid cron expressions", async () => {
      const createDto = {
        workspaceId: "ws-123",
        expression: "not-a-cron",
        command: "test command",
      };

      await expect(service.create(createDto)).rejects.toThrow("Invalid cron expression");
    });
  });

  describe("findAll", () => {
    it("should return all schedules for a workspace", async () => {
      const workspaceId = "ws-123";
      const expectedSchedules = [
        {
          id: "cron-1",
          workspaceId,
          expression: "0 9 * * *",
          command: "morning briefing",
          enabled: true,
        },
        {
          id: "cron-2",
          workspaceId,
          expression: "0 17 * * *",
          command: "evening summary",
          enabled: true,
        },
      ];

      mockPrisma.cronSchedule.findMany.mockResolvedValue(expectedSchedules);

      const result = await service.findAll(workspaceId);

      expect(result).toEqual(expectedSchedules);
      expect(mockPrisma.cronSchedule.findMany).toHaveBeenCalledWith({
        where: { workspaceId },
        orderBy: { createdAt: "desc" },
      });
    });
  });

  describe("findOne", () => {
    it("should return a schedule by id", async () => {
      const schedule = {
        id: "cron-1",
        workspaceId: "ws-123",
        expression: "0 9 * * *",
        command: "morning briefing",
        enabled: true,
      };

      mockPrisma.cronSchedule.findUnique.mockResolvedValue(schedule);

      const result = await service.findOne("cron-1", "ws-123");

      expect(result).toEqual(schedule);
      expect(mockPrisma.cronSchedule.findUnique).toHaveBeenCalledWith({
        where: { id: "cron-1" },
      });
    });

    it("should return null if schedule not found", async () => {
      mockPrisma.cronSchedule.findUnique.mockResolvedValue(null);

      const result = await service.findOne("cron-999", "ws-123");

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

  describe("update", () => {
    it("should update a cron schedule", async () => {
      const updateDto = { expression: "0 8 * * *", enabled: false };
      const expectedSchedule = {
        id: "cron-1",
        workspaceId: "ws-123",
        expression: "0 8 * * *",
        command: "morning briefing",
        enabled: false,
      };

      mockPrisma.cronSchedule.findUnique.mockResolvedValue({ id: "cron-1", workspaceId: "ws-123" });
      mockPrisma.cronSchedule.update.mockResolvedValue(expectedSchedule);

      const result = await service.update("cron-1", "ws-123", updateDto);

      expect(result).toEqual(expectedSchedule);
      expect(mockPrisma.cronSchedule.update).toHaveBeenCalled();
    });
  });

  describe("remove", () => {
    it("should delete a cron schedule", async () => {
      const schedule = {
        id: "cron-1",
        workspaceId: "ws-123",
        expression: "0 9 * * *",
        command: "morning briefing",
        enabled: true,
      };

      mockPrisma.cronSchedule.findUnique.mockResolvedValue(schedule);
      mockPrisma.cronSchedule.delete.mockResolvedValue(schedule);

      const result = await service.remove("cron-1", "ws-123");

      expect(result).toEqual(schedule);
      expect(mockPrisma.cronSchedule.delete).toHaveBeenCalledWith({
        where: { id: "cron-1" },
      });
    });

    it("should throw if schedule belongs to different workspace", async () => {
      mockPrisma.cronSchedule.findUnique.mockResolvedValue({
        id: "cron-1",
        workspaceId: "ws-456",
      });

      await expect(service.remove("cron-1", "ws-123")).rejects.toThrow(
        "Not authorized to delete this schedule"
      );
    });
  });
});
