/**
 * Power-up rules (ARCHITECTURE §2.6), as one validator used both to accept USE_POWERUP commands
 * and to tell a player's phone which power-ups are usable right now.
 */
import { isValidPosition } from './board';
import { canPlayRound, currentQuestion, isBetweenQuestions, nextQuestionToOpen } from './round';
import { POINTS, ROUND_RULES, type ErrorCode, type GameState, type Id, type Position, type PowerUpType, type PowerUpWallet } from './types';

export const POWER_UP_TYPES: readonly PowerUpType[] = ['DOUBLE_DOWN', 'SECOND_CHANCE', 'CATEGORY_SWAP'];

/** tokens + used.length never exceeds this: one use of each type per event. */
export const POWER_UP_CAP = 3;

export function emptyWallet(): PowerUpWallet {
  return { tokens: 0, grants: [], used: [], keys: [] };
}

export interface PowerUpArgs {
  questionId?: Id | undefined;
  from?: Position | undefined;
  to?: Position | undefined;
}

export type PowerUpCheck =
  | { ok: true; /** DOUBLE_DOWN target / SECOND_CHANCE question; null for CATEGORY_SWAP. */ questionId: Id | null }
  | { ok: false; code: ErrorCode; message: string };

const no = (code: ErrorCode, message: string): PowerUpCheck => ({ ok: false, code, message });

/**
 * Can `playerId` use `type` right now? With `args === null` this answers "could they, in
 * principle" (for the UI); with args it validates a concrete command (target question, tiles).
 * Order: session → feature flag → round → player → already used → token → type rules.
 */
export function checkPowerUp(state: GameState, playerId: Id, type: PowerUpType, args: PowerUpArgs | null): PowerUpCheck {
  if (state.status === 'COMPLETED') return no('SESSION_COMPLETED', 'This game has finished');
  if (state.status !== 'RUNNING') return no('SESSION_NOT_RUNNING', 'The game is not running');
  if (!state.settings.powerUpsEnabled) return no('POWERUPS_DISABLED', 'Power-ups are off for this game');
  const round = state.round;
  if (!round) return no('NO_ROUND', 'No round is running');
  const player = state.players[playerId];
  if (!player) return no('UNKNOWN_PLAYER', 'Unknown player');
  if (player.status === 'KICKED') return no('PLAYER_KICKED', 'You were removed from this game');
  if (player.status !== 'ACTIVE' || !canPlayRound(state, round, playerId)) return no('NOT_PARTICIPANT', 'You are not playing this round');
  const wallet = state.wallets[playerId] ?? emptyWallet();
  if (wallet.used.includes(type)) return no('POWERUP_ALREADY_USED', 'You already used this power-up tonight');
  if (wallet.tokens < 1) return no('NO_TOKEN', 'Complete a line to earn a power-up token');

  switch (type) {
    case 'DOUBLE_DOWN': {
      if (!isBetweenQuestions(round)) return no('WRONG_PHASE', 'Double Down is placed before a question opens');
      const target = nextQuestionToOpen(round);
      if (!target) return no('WRONG_PHASE', 'No question left to double down on');
      if (args?.questionId !== undefined && args.questionId !== target.id) return no('WRONG_QUESTION', 'Double Down applies to the next question');
      if (round.doubleDowns[target.id]?.includes(playerId)) return no('POWERUP_ALREADY_USED', 'Already doubled down on this question');
      if ((state.totals[playerId]?.points ?? 0) < POINTS.DOUBLE_DOWN_MIN_BALANCE) {
        return no('INSUFFICIENT_BALANCE', `You need at least ${POINTS.DOUBLE_DOWN_MIN_BALANCE} points to double down`);
      }
      return { ok: true, questionId: target.id };
    }
    case 'SECOND_CHANCE': {
      const q = currentQuestion(round);
      if (round.phase !== 'OPEN' || !q) return no('WRONG_PHASE', 'Second Chance works while a question is open');
      if (args?.questionId !== undefined && args.questionId !== q.id) return no('WRONG_QUESTION', 'That question is not open');
      if (round.answers[q.id]?.[playerId]) return no('ALREADY_ANSWERED', 'You already answered');
      if (q.type !== 'MULTIPLE_CHOICE' || q.options.length < 3) return no('POWERUP_NOT_APPLICABLE', 'Second Chance needs a multiple-choice question with 3+ options');
      return { ok: true, questionId: q.id };
    }
    case 'CATEGORY_SWAP': {
      if (!ROUND_RULES[round.kind].hasBoard) return no('POWERUP_NOT_APPLICABLE', 'Category Swap needs a board round');
      if (!isBetweenQuestions(round)) return no('WRONG_PHASE', 'Category Swap works between questions');
      const board = round.boards[playerId];
      if (!board) return no('POWERUP_NOT_APPLICABLE', 'You have no board yet');
      if (args === null) {
        const open = board.tiles.filter((t) => !t.activated).length;
        return open >= 2 ? { ok: true, questionId: null } : no('INVALID_TILE', 'You need two unactivated tiles to swap');
      }
      const { from, to } = args;
      if (!isValidPosition(from) || !isValidPosition(to) || from === to) return no('INVALID_TILE', 'Pick two different tiles');
      if (board.tiles[from]?.activated !== false || board.tiles[to]?.activated !== false) {
        return no('INVALID_TILE', 'Only unactivated tiles can be swapped');
      }
      return { ok: true, questionId: null };
    }
  }
}
