import { type PackageInfo } from "@workshop/shared/packages"
import { DateTime } from "luxon"
import { test, expect } from "bun:test"
import { CronJob, type CronSchedule } from "./cron"

const createCron = (cronString: string, lastRunAt?: DateTime) => {
  const cronJob = new CronJob(cronString, "", {json: {name: "test", version: "1.0.0"}, path: "."}, lastRunAt)
  return cronJob
}

test("valid CronJobs", () => {
  expect(createCron("hourly").schedule).toEqual({ unit: "hourly", hour: 0, minute: 0 })
  expect(createCron("hourly@").schedule).toEqual({ unit: "hourly", hour: 0, minute: 0 })
  expect(createCron("hourly@0:30").schedule).toEqual({ unit: "hourly", hour: 0, minute: 30 })
  expect(createCron("hourly@10:59").schedule).toEqual({ unit: "hourly", hour: 10, minute: 59 })
})

test("invalid CronJobs", () => {
  expect(() => createCron("")).toThrow()
  expect(() => createCron("hourl")).toThrow()
  expect(() => createCron("hourly@*")).toThrow()
  expect(() => createCron("hourly@45:59")).toThrow()
  expect(() => createCron("hourly@0:99")).toThrow()
})

test("isDue", () => {
  const lastRunAt = DateTime.fromRFC2822("1 Jan 2025 00:00:00 GMT")

  let cronJob = createCron("constantly", lastRunAt)
  expect(cronJob.isDue(lastRunAt.plus({ seconds: 5 }))).toBe(false)
  expect(cronJob.isDue(lastRunAt.plus({ seconds: 60 }))).toBe(true)

  cronJob = createCron("hourly@0:15", lastRunAt)
  expect(cronJob.isDue(lastRunAt.plus({ minutes: 14 }))).toBe(false)

  cronJob = createCron("hourly@0:30", lastRunAt.set({ minute: 45 }).minus({ hour: 1 }))
  expect(cronJob.isDue(lastRunAt.set({ minute: 15 }))).toBe(false)
  expect(cronJob.isDue(lastRunAt.set({ minute: 50 }))).toBe(true)

  const dailyLastRunAt = lastRunAt.set({ hour: 1, minute: 30 }).minus({ days: 1 })
  cronJob = createCron("daily@1:20", dailyLastRunAt)
  expect(cronJob.isDue(dailyLastRunAt.plus({ hours: 20 }))).toBe(false)
  expect(cronJob.isDue(dailyLastRunAt.plus({ hours: 25 }))).toBe(true)

  const weeklyLastRunAt = lastRunAt.set({ hour: 5, minute: 59 }).minus({ week: 1 })
  cronJob = createCron("weekly@5:59", weeklyLastRunAt)
  expect(cronJob.isDue(weeklyLastRunAt.plus({ days: 1 }))).toBe(false)
  expect(cronJob.isDue(weeklyLastRunAt.plus({ days: 7 }))).toBe(true)

  const monthLastRunAt = lastRunAt.set({ hour: 1, minute: 0 }).minus({ days: 20 })
  cronJob = createCron("monthly@1:00", monthLastRunAt)
  expect(cronJob.isDue(monthLastRunAt.plus({ days: 5 }))).toBe(false)
  expect(cronJob.isDue(monthLastRunAt.plus({ days: 20 }))).toBe(true)

  const shortMonthLastRunAt = DateTime.fromRFC2822("1 Feb 2025 00:00:00 GMT").set({ hour: 1, minute: 0 })
  cronJob = createCron("monthly@1:00", shortMonthLastRunAt)
  expect(cronJob.isDue(shortMonthLastRunAt.plus({ days: 5 }))).toBe(false)
  expect(cronJob.isDue(shortMonthLastRunAt.plus({ days: 29 }))).toBe(true)
})
