import { describe, expect, it } from 'vitest';

import {
    resolveUpcomingResetAnnouncement,
    resolveUpcomingResetAnnouncements,
    shouldExposeUpcomingReset,
} from '../src/lobby/profileStatusService.js';
import type { GatewayOperationRecord } from '../src/orchestrator/profileRepository.js';

const buildOperation = (status: GatewayOperationRecord['status'] = 'QUEUED'): GatewayOperationRecord => ({
    id: '11111111-1111-4111-8111-111111111111',
    profileName: 'che:2',
    type: 'RESET',
    status,
    sourceMode: 'BRANCH',
    sourceRef: 'private/source-ref',
    payload: {
        install: { scenarioId: 1010 },
        requestedSource: 'CURRENT',
        publicAnnouncement: {
            enabled: true,
            scenarioId: 1010,
            scenarioTitle: '황건적의 난',
            scheduledAt: '2026-08-27T05:00:00.000Z',
            preopenAt: '2026-08-27T05:30:00.000Z',
            openAt: '2026-08-27T11:00:00.000Z',
            turnTermMinutes: 60,
            fictionMode: '가상',
            npcMode: 1,
            defaultStatTotal: 70,
            otherTextInfo: '랜덤 임관',
            autorunUser: {
                limitMinutes: 1440,
                options: ['develop', 'battle'],
            },
            requestedBy: 'must-not-leak',
            reason: 'must-not-leak',
        },
    },
    reason: 'private reason',
    requestedBy: 'admin-id',
    scheduledAt: '2026-08-27T05:00:00.000Z',
    error: 'private error',
    createdAt: '2026-08-23T00:00:00.000Z',
    updatedAt: '2026-08-23T00:00:00.000Z',
});

describe('resolveUpcomingResetAnnouncement', () => {
    it('projects only the public snapshot while the delayed build is queued', () => {
        const result = resolveUpcomingResetAnnouncement(buildOperation(), new Date('2026-08-27T04:00:00.000Z'));

        expect(result).toEqual({
            phase: 'SCHEDULED',
            scheduledAt: '2026-08-27T05:00:00.000Z',
            preopenAt: '2026-08-27T05:30:00.000Z',
            openAt: '2026-08-27T11:00:00.000Z',
            scenarioId: 1010,
            scenarioTitle: '황건적의 난',
            turnTermMinutes: 60,
            fictionMode: '가상',
            npcMode: 1,
            defaultStatTotal: 70,
            otherTextInfo: '랜덤 임관',
            autorunUser: {
                limitMinutes: 1440,
                options: ['develop', 'battle'],
            },
        });
        expect(result).not.toHaveProperty('sourceRef');
        expect(result).not.toHaveProperty('requestedBy');
        expect(result).not.toHaveProperty('reason');
        expect(result).not.toHaveProperty('error');
    });

    it('moves from preparation to a truthful delay state without changing the profile lifecycle', () => {
        expect(
            resolveUpcomingResetAnnouncement(buildOperation('RUNNING'), new Date('2026-08-27T05:10:00.000Z'))
        ).toMatchObject({ phase: 'PREPARING' });
        expect(
            resolveUpcomingResetAnnouncement(buildOperation('RUNNING'), new Date('2026-08-27T05:31:00.000Z'))
        ).toMatchObject({ phase: 'DELAYED' });
    });

    it('keeps a completed build ready for RESERVED handoff and removes cancelled or failed announcements', () => {
        const succeeded = buildOperation('SUCCEEDED');
        expect(resolveUpcomingResetAnnouncement(succeeded, new Date('2026-08-27T05:20:00.000Z'))).toMatchObject({
            phase: 'READY',
        });
        expect(shouldExposeUpcomingReset(succeeded, 'RESERVED')).toBe(true);
        expect(shouldExposeUpcomingReset(succeeded, 'PREOPEN')).toBe(false);
        expect(shouldExposeUpcomingReset(succeeded, 'RUNNING')).toBe(false);
        for (const status of ['CANCELLED', 'FAILED'] as const) {
            expect(
                resolveUpcomingResetAnnouncement(buildOperation(status), new Date('2026-08-27T04:00:00.000Z'))
            ).toBeNull();
        }
    });

    it('keeps an unpublished snapshot hidden until a completed build is ready', () => {
        const unpublished = buildOperation();
        unpublished.payload = {
            publicAnnouncement: {
                ...(unpublished.payload as { publicAnnouncement: Record<string, unknown> }).publicAnnouncement,
                enabled: false,
                scheduledAt: null,
            },
        };
        expect(resolveUpcomingResetAnnouncement(unpublished, new Date('2026-08-27T04:00:00.000Z'))).toBeNull();
        unpublished.status = 'SUCCEEDED';
        expect(resolveUpcomingResetAnnouncement(unpublished, new Date('2026-08-27T04:00:00.000Z'))).toMatchObject({
            phase: 'READY',
            scheduledAt: null,
        });

        const incomplete = buildOperation();
        incomplete.payload = { publicAnnouncement: { enabled: true, scenarioTitle: '황건적의 난' } };
        expect(resolveUpcomingResetAnnouncement(incomplete, new Date('2026-08-27T04:00:00.000Z'))).toBeNull();
    });

    it('does not restore an older published notice after a newer reset replaces it', () => {
        const olderPublished = buildOperation('SUCCEEDED');
        const newerImmediate = {
            ...buildOperation('SUCCEEDED'),
            id: '22222222-2222-4222-8222-222222222222',
            payload: {
                install: {
                    scenarioId: 1011,
                    preopenAt: '2026-09-01T05:30:00.000Z',
                    openAt: '2026-09-01T11:00:00.000Z',
                },
            },
            scheduledAt: undefined,
            createdAt: '2026-08-27T01:00:00.000Z',
            updatedAt: '2026-08-27T01:00:00.000Z',
        } satisfies GatewayOperationRecord;

        const result = resolveUpcomingResetAnnouncements(
            [newerImmediate, olderPublished],
            new Map([['che:2', 'RESERVED']]),
            new Date('2026-08-27T04:00:00.000Z')
        );

        expect(result.has('che:2')).toBe(false);
    });

    it('treats a newer cancelled reset as the announcement boundary', () => {
        const olderPublished = buildOperation('SUCCEEDED');
        const newerCancelled = {
            ...buildOperation('CANCELLED'),
            id: '33333333-3333-4333-8333-333333333333',
            createdAt: '2026-08-27T01:00:00.000Z',
            updatedAt: '2026-08-27T01:00:00.000Z',
        } satisfies GatewayOperationRecord;

        const result = resolveUpcomingResetAnnouncements(
            [newerCancelled, olderPublished],
            new Map([['che:2', 'RESERVED']]),
            new Date('2026-08-27T04:00:00.000Z')
        );

        expect(result.has('che:2')).toBe(false);
    });
});
