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

import { EventResponder } from '../../src/events/v2/engine/respond.js';
import { isEventEligible, playerHasFlag } from '../../src/events/v2/engine/selector.js';
import { createEventRegistry } from '../../src/events/v2/registry.js';
import { careerCatalog, CAREER_FLAGS } from '../../src/events/v2/catalog/career.js';
import type { EventPlayerContext } from '../../src/events/v2/types.js';

/**
 * In-memory event-instance store that holds exactly one pending instance,
 * matching the EventResponder's contract.
 */
function makeStore(eventId: string) {
  let pending = [{ instanceId: 'inst-1', playerId: 'p1', eventId, status: 'pending' as const }];
  return {
    getPendingEventInstances: async () => pending,
    answerEventInstance: async () => true,
    resolveEventInstance: async () => {
      pending = [];
      return true;
    },
  };
}

function makePlayer(overrides: Partial<EventPlayerContext> = {}): EventPlayerContext {
  return {
    userId: 'p1',
    c: { ageYears: 30, occupation: 'work', money: 12000, happiness: 50, stress: 10, energy: 100 },
    askedQuestions: new Set<string>(),
    eventLastFired: {},
    flags: new Set<string>(),
    ...overrides,
  };
}

const businessOutcome = careerCatalog.find((e) => e.id === 'businessOutcome')!;
const sideHustle = careerCatalog.find((e) => e.id === 'sideHustleProgress')!;
const layoffSearch = careerCatalog.find((e) => e.id === 'layoffJobSearch')!;

describe('flag mechanism: choice-gated follow-up eligibility', () => {
  it('businessOutcome is INELIGIBLE before the all-in choice is made', () => {
    const player = makePlayer();
    // stage 1 has not fired and no flag set
    expect(isEventEligible(businessOutcome, player, 1)).toBe(false);
  });

  it('the all-in choice sets the startedBusiness flag and unlocks businessOutcome', async () => {
    const player = makePlayer();
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('startOwnBusiness'));

    const result = await responder.respond(player, { eventId: 'startOwnBusiness', choiceId: 'allin' });
    expect(result.type).toBe('event_resolved');

    // Flag is now set, stage 1 recorded in askedQuestions.
    expect(playerHasFlag(player, CAREER_FLAGS.startedBusiness)).toBe(true);
    expect((player.askedQuestions as Set<string>).has('startOwnBusiness')).toBe(true);

    // Stage 2 (businessOutcome) is now eligible; the side-hustle follow-up is NOT.
    expect(isEventEligible(businessOutcome, player, 1)).toBe(true);
    expect(isEventEligible(sideHustle, player, 1)).toBe(false);
  });

  it('the side-hustle choice unlocks only sideHustleProgress, not businessOutcome', async () => {
    const player = makePlayer();
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('startOwnBusiness'));

    await responder.respond(player, { eventId: 'startOwnBusiness', choiceId: 'side' });

    expect(playerHasFlag(player, CAREER_FLAGS.businessSideHustle)).toBe(true);
    expect(isEventEligible(sideHustle, player, 1)).toBe(true);
    expect(isEventEligible(businessOutcome, player, 1)).toBe(false);
  });

  it('the "stay" choice sets no flag, so neither follow-up unlocks', async () => {
    const player = makePlayer();
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('startOwnBusiness'));

    await responder.respond(player, { eventId: 'startOwnBusiness', choiceId: 'stay' });

    expect(playerHasFlag(player, CAREER_FLAGS.startedBusiness)).toBe(false);
    expect(playerHasFlag(player, CAREER_FLAGS.businessSideHustle)).toBe(false);
    expect(isEventEligible(businessOutcome, player, 1)).toBe(false);
    expect(isEventEligible(sideHustle, player, 1)).toBe(false);
  });

  it('layoff internal-transfer branch sets no laid-off flag, so job search does not fire', async () => {
    const player = makePlayer();
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('layoffNotice'));

    await responder.respond(player, { eventId: 'layoffNotice', choiceId: 'internal' });

    expect(playerHasFlag(player, CAREER_FLAGS.wasLaidOff)).toBe(false);
    expect(isEventEligible(layoffSearch, player, 1)).toBe(false);
  });

  it('layoff accept branch sets the laid-off flag and unlocks the job search follow-up', async () => {
    const player = makePlayer();
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('layoffNotice'));

    await responder.respond(player, { eventId: 'layoffNotice', choiceId: 'accept' });

    expect(playerHasFlag(player, CAREER_FLAGS.wasLaidOff)).toBe(true);
    expect(isEventEligible(layoffSearch, player, 1)).toBe(true);
  });

  it('flags round-trip through an array container (post-JSON load shape)', async () => {
    const player = makePlayer({ flags: [] as unknown as Set<string> });
    const registry = createEventRegistry(careerCatalog);
    const responder = new EventResponder(registry, makeStore('startOwnBusiness'));

    await responder.respond(player, { eventId: 'startOwnBusiness', choiceId: 'allin' });

    expect(playerHasFlag(player, CAREER_FLAGS.startedBusiness)).toBe(true);
    expect(Array.isArray((player as { flags?: unknown }).flags)).toBe(true);
  });
});
