import { describe, it, expect } from 'vitest';
import { cronToFormState } from './cronForm';

/**
 * The backend's convertToCron (src/scheduler.ts) — mirrored here so the test
 * asserts a true round-trip: editing a saved schedule and saving it back must
 * not change the cron expression.
 */
function convertToCron(f: ReturnType<typeof cronToFormState>): string {
  const m = f.minute ?? 0;
  const h = f.hour ?? 0;
  switch (f.scheduleType) {
    case 'daily': return `${m} ${h} * * *`;
    case 'weekly': return `${m} ${h} * * ${f.dayOfWeek ?? 0}`;
    case 'monthly': return `${m} ${h} ${f.dayOfMonth ?? 1} * *`;
    case 'cron': return f.cronExpression;
    case 'once': return 'once';
  }
}

describe('cronToFormState', () => {
  it('classifies simple presets (daily/weekly/monthly)', () => {
    expect(cronToFormState('0 9 * * *')).toMatchObject({ scheduleType: 'daily', hour: 9, minute: 0 });
    expect(cronToFormState('30 14 * * *')).toMatchObject({ scheduleType: 'daily', hour: 14, minute: 30 });
    expect(cronToFormState('0 9 * * 3')).toMatchObject({ scheduleType: 'weekly', hour: 9, minute: 0, dayOfWeek: 3 });
    expect(cronToFormState('0 9 15 * *')).toMatchObject({ scheduleType: 'monthly', hour: 9, minute: 0, dayOfMonth: 15 });
  });

  it("keeps custom expressions as 'cron' and preserves the raw expression", () => {
    for (const expr of [
      '0 9 * * 1-5', '*/30 * * * *', '0 */2 * * *', '0 9,17 * * *', '15 9 1,15 * *', '0 9 1 6 *',
      '0 9 * * 7',          // Sunday-as-7 is out of the editor's 0-6 select range
      '0  9 * * *',         // double space → 6 tokens, not a clean preset
    ]) {
      const f = cronToFormState(expr);
      expect(f.scheduleType).toBe('cron');
      expect(f.cronExpression).toBe(expr);
    }
  });

  it('classifies Sunday-as-0 weekly', () => {
    expect(cronToFormState('0 9 * * 0')).toMatchObject({ scheduleType: 'weekly', dayOfWeek: 0 });
  });

  it('never produces NaN fields for custom expressions', () => {
    const f = cronToFormState('0 9 * * 1-5');
    expect(Number.isNaN(f.hour)).toBe(false);
    expect(Number.isNaN(f.minute)).toBe(false);
    expect(Number.isNaN(f.dayOfWeek)).toBe(false);
    expect(Number.isNaN(f.dayOfMonth)).toBe(false);
  });

  it("maps 'once' to the once type", () => {
    expect(cronToFormState('once')).toMatchObject({ scheduleType: 'once' });
  });

  it('treats a malformed (non 5-field) string as a raw cron expression', () => {
    expect(cronToFormState('garbage')).toMatchObject({ scheduleType: 'cron', cronExpression: 'garbage' });
  });

  it('round-trips losslessly through the backend convertToCron', () => {
    const cases = [
      '0 9 * * *', '30 14 * * *', '0 9 * * 3', '0 9 15 * *',
      '0 9 * * 1-5', '*/30 * * * *', '0 */2 * * *', '0 9,17 * * *',
      '15 9 1,15 * *', '0 9 1 6 *', 'once',
    ];
    for (const expr of cases) {
      expect(convertToCron(cronToFormState(expr))).toBe(expr);
    }
  });
});
