import test from 'node:test';
import assert from 'node:assert/strict';
import {
  buildScheduledBoardPostTitle,
  buildScheduledPlanWorkIdBase,
  isPlanScheduledTaskDue,
  mapPlanScheduledTaskRow,
  shouldCreatePlanForScheduleExecution,
  updatePlanScheduledTaskSchema,
} from './plan-schedule-service.js';

test('buildScheduledBoardPostTitle prefers the first memo line when present', () => {
  assert.equal(
    buildScheduledBoardPostTitle({
      work_id: '반복작업',
      note: '  첫 줄 제목  \n둘째 줄 설명',
    }),
    '첫 줄 제목',
  );
});

test('buildScheduledBoardPostTitle falls back to normalized work id when memo is empty', () => {
  assert.equal(
    buildScheduledBoardPostTitle({
      work_id: '  작업 ID  ',
      note: '   \n ',
    }),
    '반복작업',
  );
});

test('buildScheduledPlanWorkIdBase uses the schedule work id for automation registration', () => {
  assert.equal(
    buildScheduledPlanWorkIdBase({
      work_id: '  반복-정리  ',
    }),
    '반복-정리',
  );
});

test('buildScheduledPlanWorkIdBase keeps managed-service runs on the schedule work id with schedule pk prefix', () => {
  assert.equal(
    buildScheduledPlanWorkIdBase({
      id: 10,
      work_id: '  반복-정리  ',
      execution_mode: 'managed-service',
    }),
    'schedule-10-반복-정리',
  );

  assert.equal(
    buildScheduledPlanWorkIdBase({
      id: 10,
      work_id: '반복-정리-service',
      execution_mode: 'managed-service',
    }),
    'schedule-10-반복-정리-service',
  );
});

test('buildScheduledPlanWorkIdBase falls back when managed-service schedule id is missing', () => {
  assert.equal(
    buildScheduledPlanWorkIdBase({
      work_id: '반복-정리-service',
      execution_mode: 'managed-service',
    }),
    '반복-정리-service',
  );
});

test('shouldCreatePlanForScheduleExecution only returns true for managed-service schedules', () => {
  assert.equal(
    shouldCreatePlanForScheduleExecution({
      execution_mode: 'codex',
    }),
    false,
  );

  assert.equal(
    shouldCreatePlanForScheduleExecution({
      execution_mode: 'managed-service',
    }),
    true,
  );
});

test('updatePlanScheduledTaskSchema keeps partial updates partial', () => {
  assert.deepEqual(updatePlanScheduledTaskSchema.parse({ recreateManagedServiceOnNextSave: true }), {
    recreateManagedServiceOnNextSave: true,
  });
});

test('updatePlanScheduledTaskSchema accepts second-based interval updates', () => {
  assert.deepEqual(
    updatePlanScheduledTaskSchema.parse({
      repeatIntervalValue: 10,
      repeatIntervalUnit: 'second',
      repeatIntervalSeconds: 10,
    }),
    {
      repeatIntervalValue: 10,
      repeatIntervalUnit: 'second',
      repeatIntervalSeconds: 10,
    },
  );
});

test('updatePlanScheduledTaskSchema accepts multiple interval time windows', () => {
  assert.deepEqual(
    updatePlanScheduledTaskSchema.parse({
      repeatWindows: [
        { startTime: '09:00', endTime: '12:00' },
        { startTime: '13:00', endTime: '18:00' },
      ],
    }),
    {
      repeatWindows: [
        { startTime: '09:00', endTime: '12:00' },
        { startTime: '13:00', endTime: '18:00' },
      ],
    },
  );
});

test('interval schedule with start time waits until start time when immediate run is enabled', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        repeat_window_start_time: '09:00',
        created_at: '2026-04-30T07:00:00+09:00',
      },
      new Date('2026-04-30T08:59:00+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        repeat_window_start_time: '09:00',
        created_at: '2026-04-30T07:00:00+09:00',
      },
      new Date('2026-04-30T09:00:00+09:00'),
    ),
    true,
  );
});

test('interval schedule with start time waits one interval after the start when immediate run is disabled', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_minutes: 60,
        repeat_window_start_time: '09:00',
        created_at: '2026-04-30T07:00:00+09:00',
      },
      new Date('2026-04-30T09:59:00+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_minutes: 60,
        repeat_window_start_time: '09:00',
        created_at: '2026-04-30T07:00:00+09:00',
      },
      new Date('2026-04-30T10:00:00+09:00'),
    ),
    true,
  );
});

test('interval schedule supports second-based due calculation', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_unit: 'second',
        repeat_interval_value: 10,
        repeat_interval_seconds: 10,
        created_at: '2026-04-30T09:00:00+09:00',
      },
      new Date('2026-04-30T09:00:09+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_unit: 'second',
        repeat_interval_value: 10,
        repeat_interval_seconds: 10,
        created_at: '2026-04-30T09:00:00+09:00',
      },
      new Date('2026-04-30T09:00:10+09:00'),
    ),
    true,
  );
});

test('schedule date ranges block execution outside configured dates', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        schedule_date_ranges_json: JSON.stringify([
          {
            startDate: '2026-05-10',
            endDate: '2026-05-12',
          },
        ]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-09T10:00:00+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        schedule_date_ranges_json: JSON.stringify([
          {
            startDate: '2026-05-10',
            endDate: '2026-05-12',
          },
        ]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-10T10:00:00+09:00'),
    ),
    true,
  );
});

test('daily schedule weekdays block execution outside configured days', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'daily',
        immediate_run_enabled: true,
        daily_run_time: '09:00',
        schedule_weekdays_json: JSON.stringify([1, 3, 5]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-10T10:00:00+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'daily',
        immediate_run_enabled: true,
        daily_run_time: '09:00',
        schedule_weekdays_json: JSON.stringify([1, 3, 5]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-11T10:00:00+09:00'),
    ),
    true,
  );
});

test('interval schedule weekdays block execution outside configured days', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        schedule_weekdays_json: JSON.stringify([1, 3, 5]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-10T10:00:00+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: true,
        repeat_interval_minutes: 60,
        schedule_weekdays_json: JSON.stringify([1, 3, 5]),
        created_at: '2026-05-01T09:00:00+09:00',
      },
      new Date('2026-05-11T10:00:00+09:00'),
    ),
    true,
  );
});

test('interval schedule respects multiple configured time windows', () => {
  const row = {
    schedule_mode: 'interval',
    immediate_run_enabled: true,
    repeat_interval_minutes: 60,
    repeat_windows_json: JSON.stringify([
      { startTime: '09:00', endTime: '12:00' },
      { startTime: '14:00', endTime: '18:00' },
    ]),
    created_at: '2026-05-11T08:00:00+09:00',
  };

  assert.equal(isPlanScheduledTaskDue(row, new Date('2026-05-11T08:59:00+09:00')), false);
  assert.equal(isPlanScheduledTaskDue(row, new Date('2026-05-11T09:00:00+09:00')), true);
  assert.equal(isPlanScheduledTaskDue(row, new Date('2026-05-11T13:00:00+09:00')), false);
  assert.equal(isPlanScheduledTaskDue(row, new Date('2026-05-11T14:00:00+09:00')), true);
});

test('interval schedule uses repeat interval value and unit when stored seconds are stale', () => {
  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_unit: 'minute',
        repeat_interval_value: 10,
        repeat_interval_seconds: 3600,
        created_at: '2026-04-30T09:50:00+09:00',
      },
      new Date('2026-04-30T09:59:59+09:00'),
    ),
    false,
  );

  assert.equal(
    isPlanScheduledTaskDue(
      {
        schedule_mode: 'interval',
        immediate_run_enabled: false,
        repeat_interval_unit: 'minute',
        repeat_interval_value: 10,
        repeat_interval_seconds: 3600,
        created_at: '2026-04-30T09:50:00+09:00',
      },
      new Date('2026-04-30T10:00:00+09:00'),
    ),
    true,
  );
});

test('mapPlanScheduledTaskRow normalizes stale stored repeat interval seconds', () => {
  const mapped = mapPlanScheduledTaskRow({
    id: 2,
    work_id: 'stock-alert',
    schedule_mode: 'interval',
    repeat_interval_unit: 'minute',
    repeat_interval_value: 10,
    repeat_interval_seconds: 3600,
    repeat_interval_minutes: 60,
  });

  assert.equal(mapped.repeatIntervalValue, 10);
  assert.equal(mapped.repeatIntervalUnit, 'minute');
  assert.equal(mapped.repeatIntervalSeconds, 600);
  assert.equal(mapped.repeatIntervalMinutes, 10);
  assert.deepEqual(mapped.scheduleDateRanges, []);
});

test('mapPlanScheduledTaskRow parses stored schedule date ranges', () => {
  const mapped = mapPlanScheduledTaskRow({
    id: 3,
    work_id: 'range-task',
    schedule_date_ranges_json: JSON.stringify([
      { startDate: '2026-05-10', endDate: '2026-05-12' },
      { startDate: '2026-05-20', endDate: '2026-05-21' },
    ]),
  });

  assert.deepEqual(mapped.scheduleDateRanges, [
    { startDate: '2026-05-10', endDate: '2026-05-12' },
    { startDate: '2026-05-20', endDate: '2026-05-21' },
  ]);
});

test('mapPlanScheduledTaskRow parses stored schedule weekdays', () => {
  const mapped = mapPlanScheduledTaskRow({
    id: 4,
    work_id: 'weekday-task',
    schedule_weekdays_json: JSON.stringify([5, 1, 3, 1]),
  });

  assert.deepEqual(mapped.scheduleWeekdays, [1, 3, 5]);
});

test('mapPlanScheduledTaskRow parses stored repeat windows and falls back to legacy columns', () => {
  const mapped = mapPlanScheduledTaskRow({
    id: 5,
    work_id: 'window-task',
    repeat_windows_json: JSON.stringify([
      { startTime: '14:00', endTime: '18:00' },
      { startTime: '09:00', endTime: '12:00' },
    ]),
  });

  assert.deepEqual(mapped.repeatWindows, [
    { startTime: '09:00', endTime: '12:00' },
    { startTime: '14:00', endTime: '18:00' },
  ]);

  const legacyMapped = mapPlanScheduledTaskRow({
    id: 6,
    work_id: 'legacy-window-task',
    repeat_window_start_time: '10:00',
    repeat_window_end_time: '12:00',
  });

  assert.deepEqual(legacyMapped.repeatWindows, [{ startTime: '10:00', endTime: '12:00' }]);
});
