/**
 * Standalone-Test für distributeJobs() — kein DB-Zugriff, pure Function.
 * Aufruf: `npx tsx scripts/test-distribute-jobs.ts`
 *
 * Wir nutzen ein winziges Assert-Setup statt einer Test-Lib, weil das Repo
 * bewusst kein Test-Framework hat (siehe CLAUDE.md).
 */
import { distributeJobs, estimateDistribution, hasDistributionLimits } from '../src/modules/scheduler/lib/distribute-jobs';

let failed = 0;
let passed = 0;

function check(name: string, cond: boolean, detail?: string) {
  if (cond) {
    passed++;
    console.log(`  ✓ ${name}`);
  } else {
    failed++;
    console.log(`  ✗ ${name}${detail ? ` — ${detail}` : ''}`);
  }
}

function section(title: string) {
  console.log(`\n${title}`);
}

function isoDay(d: Date): string {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

function isoHm(d: Date): string {
  return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}

// ─── Case 1: kein Limit → alle gleichzeitig ─────────────────────────────────
section('Case 1: ohne Limits → alle Slots auf startAt');
{
  const start = new Date('2026-05-15T09:00:00');
  const slots = distributeJobs({ count: 100, startAt: start });
  check('liefert 100 Dates', slots.length === 100);
  check('alle gleich startAt', slots.every((d) => d.getTime() === start.getTime()));
  check('hasDistributionLimits=false', !hasDistributionLimits({ count: 100, startAt: start }));
}

// ─── Case 2: 1000 Empfänger, max 100/Tag, Mo-Fr, 07:00-09:00 ─────────────────
section('Case 2: 1000 Empfänger · 100/Tag · Mo-Fr · 07-09 Uhr');
{
  // 2026-05-15 ist Freitag — wir starten Donnerstag um sicher 10 Werktage zu sehen
  const start = new Date('2026-05-14T07:00:00');
  const slots = distributeJobs({
    count: 1000,
    startAt: start,
    maxPerDay: 100,
    windowStart: '07:00',
    windowEnd: '09:00',
    allowedWeekdays: [1, 2, 3, 4, 5],
  });
  check('liefert 1000 Dates', slots.length === 1000);
  const uniqueDays = new Set(slots.map(isoDay));
  check('verteilt auf genau 10 Tage', uniqueDays.size === 10, `got ${uniqueDays.size}`);
  check('kein Wochenende',
    slots.every((d) => [1, 2, 3, 4, 5].includes(d.getDay())));
  check('alle innerhalb 07:00–09:00',
    slots.every((d) => {
      const min = d.getHours() * 60 + d.getMinutes();
      return min >= 7 * 60 && min < 9 * 60;
    }));
  check('100 pro Tag',
    [...uniqueDays].every((day) => slots.filter((d) => isoDay(d) === day).length === 100));
}

// ─── Case 3: 50 Empfänger, Fenster 07-09 Uhr, kein Cap → alle Tag 1 ─────────
section('Case 3: 50 Empfänger · Fenster ohne Cap → alle Tag 1');
{
  const start = new Date('2026-05-15T07:00:00'); // Freitag
  const slots = distributeJobs({
    count: 50,
    startAt: start,
    windowStart: '07:00',
    windowEnd: '09:00',
  });
  check('liefert 50 Dates', slots.length === 50);
  const uniqueDays = new Set(slots.map(isoDay));
  check('alle am gleichen Tag', uniqueDays.size === 1);
  check('alle innerhalb 07:00–09:00',
    slots.every((d) => {
      const min = d.getHours() * 60 + d.getMinutes();
      return min >= 7 * 60 && min < 9 * 60;
    }));
}

// ─── Case 4: nur Montage erlaubt ────────────────────────────────────────────
section('Case 4: 200 Empfänger · nur Mo · 100/Tag');
{
  const start = new Date('2026-05-15T09:00:00'); // Freitag
  const slots = distributeJobs({
    count: 200,
    startAt: start,
    maxPerDay: 100,
    allowedWeekdays: [1], // nur Montag
  });
  check('liefert 200 Dates', slots.length === 200);
  check('alle an Montagen', slots.every((d) => d.getDay() === 1));
  const uniqueDays = new Set(slots.map(isoDay));
  check('2 verschiedene Montage', uniqueDays.size === 2, `got ${uniqueDays.size}`);
}

// ─── Case 5: estimateDistribution ───────────────────────────────────────────
section('Case 5: estimate (1000 · 100/Tag · Mo-Fr)');
{
  const start = new Date('2026-05-14T07:00:00');
  const est = estimateDistribution({
    count: 1000,
    startAt: start,
    maxPerDay: 100,
    windowStart: '07:00',
    windowEnd: '09:00',
    allowedWeekdays: [1, 2, 3, 4, 5],
  });
  check('totalDays = 10', est.totalDays === 10, `got ${est.totalDays}`);
  check('perDay = 100', est.perDay === 100, `got ${est.perDay}`);
  check('finishAt vorhanden', est.finishAt !== null);
  check('finishAt innerhalb Fenster',
    est.finishAt !== null && (() => {
      const min = est.finishAt.getHours() * 60 + est.finishAt.getMinutes();
      return min >= 7 * 60 && min < 9 * 60;
    })());
}

// ─── Case 6: edge case — startAt mitten im Fenster ─────────────────────────
section('Case 6: startAt mitten im Fenster (08:30, Fenster 07-09)');
{
  const start = new Date('2026-05-15T08:30:00');
  const slots = distributeJobs({
    count: 5,
    startAt: start,
    maxPerDay: 10,
    windowStart: '07:00',
    windowEnd: '09:00',
  });
  check('liefert 5 Dates', slots.length === 5);
  check('erster Slot ≥ 08:30',
    slots[0].getHours() * 60 + slots[0].getMinutes() >= 8 * 60 + 30,
    `erster Slot war ${isoHm(slots[0])}`);
}

// ─── Case 7: backwards compat — keine Limits gesetzt ────────────────────────
section('Case 7: nur throttlePerHour gesetzt → kein Pre-Schedule');
{
  const start = new Date('2026-05-15T09:00:00');
  // throttlePerHour alleine soll KEIN Pre-Schedule triggern (Backwards-Compat)
  check('hasDistributionLimits=false bei nur throttle',
    !hasDistributionLimits({ count: 100, startAt: start, throttlePerHour: 50 }));
}

// ─── Result ────────────────────────────────────────────────────────────────
console.log(`\n${passed} passed, ${failed} failed`);
if (failed > 0) process.exit(1);
