/**
 * Diamond Economy System
 *
 * Handles diamond earning, spending, and tracking.
 * Diamonds are the premium currency in BaoLife.
 *
 * All operations read/write player.c.diamonds directly.
 * When only a playerId string is provided, the player is resolved
 * via the connectionRegistry (active session lookup).
 */

import { connectionRegistry } from '../server/ConnectionRegistry.js';

export interface DiamondTransaction {
  playerId: string;
  transactionType: 'earn' | 'spend';
  amount: number;
  reason: string;
  timestamp: Date;
}

export interface DeductResult {
  success: boolean;
  message: string;
  newBalance?: number;
}

const transactionHistory: Map<string, DiamondTransaction[]> = new Map();

interface DiamondPlayerState {
  userId?: string;
  c?: {
    diamonds?: number;
  };
  character?: {
    diamonds?: number;
  };
}

// Diamond rewards for various achievements
export const DIAMOND_REWARDS: Record<string, number> = {
  level_up: 10,
  complete_school_year: 15,
  graduate_high_school: 25,
  graduate_college: 50,
  get_first_job: 15,
  promotion: 20,
  become_ceo: 100,
  get_married: 25,
  have_first_child: 30,
  reach_age_milestone: 20,
  daily_login: 5,
  weekly_streak: 25,
  monthly_streak: 100,
};

function resolveTarget(
  playerIdOrPlayer: string | DiamondPlayerState,
  playerArg?: DiamondPlayerState
): { playerId: string; player: DiamondPlayerState | null } {
  if (typeof playerIdOrPlayer === 'string') {
    // If a player object was explicitly provided, use it
    if (playerArg) {
      return { playerId: playerIdOrPlayer, player: playerArg };
    }
    // Otherwise resolve via connectionRegistry (active session lookup)
    const session = connectionRegistry.get(playerIdOrPlayer);
    if (session) {
      return { playerId: playerIdOrPlayer, player: session.player };
    }
    return { playerId: playerIdOrPlayer, player: null };
  }

  return {
    playerId: playerIdOrPlayer.userId ?? 'unknown',
    player: playerIdOrPlayer,
  };
}

function getCurrencyContainer(player: DiamondPlayerState): { diamonds?: number } | null {
  return player.c ?? player.character ?? null;
}

function readBalance(player: DiamondPlayerState): number {
  const container = getCurrencyContainer(player);
  if (!container) {
    return 0;
  }
  return Math.max(0, container.diamonds ?? 0);
}

function writeBalance(player: DiamondPlayerState, amount: number): boolean {
  const container = getCurrencyContainer(player);
  if (!container) {
    return false;
  }
  container.diamonds = Math.max(0, amount);
  return true;
}

/**
 * Initialize player diamond balance
 */
export function initializePlayerDiamonds(
  playerIdOrPlayer: string | DiamondPlayerState,
  initialAmount = 0,
  playerArg?: DiamondPlayerState
): void {
  const { playerId, player } = resolveTarget(playerIdOrPlayer, playerArg);

  if (!player) {
    console.warn(`Cannot initialize diamonds for player ${playerId}: player object is required`);
    return;
  }

  if (!writeBalance(player, initialAmount)) {
    console.warn(`Cannot initialize diamonds for player ${playerId}: missing currency container`);
    return;
  }

  if (!transactionHistory.has(playerId)) {
    transactionHistory.set(playerId, []);
  }

  console.log(`Initialized diamonds for player ${playerId}: ${Math.max(0, initialAmount)}`);
}

/**
 * Award diamonds to player with tracking
 */
export function awardDiamonds(
  playerIdOrPlayer: string | DiamondPlayerState,
  reason: string,
  amount: number,
  playerArg?: DiamondPlayerState
): boolean {
  const { playerId, player } = resolveTarget(playerIdOrPlayer, playerArg);

  if (amount <= 0) {
    console.error(`Invalid diamond amount for player ${playerId}: ${amount} (must be positive)`);
    return false;
  }

  if (!player) {
    console.error(`Cannot award diamonds for player ${playerId}: player object is required`);
    return false;
  }

  const currentBalance = readBalance(player);
  const newBalance = currentBalance + amount;
  if (!writeBalance(player, newBalance)) {
    console.error(`Cannot award diamonds for player ${playerId}: missing currency container`);
    return false;
  }

  // Log transaction
  const transaction: DiamondTransaction = {
    playerId,
    transactionType: 'earn',
    amount,
    reason,
    timestamp: new Date(),
  };

  const history = transactionHistory.get(playerId) ?? [];
  history.push(transaction);
  transactionHistory.set(playerId, history);

  console.log(`Player ${playerId} earned ${amount} diamonds: ${reason} (total: ${newBalance})`);

  return true;
}

/**
 * Deduct diamonds from player with validation
 */
export function deductDiamonds(
  playerIdOrPlayer: string | DiamondPlayerState,
  reason: string,
  amount: number,
  playerArg?: DiamondPlayerState
): DeductResult {
  return spendDiamonds(playerIdOrPlayer, reason, amount, playerArg);
}

/**
 * Spend diamonds from player with validation
 */
export function spendDiamonds(
  playerIdOrPlayer: string | DiamondPlayerState,
  reason: string,
  amount: number,
  playerArg?: DiamondPlayerState
): DeductResult {
  const { playerId, player } = resolveTarget(playerIdOrPlayer, playerArg);

  if (amount <= 0) {
    console.error(`Invalid diamond amount for player ${playerId}: ${amount} (must be positive)`);
    return { success: false, message: 'Invalid amount' };
  }

  if (!player) {
    console.error(`Cannot spend diamonds for player ${playerId}: player object is required`);
    return { success: false, message: 'Player context missing' };
  }

  const currentBalance = readBalance(player);

  if (currentBalance < amount) {
    return {
      success: false,
      message: `Insufficient diamonds. Need ${amount}, have ${currentBalance}`,
    };
  }

  const newBalance = currentBalance - amount;
  if (!writeBalance(player, newBalance)) {
    console.error(`Cannot spend diamonds for player ${playerId}: missing currency container`);
    return { success: false, message: 'Player currency not available' };
  }

  // Log transaction
  const transaction: DiamondTransaction = {
    playerId,
    transactionType: 'spend',
    amount,
    reason,
    timestamp: new Date(),
  };

  const history = transactionHistory.get(playerId) ?? [];
  history.push(transaction);
  transactionHistory.set(playerId, history);

  console.log(`Player ${playerId} spent ${amount} diamonds: ${reason} (remaining: ${newBalance})`);

  return {
    success: true,
    message: 'Diamonds deducted successfully',
    newBalance,
  };
}

/**
 * Get player's current diamond balance
 */
export function getDiamondBalance(
  playerIdOrPlayer: string | DiamondPlayerState,
  playerArg?: DiamondPlayerState
): number {
  const { player } = resolveTarget(playerIdOrPlayer, playerArg);
  if (!player) {
    return 0;
  }
  return readBalance(player);
}

/**
 * Get player's diamond transaction history
 */
export function getDiamondTransactionHistory(playerId: string, limit = 50): DiamondTransaction[] {
  const history = transactionHistory.get(playerId) ?? [];
  return history.slice(-limit).reverse();
}

/**
 * Get the diamond reward amount for a specific reason
 */
export function getRewardAmount(reason: string): number {
  return DIAMOND_REWARDS[reason] ?? 0;
}

/**
 * Check if player can afford an amount
 */
export function canAfford(
  playerIdOrPlayer: string | DiamondPlayerState,
  amount: number,
  playerArg?: DiamondPlayerState
): boolean {
  const balance = getDiamondBalance(playerIdOrPlayer, playerArg);
  return balance >= amount;
}

/**
 * Set player's diamond balance directly (for admin/testing)
 */
export function setDiamondBalance(
  playerIdOrPlayer: string | DiamondPlayerState,
  amount: number,
  playerArg?: DiamondPlayerState
): void {
  const { playerId, player } = resolveTarget(playerIdOrPlayer, playerArg);
  if (!player) {
    console.warn(`Cannot set diamond balance for player ${playerId}: player object is required`);
    return;
  }
  if (!writeBalance(player, amount)) {
    console.warn(`Cannot set diamond balance for player ${playerId}: missing currency container`);
  }
}

/**
 * Clear player's diamond data (for testing)
 */
export function clearPlayerDiamonds(
  playerIdOrPlayer: string | DiamondPlayerState,
  playerArg?: DiamondPlayerState
): void {
  const { playerId, player } = resolveTarget(playerIdOrPlayer, playerArg);
  if (player) {
    writeBalance(player, 0);
  }
  transactionHistory.delete(playerId);
}

/**
 * Clear all diamond data (for testing)
 */
export function clearAllDiamonds(): void {
  transactionHistory.clear();
}
