/**
 * Romance handler content-safety gate (T003).
 *
 * Asserts the adults-only (18+) gate added to handleDateNight and handlePartnerGift:
 * an under-18 player is blocked (no affinity/cost change, error response) and the
 * underlying relationship-service call is never reached.
 */
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';

const {
  dateNightMock,
  partnerGiftMock,
  getActiveRelationshipMock,
} = vi.hoisted(() => ({
  dateNightMock: vi.fn(() => ({ id: 'dn', message: 'date', success: true })),
  partnerGiftMock: vi.fn(() => ({ id: 'pg', message: 'gift', success: true })),
  getActiveRelationshipMock: vi.fn(() => null),
}));

vi.mock('../../src/services/relationships/index.js', () => ({
  romance: vi.fn(() => true),
  establishMatchRelationship: vi.fn(() => null),
  dateNight: dateNightMock,
  breakUp: vi.fn(() => true),
  divorce: vi.fn(() => true),
  partnerGift: partnerGiftMock,
  getDateIdeas: vi.fn(() => []),
  propose: vi.fn(() => true),
  marry: vi.fn(() => true),
  getActiveRelationship: getActiveRelationshipMock,
}));

vi.mock('../../src/services/character/character_manager.js', () => ({
  createCharacter: vi.fn(),
}));

vi.mock('../../src/services/dating/compatibility.js', () => ({
  calculateCompatibility: vi.fn(() => 50),
}));

vi.mock('../../src/services/retention/index.js', () => ({
  updateQuestProgress: vi.fn(async () => null),
  sendQuestProgress: vi.fn(),
}));

import { handleDateNight, handlePartnerGift } from '../../src/handlers/romance.js';

class MockSession {
  player: Player;
  sent: any[] = [];
  constructor(player: Player) {
    this.player = player;
  }
  send(message: any): void {
    this.sent.push(message);
  }
  sendPlayerObject(): void {
    this.sent.push({ type: 'playerObject' });
  }
}

function createPlayer(ageYears: number, partner?: Person): Player {
  const character = new Person({
    id: 'char-1',
    firstname: 'Test',
    lastname: 'Player',
    sex: 'Male',
    ageYears,
    money: 1000,
    energy: 100,
  });
  const player = new Player({
    userId: 'user-1',
    character,
    r: partner ? [partner] : [],
    status: 'playing',
    date: '2026-05-28',
  });
  return player;
}

describe('romance content-safety age gate (18+) — date night & gift', () => {
  beforeEach(() => vi.clearAllMocks());

  it('handleDateNight is blocked for an under-18 player (service not called, error sent)', async () => {
    const session = new MockSession(createPlayer(16));
    await handleDateNight('Coffee Date', session as unknown as Parameters<typeof handleDateNight>[1]);

    expect(dateNightMock).not.toHaveBeenCalled();
    expect(session.sent).toContainEqual(
      expect.objectContaining({ type: 'error', message: 'Dating unlocks at age 18.' })
    );
    // No success/messageEvent emitted.
    expect(session.sent.some((m) => m?.type === 'messageEvent')).toBe(false);
  });

  it('handlePartnerGift is blocked for an under-18 player (service not called, no cost change)', async () => {
    const session = new MockSession(createPlayer(17));
    const startMoney = session.player.c.money;
    await handlePartnerGift('partner-1', session as unknown as Parameters<typeof handlePartnerGift>[1]);

    expect(partnerGiftMock).not.toHaveBeenCalled();
    expect(session.player.c.money).toBe(startMoney);
    expect(session.sent).toContainEqual(
      expect.objectContaining({ type: 'error', message: 'Dating unlocks at age 18.' })
    );
  });

  it('handlePartnerGift is blocked when the resolved partner is under 18 (adult player)', async () => {
    const minorPartner = new Person({ id: 'partner-1', firstname: 'Pat', lastname: 'Lee', sex: 'Female', ageYears: 17 });
    const session = new MockSession(createPlayer(25, minorPartner));
    await handlePartnerGift('partner-1', session as unknown as Parameters<typeof handlePartnerGift>[1]);

    expect(partnerGiftMock).not.toHaveBeenCalled();
    expect(session.sent).toContainEqual(
      expect.objectContaining({ type: 'error', message: 'Dating unlocks at age 18.' })
    );
  });

  it('handlePartnerGift proceeds for two adults', async () => {
    const adultPartner = new Person({ id: 'partner-1', firstname: 'Pat', lastname: 'Lee', sex: 'Female', ageYears: 24 });
    const session = new MockSession(createPlayer(25, adultPartner));
    await handlePartnerGift('partner-1', session as unknown as Parameters<typeof handlePartnerGift>[1]);

    expect(partnerGiftMock).toHaveBeenCalledTimes(1);
    expect(session.sent.some((m) => m?.type === 'messageEvent')).toBe(true);
  });
});
