/**
 * Content-safety: dating/swipe is adults only (18+).
 *
 * Asserts that handleGetSwipeCharacter no-ops for an under-18 player (serves no
 * candidate) and that handleSwipeMatch refuses to create a match for one.
 */
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';

const { createCharacterMock, establishMatchRelationshipMock } = vi.hoisted(() => ({
  createCharacterMock: vi.fn(() => new Person({ id: 'swipe-1', firstname: 'Pat', lastname: 'Lee' })),
  establishMatchRelationshipMock: vi.fn(() => ({ id: 'rel-1', relationshipStatus: 'Dating' })),
}));

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

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

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 { handleGetSwipeCharacter, handleSwipeMatch } from '../../src/handlers/romance.js';

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

function createPlayer(ageYears: number): Player {
  const character = new Person({
    id: 'char-1',
    firstname: 'Test',
    lastname: 'Player',
    sex: 'Male',
    ageYears,
  });
  return new Player({ userId: 'user-1', character, status: 'playing', date: '2026-05-28' });
}

describe('swipe dating content-safety age gate (18+)', () => {
  beforeEach(() => vi.clearAllMocks());

  it('handleGetSwipeCharacter no-ops for an under-18 player', async () => {
    const session = new MockSession(createPlayer(16));
    await handleGetSwipeCharacter(undefined, session as unknown as Parameters<typeof handleGetSwipeCharacter>[1]);

    // No swipe character is generated and none is stored on the player.
    expect(createCharacterMock).not.toHaveBeenCalled();
    expect((session.player as unknown as { swipeCharacter: unknown }).swipeCharacter).toBeUndefined();
    expect(session.sent).toContainEqual(expect.objectContaining({ type: 'getSwipeCharacter', swipeCharacter: false }));
  });

  it('handleSwipeMatch refuses to create a match for an under-18 player', async () => {
    const session = new MockSession(createPlayer(17));
    // Seed a stale swipe character to ensure even that cannot create a match.
    (session.player as unknown as { swipeCharacter: Person }).swipeCharacter = new Person({
      id: 'swipe-1',
      firstname: 'Pat',
      lastname: 'Lee',
      ageYears: 18,
    });

    await handleSwipeMatch(undefined, session as unknown as Parameters<typeof handleSwipeMatch>[1]);

    expect(establishMatchRelationshipMock).not.toHaveBeenCalled();
    expect(session.player.r.some((p) => p.id === 'swipe-1')).toBe(false);
    expect(session.sent).toContainEqual(expect.objectContaining({ type: 'error' }));
  });

  it('handleGetSwipeCharacter serves a candidate for an adult player', async () => {
    const session = new MockSession(createPlayer(25));
    await handleGetSwipeCharacter(undefined, session as unknown as Parameters<typeof handleGetSwipeCharacter>[1]);
    expect(createCharacterMock).toHaveBeenCalledTimes(1);
  });
});
