import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'
import {
  type Companion,
  type CompanionBones,
  type CompanionProgress,
  type Equipment,
  EQUIPMENT_REWARDS,
  EYES,
  MAX_LEVEL,
  RARITIES,
  RARITY_WEIGHTS,
  type Rarity,
  SPECIES,
  STAT_NAMES,
  type StatName,
  TOKENS_PER_XP,
  xpForLevel,
} from './types.js'

// Mulberry32 — tiny seeded PRNG
function mulberry32(seed: number): () => number {
  let a = seed >>> 0
  return function () {
    a |= 0
    a = (a + 0x6d2b79f5) | 0
    let t = Math.imul(a ^ (a >>> 15), 1 | a)
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}

function hashString(s: string): number {
  if (typeof Bun !== 'undefined') {
    return Number(BigInt(Bun.hash(s)) & 0xffffffffn)
  }
  let h = 2166136261
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i)
    h = Math.imul(h, 16777619)
  }
  return h >>> 0
}

function pick<T>(rng: () => number, arr: readonly T[]): T {
  return arr[Math.floor(rng() * arr.length)]!
}

function rollRarity(rng: () => number): Rarity {
  const total = Object.values(RARITY_WEIGHTS).reduce((a, b) => a + b, 0)
  let roll = rng() * total
  for (const rarity of RARITIES) {
    roll -= RARITY_WEIGHTS[rarity]
    if (roll < 0) return rarity
  }
  return 'common'
}

const RARITY_FLOOR: Record<Rarity, number> = {
  common: 5,
  uncommon: 15,
  rare: 25,
  epic: 35,
  legendary: 50,
}

function rollStats(
  rng: () => number,
  rarity: Rarity,
): Record<StatName, number> {
  const floor = RARITY_FLOOR[rarity]
  const peak = pick(rng, STAT_NAMES)
  let dump = pick(rng, STAT_NAMES)
  while (dump === peak) dump = pick(rng, STAT_NAMES)

  const stats = {} as Record<StatName, number>
  for (const name of STAT_NAMES) {
    if (name === peak) {
      stats[name] = Math.min(100, floor + 50 + Math.floor(rng() * 30))
    } else if (name === dump) {
      stats[name] = Math.max(1, floor - 10 + Math.floor(rng() * 15))
    } else {
      stats[name] = floor + Math.floor(rng() * 40)
    }
  }
  return stats
}

const SALT = 'forge-2026-777'

function rollBonesFrom(rng: () => number): CompanionBones {
  const rarity = rollRarity(rng)
  return {
    rarity,
    species: pick(rng, SPECIES),
    eye: pick(rng, EYES),
    shiny: rng() < 0.01,
    stats: rollStats(rng, rarity),
  }
}

let bonesCache: { key: string; value: CompanionBones } | undefined
function rollBones(userId: string): CompanionBones {
  const key = userId + SALT
  if (bonesCache?.key === key) return bonesCache.value
  const value = rollBonesFrom(mulberry32(hashString(key)))
  bonesCache = { key, value }
  return value
}

export function companionUserId(): string {
  const config = getGlobalConfig()
  return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon'
}

export function getCompanion(): Companion | undefined {
  const stored = getGlobalConfig().companion
  if (!stored) return undefined
  const bones = rollBones(companionUserId())
  const progress: CompanionProgress = {
    level: stored.level ?? 1,
    xp: stored.xp ?? 0,
    totalTokensEarned: stored.totalTokensEarned ?? 0,
    equipment: stored.equipment ?? {},
    petCount: stored.petCount ?? 0,
    lastPetAt: stored.lastPetAt,
  }
  return { ...stored, ...bones, ...progress }
}

// --- XP and Leveling ---

// Level-up quips based on equipment rewards or milestone levels
function levelUpQuip(level: number, equipment: Equipment): string {
  const reward = EQUIPMENT_REWARDS[level]
  if (reward) {
    const names = Object.values(reward).filter(Boolean)
    if (names.length > 0) {
      const equipName = names[0]!.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
      return `Level ${level}! Got: ${equipName}!`
    }
  }
  const milestones: Record<number, string> = {
    5: 'The forge grows stronger!',
    10: 'A decade of sparks!',
    15: 'Steel bends to our will!',
    20: 'Master of the anvil!',
    25: 'The coals burn eternal!',
    30: 'Legendary heat!',
    35: 'Forged in dragonfire!',
    40: 'MAX LEVEL! The forge is complete!',
  }
  return milestones[level] ?? `Level ${level}! The forge burns brighter!`
}

/** Award XP based on tokens processed. Returns the companion after update, or undefined if no companion. */
export function awardTokenXP(inputTokens: number, outputTokens: number): { companion: Companion; leveledUp: boolean; newLevel: number; quip: string } | undefined {
  const stored = getGlobalConfig().companion
  if (!stored) return undefined

  const totalNewTokens = inputTokens + outputTokens
  const xpGain = Math.floor(totalNewTokens / TOKENS_PER_XP)
  if (xpGain <= 0) return undefined

  const currentLevel = stored.level ?? 1
  const currentXP = (stored.xp ?? 0) + xpGain
  const currentTokens = (stored.totalTokensEarned ?? 0) + totalNewTokens

  let newLevel = currentLevel
  let remainingXP = currentXP
  while (newLevel < MAX_LEVEL && remainingXP >= xpForLevel(newLevel + 1)) {
    remainingXP -= xpForLevel(newLevel + 1)
    newLevel++
  }

  // Accumulate equipment rewards for all levels gained
  let equipment = { ...(stored.equipment ?? {}) } as Equipment
  for (let lvl = currentLevel + 1; lvl <= newLevel; lvl++) {
    const reward = EQUIPMENT_REWARDS[lvl]
    if (reward) {
      equipment = { ...equipment, ...reward }
    }
  }

  saveGlobalConfig(current => ({
    ...current,
    companion: {
      ...current.companion!,
      level: newLevel,
      xp: remainingXP,
      totalTokensEarned: currentTokens,
      equipment,
    },
  }))

  const quip = newLevel > currentLevel ? levelUpQuip(newLevel, equipment) : ''

  return {
    companion: getCompanion()!,
    leveledUp: newLevel > currentLevel,
    newLevel,
    quip,
  }
}

/** Record a pet interaction. Returns updated companion or undefined. */
export function petCompanion(): Companion | undefined {
  const stored = getGlobalConfig().companion
  if (!stored) return undefined

  // Petting cooldown: 10 seconds
  const now = Date.now()
  if (stored.lastPetAt && now - stored.lastPetAt < 10_000) {
    return getCompanion()
  }

  // Small XP bonus for petting (5 XP per pet)
  const petXP = 5
  const currentLevel = stored.level ?? 1
  const currentXP = (stored.xp ?? 0) + petXP

  let newLevel = currentLevel
  let remainingXP = currentXP
  while (newLevel < MAX_LEVEL && remainingXP >= xpForLevel(newLevel + 1)) {
    remainingXP -= xpForLevel(newLevel + 1)
    newLevel++
  }

  let equipment = { ...(stored.equipment ?? {}) } as Equipment
  for (let lvl = (stored.level ?? 1) + 1; lvl <= newLevel; lvl++) {
    const reward = EQUIPMENT_REWARDS[lvl]
    if (reward) equipment = { ...equipment, ...reward }
  }

  saveGlobalConfig(current => ({
    ...current,
    companion: {
      ...current.companion!,
      level: newLevel,
      xp: remainingXP,
      petCount: (current.companion!.petCount ?? 0) + 1,
      lastPetAt: now,
      equipment,
    },
  }))

  return getCompanion()
}

/** Get progress to next level as a percentage (0-100) */
export function levelProgress(companion: Companion): number {
  if (companion.level >= MAX_LEVEL) return 100
  const needed = xpForLevel(companion.level + 1)
  if (needed <= 0) return 100
  return Math.min(100, Math.floor((companion.xp / needed) * 100))
}

/** Get equipment display names for current gear */
export function getEquipmentDisplay(equipment: Equipment): string[] {
  const names: Record<string, string> = {
    forge_apron: 'Forge Apron',
    iron_tongs: 'Iron Tongs',
    welding_mask: 'Welding Mask',
    steel_hammer: 'Steel Hammer',
    coal_amulet: 'Coal Amulet',
    mithril_vest: 'Mithril Vest',
    flame_crown: 'Flame Crown',
    arcane_anvil: 'Arcane Anvil',
    ember_heart: 'Ember Heart',
    dragon_helm: "Dragon Helm",
    adamant_plate: 'Adamant Plate',
    legendary_forge: 'Legendary Forge',
  }
  return Object.values(equipment)
    .filter(Boolean)
    .map(id => names[id] ?? id)
}
