import { describe, expect, it } from 'vitest';
import { Person } from '../../../src/models/Person.js';
import { Player } from '../../../src/models/Player.js';
import {
  buildAlertApnsRequest,
  buildLiveActivityApnsRequest,
} from '../../../src/services/notifications/pushNotificationService.js';
import {
  createLiveActivityContentState,
  shouldSendLiveActivityUpdate,
} from '../../../src/services/notifications/liveActivityManager.js';

const apnsConfig = {
  keyId: 'KEY123',
  teamId: 'TEAM123',
  keyData: 'unused',
  bundleId: 'lichun.lichunWebsocket',
  production: true,
};

function createPlayer(): Player {
  return new Player({
    userId: 'player-live-1',
    status: 'playing',
    controller: 'active',
    date: '05-26',
    hourOfDay: 14,
    minuteOfHour: 7,
    season: 'spring',
    gameSpeed: 500,
    character: new Person({
      id: 'char-live-1',
      firstname: 'Alex',
      lastname: 'River',
      ageYears: 18,
      location: 'Campus',
      intraDayMessage: 'Studying for finals',
      calcEnergy: 72,
      health: 91,
      happiness: 64,
      status: 'alive',
    }),
  });
}

describe('APNS Live Activity requests', () => {
  it('keeps regular alert pushes on the alert push type and base bundle topic', () => {
    const request = buildAlertApnsRequest('device-token', {
      title: 'BaoLife',
      body: 'A new life event happened.',
      category: 'life_event',
      sound: 'default',
    }, apnsConfig, 'jwt-token');

    expect(request.headers['apns-push-type']).toBe('alert');
    expect(request.headers['apns-topic']).toBe('lichun.lichunWebsocket');
    expect(request.payload.aps).toMatchObject({
      alert: {
        title: 'BaoLife',
        body: 'A new life event happened.',
      },
      sound: 'default',
      category: 'life_event',
    });
  });

  it('builds ActivityKit updates without alert sound or badge payloads', () => {
    const request = buildLiveActivityApnsRequest('activity-token', {
      event: 'update',
      timestamp: 1_700_000_000,
      contentState: {
        gameDate: '05-26',
        gameTime: '2:07 PM',
        age: 18,
        season: 'spring',
        location: 'Campus',
        statusMessage: 'Studying for finals',
        energy: 72,
        health: 91,
        happiness: 64,
        speedLabel: 'Normal',
        isRunning: true,
        updatedAt: '2026-05-26T14:00:00.000Z',
      },
    }, apnsConfig, 'jwt-token');

    expect(request.headers['apns-push-type']).toBe('liveactivity');
    expect(request.headers['apns-topic']).toBe('lichun.lichunWebsocket.push-type.liveactivity');
    expect(request.payload.aps).toEqual({
      event: 'update',
      timestamp: 1_700_000_000,
      'content-state': {
        gameDate: '05-26',
        gameTime: '2:07 PM',
        age: 18,
        season: 'spring',
        location: 'Campus',
        statusMessage: 'Studying for finals',
        energy: 72,
        health: 91,
        happiness: 64,
        speedLabel: 'Normal',
        isRunning: true,
        updatedAt: '2026-05-26T14:00:00.000Z',
      },
    });
  });

  it('builds ActivityKit end events with a dismissal date', () => {
    const request = buildLiveActivityApnsRequest('activity-token', {
      event: 'end',
      timestamp: 1_700_000_000,
      dismissalDate: 1_700_000_060,
      contentState: {
        gameDate: '05-26',
        gameTime: '2:07 PM',
        age: 18,
        season: 'spring',
        location: 'Campus',
        statusMessage: 'Session ended',
        energy: 72,
        health: 91,
        happiness: 64,
        speedLabel: 'Normal',
        isRunning: false,
        updatedAt: '2026-05-26T14:00:00.000Z',
      },
    }, apnsConfig, 'jwt-token');

    expect(request.headers['apns-push-type']).toBe('liveactivity');
    expect(request.payload.aps.event).toBe('end');
    expect(request.payload.aps['dismissal-date']).toBe(1_700_000_060);
    expect(request.payload.aps).not.toHaveProperty('alert');
    expect(request.payload.aps).not.toHaveProperty('sound');
    expect(request.payload.aps).not.toHaveProperty('badge');
  });
});

describe('Live Activity content state', () => {
  it('maps player state to the glanceable content allowed on the Lock Screen', () => {
    const player = createPlayer();

    const state = createLiveActivityContentState(player, new Date('2026-05-26T14:00:00.000Z'));

    expect(state).toEqual({
      gameDate: '05-26',
      gameTime: '2:07 PM',
      age: 18,
      season: 'spring',
      location: 'Campus',
      statusMessage: 'Studying for finals',
      energy: 72,
      health: 91,
      happiness: 64,
      speedLabel: 'Normal',
      isRunning: true,
      updatedAt: '2026-05-26T14:00:00.000Z',
    });
  });

  it('throttles routine remote updates to 30 seconds but allows major updates', () => {
    const player = createPlayer();
    player.liveActivity = {
      activityId: 'activity-1',
      pushToken: 'token-1',
      characterId: 'char-live-1',
      startedAt: '2026-05-26T14:00:00.000Z',
      lastSentAt: '2026-05-26T14:00:10.000Z',
    };

    expect(shouldSendLiveActivityUpdate(player, new Date('2026-05-26T14:00:25.000Z'))).toBe(false);
    expect(shouldSendLiveActivityUpdate(player, new Date('2026-05-26T14:00:25.000Z'), { major: true })).toBe(true);
    expect(shouldSendLiveActivityUpdate(player, new Date('2026-05-26T14:00:41.000Z'))).toBe(true);
  });
});
