/**
 * Player Model Tests
 */
import { describe, it, expect } from 'vitest';
import { Player, type GameStatus, type Season } from '../../src/models/Player.js';
import { Person } from '../../src/models/Person.js';

describe('Player Model', () => {
  describe('constructor', () => {
    it('should create a Player with minimal data', () => {
      const person = new Person({ id: 'char-1', firstname: 'Test' });
      const player = new Player({
        userId: 'user-1',
        character: person,
      });

      expect(player.userId).toBe('user-1');
      expect(player.c.firstname).toBe('Test');
      expect(player.r).toEqual([]);
    });

    it('should create a Player with full game state', () => {
      const character = new Person({ id: 'char-1', firstname: 'Jane', ageYears: 20 });
      const player = new Player({
        userId: 'user-2',
        character: character,
        date: '2024-06-15',
        time: '14:30',
        season: 'summer',
        status: 'playing',
        hourOfDay: 14,
        minuteOfHour: 30,
        gameSpeed: 1000,
      });

      expect(player.date).toBe('2024-06-15');
      expect(player.time).toBe('14:30');
      expect(player.season).toBe('summer');
      expect(player.status).toBe('playing');
      expect(player.hourOfDay).toBe(14);
      expect(player.minuteOfHour).toBe(30);
      expect(player.gameSpeed).toBe(1000);
    });
  });

  describe('character reference', () => {
    it('should access main character via c property', () => {
      const character = new Person({
        id: 'main-char',
        firstname: 'John',
        lastname: 'Doe',
        ageYears: 25,
        money: 5000,
        diamonds: 50,
      });
      const player = new Player({ userId: 'u1', character });

      expect(player.c.firstname).toBe('John');
      expect(player.c.lastname).toBe('Doe');
      expect(player.c.ageYears).toBe(25);
      expect(player.c.money).toBe(5000);
      expect(player.c.diamonds).toBe(50);
    });

    it('should allow modifying character stats', () => {
      const character = new Person({ id: 'mod-char', firstname: 'Test', money: 100 });
      const player = new Player({ userId: 'u1', character });

      player.c.money = 200;
      expect(player.c.money).toBe(200);
    });
  });

  describe('relationships', () => {
    it('should store relationship list in r property', () => {
      const character = new Person({ id: 'char', firstname: 'Main' });
      const friend = new Person({ id: 'friend-1', firstname: 'Friend', relationships: ['friend'] });
      const family = new Person({ id: 'mom-1', firstname: 'Mom', relationships: ['mother'] });

      const player = new Player({
        userId: 'u1',
        character,
        r: [friend, family],
      });

      expect(player.r).toHaveLength(2);
      expect(player.r[0].firstname).toBe('Friend');
      expect(player.r[1].firstname).toBe('Mom');
    });

    it('should handle empty relationships', () => {
      const character = new Person({ id: 'char', firstname: 'Lonely' });
      const player = new Player({ userId: 'u1', character });

      expect(player.r).toEqual([]);
    });

    it('should support many relationships', () => {
      const character = new Person({ id: 'char', firstname: 'Popular' });
      const relationships: Person[] = [];
      for (let i = 0; i < 50; i++) {
        relationships.push(new Person({ id: `rel-${i}`, firstname: `Friend${i}` }));
      }

      const player = new Player({ userId: 'u1', character, r: relationships });
      expect(player.r).toHaveLength(50);
    });
  });

  describe('game status', () => {
    const statuses: GameStatus[] = ['paused', 'playing', 'dead', 'creating'];

    statuses.forEach(status => {
      it(`should handle status: ${status}`, () => {
        const character = new Person({ id: 'char', firstname: 'Test' });
        const player = new Player({ userId: 'u1', character, status });
        expect(player.status).toBe(status);
      });
    });
  });

  describe('seasons', () => {
    const seasons: Season[] = ['spring', 'summer', 'autumn', 'winter'];

    seasons.forEach(season => {
      it(`should handle season: ${season}`, () => {
        const character = new Person({ id: 'char', firstname: 'Test' });
        const player = new Player({ userId: 'u1', character, season });
        expect(player.season).toBe(season);
      });
    });
  });

  describe('time management', () => {
    it('should track hour and minute', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        hourOfDay: 8,
        minuteOfHour: 30,
      });

      expect(player.hourOfDay).toBe(8);
      expect(player.minuteOfHour).toBe(30);
    });

    it('should handle midnight (0:00)', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        hourOfDay: 0,
        minuteOfHour: 0,
      });

      expect(player.hourOfDay).toBe(0);
      expect(player.minuteOfHour).toBe(0);
    });

    it('should handle late night (23:59)', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        hourOfDay: 23,
        minuteOfHour: 59,
      });

      expect(player.hourOfDay).toBe(23);
      expect(player.minuteOfHour).toBe(59);
    });
  });

  describe('game speed', () => {
    const speeds = [10000, 1000, 500, 50, 20, 1];

    speeds.forEach(speed => {
      it(`should handle game speed: ${speed}`, () => {
        const character = new Person({ id: 'char', firstname: 'Test' });
        const player = new Player({ userId: 'u1', character, gameSpeed: speed });
        expect(player.gameSpeed).toBe(speed);
      });
    });
  });

  describe('active conversations', () => {
    it('should store active conversations', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        conversations: [
          { id: 'conv-1', character: 'friend-1', conversation: [], question: 0, unread: false },
          { id: 'conv-2', character: 'mom-1', conversation: [], question: 0, unread: false },
        ],
      });

      expect(player.conversations).toHaveLength(2);
    });
  });

  describe('userId reference', () => {
    it('should store user ID for authentication', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'user-auth-123',
        character,
      });

      expect(player.userId).toBe('user-auth-123');
    });
  });

  describe('serialization', () => {
    it('should serialize player to JSON', () => {
      const character = new Person({ id: 'char', firstname: 'Test', money: 1000 });
      const player = new Player({
        userId: 'u1',
        character,
        date: '2024-01-01',
        status: 'playing',
      });

      const json = JSON.stringify(player);
      const parsed = JSON.parse(json);

      expect(parsed.userId).toBe('u1');
      expect(parsed.date).toBe('2024-01-01');
      expect(parsed.status).toBe('playing');
      expect(parsed.c.firstname).toBe('Test');
      expect(parsed.c.money).toBe(1000);
    });
  });

  describe('offline stats', () => {
    it('should track offline progress', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        offlineStats: {
          minutesOffline: 60,
          lastOnline: new Date('2024-01-01T10:00:00Z'),
        },
      });

      expect(player.offlineStats.minutesOffline).toBe(60);
    });
  });

  describe('locations', () => {
    it('should store location list in l property', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        l: [
          { id: 'loc-1', type: 'home', name: 'Home' },
          { id: 'loc-2', type: 'school', name: 'School' },
        ],
      });

      expect(player.l).toHaveLength(2);
      expect(player.l[0].type).toBe('home');
      expect(player.l[1].type).toBe('school');
    });

    it('should handle empty locations', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({ userId: 'u1', character });

      expect(player.l).toEqual([]);
    });
  });

  describe('isActive getter', () => {
    it('should return true when playing and active', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        status: 'playing',
        controller: 'active',
      });

      expect(player.isActive).toBe(true);
    });

    it('should return false when paused', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        status: 'paused',
        controller: 'active',
      });

      expect(player.isActive).toBe(false);
    });

    it('should return false when inactive controller', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        status: 'playing',
        controller: 'inactive',
      });

      expect(player.isActive).toBe(false);
    });
  });

  describe('getTicksForSpeed', () => {
    it('should return game speed value', () => {
      const character = new Person({ id: 'char', firstname: 'Test' });
      const player = new Player({
        userId: 'u1',
        character,
        gameSpeed: 500,
      });

      expect(player.getTicksForSpeed()).toBe(500);
    });
  });
});
