/**
 * Theme C (P1-4) — maintenance-window evaluator.
 *
 * Pure decision function: given a check time, a monitorId, and the
 * full set of windows, returns whether the check falls inside an
 * active window. Used by the worker before persisting a heartbeat
 * (suppress alerts, set status='maintenance') and by SLA reports
 * (exclude maintenance time from uptime math).
 */
import { isWithinMaintenanceWindow } from '../maintenance-window';
import type { MaintenanceWindow } from '@prisma/client';

function w(overrides: Partial<MaintenanceWindow>): MaintenanceWindow {
    return {
        id: 1,
        monitorId: null,
        startsAt: new Date('2026-06-01T10:00:00Z'),
        endsAt: new Date('2026-06-01T11:00:00Z'),
        reason: 'maintenance',
        recurrence: null,
        createdAt: new Date('2026-05-01T00:00:00Z'),
        createdById: 1,
        deletedAt: null,
        deletedById: null,
        ...overrides,
    } as MaintenanceWindow;
}

describe('isWithinMaintenanceWindow (Theme C)', () => {
    it('matches a one-shot window when the check falls inside', () => {
        const now = new Date('2026-06-01T10:30:00Z');
        const win = w({ monitorId: 5 });
        expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(true);
    });

    it('rejects when the check is before the window', () => {
        const now = new Date('2026-06-01T09:59:59Z');
        const win = w({ monitorId: 5 });
        expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(false);
    });

    it('rejects when the check is after the window', () => {
        const now = new Date('2026-06-01T11:00:01Z');
        const win = w({ monitorId: 5 });
        expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(false);
    });

    it('respects monitorId scoping — a window for monitor 5 does not affect monitor 6', () => {
        const now = new Date('2026-06-01T10:30:00Z');
        const win = w({ monitorId: 5 });
        expect(isWithinMaintenanceWindow(now, 6, [win])).toBe(false);
    });

    it('treats monitorId=null as operator-wide — matches every monitor', () => {
        const now = new Date('2026-06-01T10:30:00Z');
        const win = w({ monitorId: null });
        expect(isWithinMaintenanceWindow(now, 99, [win])).toBe(true);
    });

    it('ignores soft-deleted windows', () => {
        const now = new Date('2026-06-01T10:30:00Z');
        const win = w({ monitorId: 5, deletedAt: new Date('2026-05-30T00:00:00Z') });
        expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(false);
    });

    it('matches when ANY of multiple windows is active', () => {
        const now = new Date('2026-06-01T10:30:00Z');
        const past = w({ id: 1, monitorId: 5, startsAt: new Date('2026-05-01T00:00:00Z'), endsAt: new Date('2026-05-01T01:00:00Z') });
        const active = w({ id: 2, monitorId: 5 });
        expect(isWithinMaintenanceWindow(now, 5, [past, active])).toBe(true);
    });

    describe('weekly recurrence', () => {
        it('matches when the day-of-week token is set and we are inside the same-time window on a recurrence day', () => {
            // Window declared 2026-06-01T10:00..11:00 UTC (Monday)
            // Recurrence: every MON,WED — check on next Wednesday at 10:30 should match
            const now = new Date('2026-06-03T10:30:00Z'); // Wednesday
            const win = w({ monitorId: 5, recurrence: 'MON,WED' });
            expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(true);
        });

        it('does NOT match a recurrence day at a different time of day', () => {
            const now = new Date('2026-06-03T15:00:00Z'); // Wednesday, 15:00 (outside 10-11 window)
            const win = w({ monitorId: 5, recurrence: 'MON,WED' });
            expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(false);
        });

        it('does NOT match a non-recurrence day even at the right time of day', () => {
            const now = new Date('2026-06-02T10:30:00Z'); // Tuesday, NOT in MON,WED
            const win = w({ monitorId: 5, recurrence: 'MON,WED' });
            expect(isWithinMaintenanceWindow(now, 5, [win])).toBe(false);
        });
    });
});
