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

import { createEventRegistry } from '../../src/events/v2/registry.js';
import type { EventDefinition } from '../../src/events/v2/types.js';
import { EventEngine } from '../../src/events/v2/engine/EventEngine.js';

describe('EventEngine selection and prompt creation', () => {
  it('creates an event_prompt for the first eligible event when player has no pending prompts', async () => {
    const definitions: EventDefinition[] = [
      {
        id: 'event_child_only',
        category: 'test',
        prompt: 'Child prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        minAge: 0,
        maxAge: 12,
      },
      {
        id: 'event_adult',
        category: 'test',
        prompt: 'Adult prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        minAge: 18,
      },
    ];

    const registry = createEventRegistry(definitions);

    const store = {
      getPendingEventInstances: vi.fn().mockResolvedValue([]),
      createEventInstance: vi.fn().mockResolvedValue({
        instanceId: 'evt-inst-1',
        playerId: 'player-1',
        eventId: 'event_adult',
        status: 'pending',
        prompt: 'Adult prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        context: null,
        selectedChoiceId: null,
        createdAt: new Date().toISOString(),
        answeredAt: null,
        resolvedAt: null,
      }),
    };

    const engine = new EventEngine(registry, store);

    const envelope = await engine.promptNext({
      userId: 'player-1',
      c: { ageYears: 22 },
    });

    expect(store.getPendingEventInstances).toHaveBeenCalledWith('player-1');
    expect(store.createEventInstance).toHaveBeenCalledTimes(1);
    expect(envelope).toMatchObject({
      type: 'event_prompt',
      eventId: 'event_adult',
      instanceId: 'evt-inst-1',
      prompt: 'Adult prompt',
    });
  });

  it('returns null when there is already a pending event instance', async () => {
    const definitions: EventDefinition[] = [
      {
        id: 'event_adult',
        category: 'test',
        prompt: 'Adult prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        minAge: 18,
      },
    ];

    const registry = createEventRegistry(definitions);

    const store = {
      getPendingEventInstances: vi.fn().mockResolvedValue([
        {
          instanceId: 'evt-inst-existing',
          status: 'pending',
        },
      ]),
      createEventInstance: vi.fn(),
    };

    const engine = new EventEngine(registry, store as any);

    const envelope = await engine.promptNext({
      userId: 'player-1',
      c: { ageYears: 22 },
    });

    expect(envelope).toBeNull();
    expect(store.createEventInstance).not.toHaveBeenCalled();
  });

  it('records last-fired day and re-fires repeatable events only after cooldown', async () => {
    const definitions: EventDefinition[] = [
      {
        id: 'event_repeat',
        category: 'test',
        prompt: 'Repeat prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        repeatable: true,
        cooldownDays: 3,
      },
    ];

    const registry = createEventRegistry(definitions);

    const createEventInstance = vi.fn().mockImplementation(async () => ({
      instanceId: 'evt-inst-1',
      playerId: 'player-1',
      eventId: 'event_repeat',
      status: 'pending',
      prompt: 'Repeat prompt',
      choices: [{ choiceId: 'ok', text: 'Ok' }],
      context: null,
      selectedChoiceId: null,
      createdAt: new Date().toISOString(),
      answeredAt: null,
      resolvedAt: null,
    }));

    // No pending instances, so the engine always tries to select.
    const store = {
      getPendingEventInstances: vi.fn().mockResolvedValue([]),
      createEventInstance,
    };

    const engine = new EventEngine(registry, store as any);

    const player: any = {
      userId: 'player-1',
      c: { ageYears: 22 },
      askedQuestions: new Set<string>(),
      currentDay: 10,
    };

    // Day 10: fires, records last-fired.
    const first = await engine.promptNext(player);
    expect(first).toMatchObject({ type: 'event_prompt', eventId: 'event_repeat' });
    expect(player.eventLastFired).toMatchObject({ event_repeat: 10 });
    expect(createEventInstance).toHaveBeenCalledTimes(1);

    // Day 12: only 2 days elapsed (< cooldown 3) -> no fire.
    player.currentDay = 12;
    const second = await engine.promptNext(player);
    expect(second).toBeNull();
    expect(createEventInstance).toHaveBeenCalledTimes(1);

    // Day 13: 3 days elapsed (>= cooldown) -> fires again (repeatable).
    player.currentDay = 13;
    const third = await engine.promptNext(player);
    expect(third).toMatchObject({ type: 'event_prompt', eventId: 'event_repeat' });
    expect(player.eventLastFired).toMatchObject({ event_repeat: 13 });
    expect(createEventInstance).toHaveBeenCalledTimes(2);
  });

  it('returns null when no event is eligible for the player', async () => {
    const definitions: EventDefinition[] = [
      {
        id: 'event_child_only',
        category: 'test',
        prompt: 'Child prompt',
        choices: [{ choiceId: 'ok', text: 'Ok' }],
        minAge: 0,
        maxAge: 12,
      },
    ];

    const registry = createEventRegistry(definitions);

    const store = {
      getPendingEventInstances: vi.fn().mockResolvedValue([]),
      createEventInstance: vi.fn(),
    };

    const engine = new EventEngine(registry, store as any);

    const envelope = await engine.promptNext({
      userId: 'player-1',
      c: { ageYears: 22 },
    });

    expect(envelope).toBeNull();
    expect(store.createEventInstance).not.toHaveBeenCalled();
  });
});
