/**
 * Activity Handlers
 * Handles extracurricular activities, job applications, and habit management
 */

import type { PlayerSession } from '../game/PlayerSession.js';
import { applyForExtracurricular, quitExtracurricular, updateFocus, getExtracurriculars } from '../services/education/index.js';
import { applyForJob, quitJob } from '../services/jobs/index.js';
import { quitHabit, stopQuitHabit } from '../services/health/index.js';
import { resolvePayloadId } from './payloadHelpers.js';

interface ExtracurricularPayload {
  activityId: string;
}

interface FocusUpdatePayload {
  activityId: string;
  newFocus: number;
}

interface JobPayload {
  jobId: string;
}

interface HabitPayload {
  habitId?: string;
  habit?: string;
}

function resolveHabitId(payload: unknown): string | undefined {
  return resolvePayloadId(payload, 'habitId', 'habit');
}

function resolveActivityId(payload: unknown): string | undefined {
  return resolvePayloadId(payload, 'activityId');
}

function resolveJobId(payload: unknown): string | undefined {
  return resolvePayloadId(payload, 'jobId');
}

export async function handleApplyExtracurricular(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  // iOS sends the ID directly as a string, TypeScript format uses { activityId }
  const activityId = resolveActivityId(payload);
  const player = session.player;

  if (!activityId) {
    session.send({
      type: 'error',
      message: 'Failed to apply for extracurricular activity',
    });
    return;
  }

  // Track that extracurricular question was asked
  player.askedQuestions.add('extracurricular');

  const success = applyForExtracurricular(player, activityId);

  if (success) {
    session.sendPlayerObject();
    session.send({
      type: 'extracurricularApplied',
      success: true,
      message: 'Successfully enrolled in extracurricular activity',
    });
  } else {
    session.send({
      type: 'error',
      message: 'Failed to apply for extracurricular activity',
    });
  }
}

export async function handleQuitExtracurricular(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  // iOS sends the ID directly as a string, TypeScript format uses { activityId }
  const activityId = resolveActivityId(payload);
  const player = session.player;

  if (!activityId) {
    session.send({
      type: 'error',
      message: 'Failed to quit extracurricular activity',
    });
    return;
  }

  const success = quitExtracurricular(player, activityId);

  if (success) {
    session.sendPlayerObject();
    session.send({
      type: 'extracurricularQuit',
      success: true,
      message: 'Successfully quit extracurricular activity',
    });
  } else {
    session.send({
      type: 'error',
      message: 'Failed to quit extracurricular activity',
    });
  }
}

export async function handleApplyJob(payload: unknown, session: PlayerSession): Promise<void> {
  const jobId = resolveJobId(payload);
  const player = session.player;

  if (!jobId) {
    session.send({
      type: 'error',
      message: 'Job not found',
    });
    return;
  }

  const result = applyForJob(player, jobId);

  if (result.success) {
    // Persist before notifying the client. applyForJob mutates career state
    // (c.job, c.occupation, c.activities, c.activityRecords) in memory only;
    // without an explicit save the new job is lost if the app is killed before
    // the next game-loop auto-save (which only runs while the game is ticking).
    // Mirrors handlePurchaseItem in purchases.ts.
    await session.savePlayer();
    session.sendPlayerObject();
    session.send({
      type: 'jobApplied',
      success: true,
      message: result.message,
      job: result.job,
    });
  } else {
    session.send({
      type: 'error',
      message: result.message,
    });
  }
}

export async function handleQuitJob(payload: unknown, session: PlayerSession): Promise<void> {
  const jobId = resolveJobId(payload);
  const player = session.player;

  if (!jobId) {
    session.send({
      type: 'error',
      message: 'Job not found',
    });
    return;
  }

  const result = quitJob(player, jobId);

  if (result.success) {
    // Persist before notifying the client. quitJob clears career state
    // (c.job, c.occupation, c.activities) in memory only; without an explicit
    // save the change is lost if the app is killed before the next auto-save.
    await session.savePlayer();
    session.sendPlayerObject();
    session.send({
      type: 'jobQuit',
      success: true,
      message: result.message,
    });
  } else {
    session.send({
      type: 'error',
      message: result.message,
    });
  }
}

/**
 * Handle quit habit command
 * Sets a habit's status to 'quitting' and begins the 30-day quit process.
 * While quitting, the habit costs +5 energy per day.
 *
 * Matches Python: handle_quit_habit() in command_dispatcher.py
 */
export async function handleQuitHabit(payload: unknown, session: PlayerSession): Promise<void> {
  const habitId = resolveHabitId(payload);
  const player = session.player;

  if (!habitId) {
    session.send({
      type: 'error',
      message: 'Habit not found',
    });
    return;
  }

  const result = quitHabit(player, habitId);

  if (result.success) {
    session.sendPlayerObject();
    session.send({
      type: 'habitQuitting',
      success: true,
      message: result.message,
      habitId,
    });
  } else {
    session.send({
      type: 'error',
      message: result.message,
    });
  }
}

/**
 * Handle stop quit habit command
 * Reverts a habit's status from 'quitting' back to 'active' and resets progress.
 * This cancels the quit attempt and removes the +5 energy cost.
 *
 * Matches Python: handle_stop_quit_habit() in command_dispatcher.py
 */
export async function handleStopQuitHabit(payload: unknown, session: PlayerSession): Promise<void> {
  const habitId = resolveHabitId(payload);
  const player = session.player;

  if (!habitId) {
    session.send({
      type: 'error',
      message: 'Habit not found or not currently quitting',
    });
    return;
  }

  const result = stopQuitHabit(player, habitId);

  if (result.success) {
    session.sendPlayerObject();
    session.send({
      type: 'habitQuitStopped',
      success: true,
      message: result.message,
      habitId,
    });
  } else {
    session.send({
      type: 'error',
      message: result.message,
    });
  }
}

export async function handleGetExtracurriculars(
  _payload: unknown,
  session: PlayerSession
): Promise<void> {
  const player = session.player;

  // Get extracurriculars from education service
  const extracurriculars = getExtracurriculars();

  session.send({
    type: 'extraCurriculars',
    extraCurriculars: extracurriculars,
  });
}

export async function handleFocusUpdate(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  const activityId = resolvePayloadId(payload, 'activityId');
  const newFocus = payload && typeof payload === 'object'
    ? (payload as FocusUpdatePayload).newFocus
    : undefined;
  const player = session.player;

  if (!activityId || newFocus === undefined || newFocus === null) {
    session.send({
      type: 'error',
      message: 'Activity not found or invalid focus',
    });
    return;
  }

  // Use the education service to update focus (matches Python: update_focus)
  const success = updateFocus(player, activityId, String(newFocus));

  if (success) {
    session.sendPlayerObject();
    session.send({
      type: 'focusUpdated',
      success: true,
      activityId,
      newFocus,
    });
  } else {
    session.send({
      type: 'error',
      message: 'Activity not found or invalid focus',
    });
  }
}

// ============================================================================
// Lifestyle & Retirement (T6 economy)
// ============================================================================

/**
 * Set the player's spending habit (frugal / normal / extravagant). This lever
 * was defined and read by computeWeeklyFinances but had NO setter, so it was
 * permanently locked at 'normal' — players could never trade lifestyle for
 * savings. frugal banks the most (and lowers expenses); extravagant the least.
 */
export async function handleSetSpendingHabits(payload: unknown, session: PlayerSession): Promise<void> {
  const raw = (payload && typeof payload === 'object'
    ? ((payload as { habit?: unknown; value?: unknown }).habit ?? (payload as { value?: unknown }).value)
    : undefined);
  const habit = typeof raw === 'string' ? raw.toLowerCase() : '';

  if (habit !== 'frugal' && habit !== 'normal' && habit !== 'extravagant') {
    session.send({ type: 'error', message: 'Invalid spending habit. Use frugal, normal, or extravagant.' });
    return;
  }

  session.player.c.spendingHabits = habit;
  await session.savePlayer();
  session.sendPlayerObject();
  session.send({ type: 'spendingHabitsUpdated', success: true, habit });
}

/** Minimum age at which a character may choose to retire. */
const MIN_RETIREMENT_AGE = 50;

/**
 * Retire: a mechanical retirement (previously only a narrative arc existed; the
 * character worked the same job until death). Stops wages (occupation 'retired'
 * is non-earning), clears the job, and grants a happiness lift. Retirement-era
 * cashflow then becomes the drawdown of accumulated savings against the rent floor.
 */
export async function handleRetire(_payload: unknown, session: PlayerSession): Promise<void> {
  const c = session.player.c;

  if (c.occupation === 'retired') {
    session.send({ type: 'error', message: 'You are already retired.' });
    return;
  }
  if ((c.ageYears ?? 0) < MIN_RETIREMENT_AGE) {
    session.send({ type: 'error', message: `You can retire from age ${MIN_RETIREMENT_AGE}.` });
    return;
  }

  c.occupation = 'retired';
  c.salary = 0;
  c.job = null;
  c.happiness = Math.min(100, (c.happiness ?? 50) + 10);
  session.player.messageQueue = session.player.messageQueue ?? [];
  session.player.messageQueue.push('You have retired! Time to enjoy the life you built.');

  await session.savePlayer();
  session.sendPlayerObject();
  session.send({ type: 'retired', success: true });
}
