import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';
import type { PlayerSession } from '../../src/game/PlayerSession.js';

class MockPlayerSession {
  player: Player;
  sentMessages: unknown[] = [];
  savePlayerCalled = false;

  constructor(player: Player) {
    this.player = player;
  }

  send(message: unknown) {
    this.sentMessages.push(message);
  }

  sendPlayerObject() {
    this.sentMessages.push({ type: 'playerObject', player: this.player });
  }

  async savePlayer() {
    this.savePlayerCalled = true;
  }

  getLastMessage(): Record<string, unknown> {
    return this.sentMessages[this.sentMessages.length - 1] as Record<string, unknown>;
  }
}

function createTestPlayer(): Player {
  const character = new Person({
    id: 'char-1',
    firstname: 'Test',
    lastname: 'Player',
    ageYears: 25,
    money: 1000,
    diamonds: 50,
    energy: 80,
    health: 90,
    happiness: 70,
  });

  return new Player({
    userId: 'user-1',
    character,
    r: [],
    status: 'playing',
    date: '2024-06-15',
    hourOfDay: 10,
    minuteOfHour: 30,
  });
}

describe('debug handlers', () => {
  const envSnapshot = { ...process.env };

  afterEach(() => {
    process.env = { ...envSnapshot };
    vi.resetModules();
  });

  async function importDebugHandlers() {
    return import('../../src/handlers/debug.js');
  }

  describe('isDebugEnabled', () => {
    it('returns true when DEBUG=true in production', async () => {
      process.env.DEBUG = 'true';
      process.env.NODE_ENV = 'production';
      vi.resetModules();
      const { isDebugEnabled } = await importDebugHandlers();
      expect(isDebugEnabled()).toBe(true);
    });

    it('returns false when DEBUG is unset in production', async () => {
      delete process.env.DEBUG;
      process.env.NODE_ENV = 'production';
      vi.resetModules();
      const { isDebugEnabled } = await importDebugHandlers();
      expect(isDebugEnabled()).toBe(false);
    });

    it('returns true in non-production without DEBUG', async () => {
      delete process.env.DEBUG;
      process.env.NODE_ENV = 'development';
      vi.resetModules();
      const { isDebugEnabled } = await importDebugHandlers();
      expect(isDebugEnabled()).toBe(true);
    });
  });

  describe('handleDebugGrant', () => {
    beforeEach(() => {
      process.env.DEBUG = 'true';
      process.env.NODE_ENV = 'production';
      vi.resetModules();
    });

    it('adds money, energy, and diamonds', async () => {
      const { handleDebugGrant } = await importDebugHandlers();
      const player = createTestPlayer();
      const session = new MockPlayerSession(player);

      await handleDebugGrant(
        { money: 500, energy: 10, diamonds: 25 },
        session as unknown as PlayerSession
      );

      expect(player.c.money).toBe(1500);
      expect(player.c.energy).toBe(90);
      expect(player.c.diamonds).toBe(75);
      expect(session.savePlayerCalled).toBe(true);

      const last = session.getLastMessage();
      expect(last.type).toBe('debugGrantComplete');
      expect(last.success).toBe(true);
    });

    it('sets energy to 100 in set mode', async () => {
      const { handleDebugGrant } = await importDebugHandlers();
      const player = createTestPlayer();
      player.c.energy = 20;
      const session = new MockPlayerSession(player);

      await handleDebugGrant({ energy: 100, mode: 'set' }, session as unknown as PlayerSession);

      expect(player.c.energy).toBe(100);
    });

    it('clamps added energy to 100', async () => {
      const { handleDebugGrant } = await importDebugHandlers();
      const player = createTestPlayer();
      player.c.energy = 95;
      const session = new MockPlayerSession(player);

      await handleDebugGrant({ energy: 50 }, session as unknown as PlayerSession);

      expect(player.c.energy).toBe(100);
    });

    it('rejects when debug is disabled in production', async () => {
      delete process.env.DEBUG;
      process.env.NODE_ENV = 'production';
      vi.resetModules();
      const { handleDebugGrant } = await importDebugHandlers();
      const session = new MockPlayerSession(createTestPlayer());

      await handleDebugGrant({ money: 100 }, session as unknown as PlayerSession);

      expect(session.getLastMessage().type).toBe('error');
      expect(session.savePlayerCalled).toBe(false);
    });

    it('rejects empty grant payload', async () => {
      const { handleDebugGrant } = await importDebugHandlers();
      const session = new MockPlayerSession(createTestPlayer());

      await handleDebugGrant({}, session as unknown as PlayerSession);

      expect(session.getLastMessage().type).toBe('error');
    });

    it('rejects amounts above per-request limits', async () => {
      const { handleDebugGrant } = await importDebugHandlers();
      const session = new MockPlayerSession(createTestPlayer());

      await handleDebugGrant({ money: 2_000_000 }, session as unknown as PlayerSession);

      expect(session.getLastMessage().type).toBe('error');
    });
  });

  describe('handleDebugSetup', () => {
    beforeEach(() => {
      process.env.DEBUG = 'true';
      process.env.NODE_ENV = 'production';
      vi.resetModules();
    });

    it('applies wealthy preset and saves', async () => {
      const { handleDebugSetup } = await importDebugHandlers();
      const session = new MockPlayerSession(createTestPlayer());

      await handleDebugSetup({ preset: 'wealthy' }, session as unknown as PlayerSession);

      expect(session.player.c.money).toBe(100_000);
      expect(session.player.c.diamonds).toBe(500);
      expect(session.savePlayerCalled).toBe(true);
      expect(session.getLastMessage().type).toBe('debugSetupComplete');
    });
  });
});
