import type { PlayerSession } from '../game/PlayerSession.js';
import { config } from '../config.js';
import { Person } from '../models/Person.js';
import { endLiveActivityForPlayer } from '../services/notifications/liveActivityManager.js';

// Game control handlers - WebSocketServer routes commands directly to these
// iOS sends {type: "command", message: "start"} which gets routed to handleStart

export async function handleStart(
  _payload: unknown,
  session: PlayerSession
): Promise<void> {
  session.player.controller = 'active';
  session.player.status = 'playing';
  session.start();
  session.sendPlayerObject();
}

export async function handleStop(
  _payload: unknown,
  session: PlayerSession
): Promise<void> {
  session.player.controller = 'inactive';
  session.stop();
  await endLiveActivityForPlayer(session.player);
}

export async function handleRestart(
  _payload: unknown,
  session: PlayerSession
): Promise<void> {
  // Python creates a completely new playerClass() - we need to reset everything
  // to match a fresh player state (status: 'creating', waiting for character setup)
  const player = session.player;

  // Stop the game loop
  session.stop();
  await endLiveActivityForPlayer(player);

  // Reset to 'creating' status like Python's new playerClass()
  player.status = 'creating';
  player.controller = 'inactive';

  // Reset time
  player.hourOfDay = 8;
  player.minuteOfHour = 0;
  player.dayOfYear = 1;
  player.dayOfWeek = 1;
  player.monthOfYear = 1;
  player.season = 'spring';
  player.ticks = 0;
  player.gameSpeed = config.SPEED_DEFAULT;
  player.previousGameSpeed = config.SPEED_DEFAULT;

  // Reset character to fresh state (empty name triggers character setup)
  player.character = new Person({
    id: `char-${player.userId}`,
    firstname: '',
    lastname: '',
    sex: 'Male',
    ageYears: 0,
    ageDays: 0,
    status: 'alive',
  });

  // Clear all game state
  player.r = [];  // Clear relationships
  player.conversations = [];  // Clear conversations
  player.events.clear();
  player.askedQuestions.clear();
  player.messageQueue = [];
  player.messageLog = [];
  player.activeDilemmas = [];

  // Reset player stats
  player.c.energy = 100;
  player.c.money = 0;
  player.c.diamonds = 0;

  console.log('Game restarted for user:', player.userId);

  // Send fresh player object (will show character creation screen)
  session.sendPlayerObject();
}

/** New-life mode: a brand new bloodline vs. continuing the lineage as the heir. */
type NewLifeMode = 'fresh' | 'heir';

interface StartNewLifePayload {
  mode?: NewLifeMode;
}

/**
 * Reset the per-life game state shared by both fresh-start and continue-as-heir.
 * Wipes time, relationships, conversations, events, messages, and dilemmas, and
 * installs an empty character (empty name triggers character creation). Does NOT
 * touch the generational layer (familyPrestige / familyTree / pendingInheritance);
 * callers decide whether to preserve or clear those.
 */
function resetPerLifeState(player: PlayerSession['player']): void {
  // Reset to 'creating' status (empty character name triggers setup).
  player.status = 'creating';
  player.controller = 'inactive';

  // Reset time.
  player.hourOfDay = 8;
  player.minuteOfHour = 0;
  player.dayOfYear = 1;
  player.dayOfWeek = 1;
  player.monthOfYear = 1;
  player.season = 'spring';
  player.ticks = 0;
  player.gameSpeed = config.SPEED_DEFAULT;
  player.previousGameSpeed = config.SPEED_DEFAULT;

  // Fresh character.
  player.character = new Person({
    id: `char-${player.userId}`,
    firstname: '',
    lastname: '',
    sex: 'Male',
    ageYears: 0,
    ageDays: 0,
    status: 'alive',
  });

  // Clear all game state.
  player.r = [];
  player.conversations = [];
  player.events.clear();
  player.askedQuestions.clear();
  player.messageQueue = [];
  player.messageLog = [];
  player.activeDilemmas = [];

  // Reset player stats.
  player.c.energy = 100;
  player.c.money = 0;
  player.c.diamonds = 0;

  // Clear the end-of-life summary so the death screen is dismissed and the
  // next death builds a fresh summary.
  player.lifeSummary = null;
}

/**
 * Start a new life after death (the "New Life" replay hook). Two modes:
 *
 *  - 'fresh' (default): a clean fresh-character reset back to character
 *    creation. Wipes the per-life state AND the generational layer
 *    (familyPrestige, familyTree, pendingInheritance) — a brand new bloodline.
 *
 *  - 'heir': continue the lineage as the eldest living child. Preserves
 *    familyPrestige + familyTree across the wipe, applies the stashed
 *    inheritance plus a prestige-based money seed to the new character, and
 *    seeds the heir's name/sex so the player picks up where their child left
 *    off. Only valid when the just-ended life left a heir (a living child);
 *    otherwise it silently falls back to a fresh start.
 *
 * Reachable from the death screen via the 'startNewLife' command. Mode is read
 * from the optional message payload ({ mode: 'heir' }); type-only envelopes
 * default to 'fresh'.
 */
export async function handleStartNewLife(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  const player = session.player;

  // Determine the requested mode and capture the heir BEFORE the wipe (the
  // lifeSummary is cleared inside resetPerLifeState).
  const requested =
    payload && typeof payload === 'object'
      ? (payload as StartNewLifePayload).mode
      : undefined;
  const heir = player.lifeSummary?.legacy?.heir ?? null;
  const pendingInheritance = player.pendingInheritance ?? 0;
  // Heir mode only applies when a heir actually exists; otherwise fall back.
  const mode: NewLifeMode = requested === 'heir' && heir ? 'heir' : 'fresh';

  // Stop the game loop and any live activity.
  session.stop();
  await endLiveActivityForPlayer(player);

  resetPerLifeState(player);

  if (mode === 'heir' && heir) {
    // Continue the lineage: preserve familyPrestige + familyTree (untouched by
    // the wipe), and seed the heir's new life with inheritance + prestige money.
    const prestigeSeed = (player.familyPrestige ?? 0) * 100;
    player.c.money = pendingInheritance + prestigeSeed;
    player.c.prestige = player.familyPrestige ?? 0;

    // Seed identity from the heir so the player resumes as their child.
    player.c.firstname = heir.name.split(' ')[0] ?? '';
    player.c.lastname = heir.name.split(' ').slice(1).join(' ');
    player.c.sex = heir.sex;
    player.c.ageYears = heir.ageYears;
    player.c.ageDays = heir.ageYears * 365;

    // Inheritance consumed.
    player.pendingInheritance = 0;

    console.log(
      `New life (heir) started for user ${player.userId}: ` +
        `inherited ${pendingInheritance} + prestige seed ${prestigeSeed}, ` +
        `familyPrestige ${player.familyPrestige}`
    );
  } else {
    // Fresh start: a brand new bloodline. Clear the generational layer.
    player.familyPrestige = 0;
    player.familyTree = [];
    player.pendingInheritance = 0;

    console.log('New life (fresh) started for user:', player.userId);
  }

  // Send fresh player object (will show character creation screen).
  session.sendPlayerObject();
}

interface SpeedPayload {
  speed?: number | '+' | '-' | 'reset';
  message?: number | '+' | '-' | 'reset';  // iOS sends as 'message'
}

export async function handleSpeed(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  // Handle different payload formats:
  // - String: "+" or "-" (from iOS via message.message)
  // - Object: { speed: "+" } or { message: "+" }
  let speed: number | '+' | '-' | 'reset' | undefined;

  if (typeof payload === 'string') {
    speed = payload as '+' | '-' | 'reset';
  } else if (typeof payload === 'number') {
    speed = payload;
  } else {
    const data = payload as SpeedPayload;
    speed = data.speed ?? data.message;
  }

  if (speed === undefined) return;

  const speedValues = config.SPEED_BUTTON_VALUES;

  // Detect an active event-driven pause. While paused, gameSpeed holds a
  // sentinel (SPEED_QUESTION_PAUSE / SPEED_PAUSED) and the REAL underlying
  // speed lives in previousGameSpeed — which the event-resume path
  // (handlers/events.ts) force-writes back into gameSpeed once the player
  // answers. A speed change made during the pause must therefore land in
  // previousGameSpeed, or the resume will clobber it and snap the game back
  // to its pre-pause speed (e.g. instant). See task #14.
  const isPaused =
    session.player.gameSpeed === config.SPEED_QUESTION_PAUSE ||
    session.player.gameSpeed === config.SPEED_PAUSED;

  // The effective speed to step relative to: while paused, step from the real
  // underlying speed, not the pause sentinel (indexOf(sentinel) === -1, which
  // would otherwise collapse +/- to the slowest value and unpause unexpectedly).
  const effectiveSpeed = isPaused ? session.player.previousGameSpeed : session.player.gameSpeed;
  const currentIndex = speedValues.indexOf(effectiveSpeed as typeof speedValues[number]);

  // Compute the new target speed from the +/-/number/reset request.
  let target: number | undefined;
  if (speed === '+') {
    // Move RIGHT in array = smaller tick value = FASTER
    const baseIndex = currentIndex === -1 ? 0 : currentIndex;
    const newIndex = Math.min(speedValues.length - 1, baseIndex + 1);
    target = speedValues[newIndex];
  } else if (speed === '-') {
    // Move LEFT in array = larger tick value = SLOWER
    const baseIndex = currentIndex === -1 ? speedValues.length - 1 : currentIndex;
    const newIndex = Math.max(0, baseIndex - 1);
    target = speedValues[newIndex];
  } else if (speed === 'reset') {
    // Reset to previous speed, unless it was paused
    target = session.player.previousGameSpeed === config.SPEED_PAUSED
      ? config.SPEED_DEFAULT
      : session.player.previousGameSpeed;
  } else if (typeof speed === 'number') {
    target = speed;
  }

  if (target === undefined) return;

  if (isPaused) {
    // Keep the game paused (the question modal owns un-pausing on answer), but
    // record the user's chosen speed as the value to restore on resume.
    session.player.previousGameSpeed = target;
  } else {
    // Normal case: remember the speed we're leaving, then apply the new one.
    session.player.previousGameSpeed = session.player.gameSpeed;
    session.player.gameSpeed = target;
  }

  // Send lightweight update matching Python format: {'type': 'u', 'gameSpeed': value}
  // While paused we report the speed the player just selected so the client UI
  // tracks their choice even though the loop stays paused until they answer.
  session.send({
    type: 'u',
    gameSpeed: isPaused ? target : session.player.gameSpeed,
  });
}

export async function handleResetSpeed(
  _payload: unknown,
  session: PlayerSession
): Promise<void> {
  // Reset to previous speed
  session.player.gameSpeed = session.player.previousGameSpeed;

  // Send lightweight update matching Python format: {'type': 'u', 'gameSpeed': value}
  session.send({
    type: 'u',
    gameSpeed: session.player.gameSpeed,
  });
}
