import { randomInt, shuffleWithRandom, weightedIndex } from "./random";
import type {
  AllyCombatSlotId,
  CombatPartyAssignment,
  PartyArchetypeId,
  PartyClassId,
  PartyCombatStats,
  PartyDraftCandidate,
  PartyRarity,
  PartyRole,
  PartyRosterMember,
  PartyTraitId,
  RandomState,
} from "./types";
import { MAX_ACTIVE_ROSTER, PARTY_OFFERS_PER_WAVE, PARTY_RECRUITS_PER_WAVE } from "./types";

export interface PartyRarityDefinition {
  readonly id: PartyRarity;
  readonly rank: number;
  readonly color: string;
  readonly healthMultiplier: number;
  readonly damageMultiplier: number;
  readonly traitCount: number;
}

export interface PartyArchetypeDefinition {
  readonly id: PartyArchetypeId;
  readonly classId: PartyClassId;
  readonly className: string;
  readonly role: PartyRole;
  readonly color: string;
  readonly baseStats: PartyCombatStats;
  readonly traits: readonly PartyTraitId[];
}

export const PARTY_RARITY_ORDER: readonly PartyRarity[] = ["white", "green", "blue", "purple", "gold"];

export const PARTY_RARITIES: Record<PartyRarity, PartyRarityDefinition> = {
  white: { id: "white", rank: 0, color: "#e7e9ec", healthMultiplier: 1, damageMultiplier: 1, traitCount: 0 },
  green: { id: "green", rank: 1, color: "#70d886", healthMultiplier: 1.04, damageMultiplier: 1.035, traitCount: 1 },
  blue: { id: "blue", rank: 2, color: "#65aef2", healthMultiplier: 1.08, damageMultiplier: 1.07, traitCount: 1 },
  purple: { id: "purple", rank: 3, color: "#b47aec", healthMultiplier: 1.12, damageMultiplier: 1.105, traitCount: 2 },
  gold: { id: "gold", rank: 4, color: "#efc858", healthMultiplier: 1.16, damageMultiplier: 1.14, traitCount: 3 },
};

const stats = (
  maxHp: number,
  singleTarget: number,
  areaDamage: number,
  defense: number,
  moveSpeed: number,
): PartyCombatStats => ({ maxHp, singleTarget, areaDamage, defense, moveSpeed });

export const PARTY_ARCHETYPES: Record<PartyArchetypeId, PartyArchetypeDefinition> = {
  "knight-tank": {
    id: "knight-tank", classId: "knight", className: "Knight", role: "Tank", color: "#69a8dd",
    baseStats: stats(150, 0.72, 0.75, 1.2, 0.92),
    traits: ["steadfast", "battle-hardened", "guardian-instinct", "royal-aegis"],
  },
  "ranger-damage": {
    id: "ranger-damage", classId: "ranger", className: "Ranger", role: "Damage", color: "#74c987",
    baseStats: stats(94, 1, 0.88, 0.92, 1.05),
    traits: ["sure-footed", "eagle-eye", "rapid-volley", "perfect-shot"],
  },
  "mage-damage": {
    id: "mage-damage", classId: "mage", className: "Mage", role: "Damage", color: "#b17ee6",
    baseStats: stats(86, 1.02, 1.18, 0.86, 0.96),
    traits: ["arcane-focus", "spell-echo", "volatile-power", "astral-mastery"],
  },
  "rogue-damage": {
    id: "rogue-damage", classId: "rogue", className: "Rogue", role: "Damage", color: "#d97171",
    baseStats: stats(92, 1.18, 0.82, 0.88, 1.18),
    traits: ["light-footed", "exploit-opening", "blade-dance", "shadow-master"],
  },
  "warrior-tank": {
    id: "warrior-tank", classId: "warrior", className: "Warrior · Vanguard", role: "Tank", color: "#d98b55",
    baseStats: stats(145, 0.8, 1.02, 1.14, 0.96),
    traits: ["iron-blood", "battle-hardened", "war-cry", "unstoppable"],
  },
  "warrior-damage": {
    id: "warrior-damage", classId: "warrior", className: "Warrior · Ravager", role: "Damage", color: "#e07a4f",
    baseStats: stats(112, 1.02, 1.28, 1, 1),
    traits: ["iron-blood", "whirlwind", "war-cry", "unstoppable"],
  },
  "warlock-damage": {
    id: "warlock-damage", classId: "warlock", className: "Warlock", role: "Damage", color: "#8d6ad4",
    baseStats: stats(84, 1.24, 0.8, 0.84, 0.94),
    traits: ["soul-drain", "doom-mark", "dark-pact", "soul-tyrant"],
  },
  "monk-tank": {
    id: "monk-tank", classId: "monk", className: "Monk · Guardian", role: "Tank", color: "#d7b35c",
    baseStats: stats(122, 0.94, 0.86, 1.08, 1.12),
    traits: ["centered", "counterstrike", "flow-state", "perfect-form"],
  },
  "monk-damage": {
    id: "monk-damage", classId: "monk", className: "Monk · Striker", role: "Damage", color: "#e0ba62",
    baseStats: stats(82, 1.28, 0.76, 0.82, 1.22),
    traits: ["centered", "counterstrike", "flow-state", "perfect-form"],
  },
};

export const PARTY_ARCHETYPE_ORDER = Object.keys(PARTY_ARCHETYPES) as PartyArchetypeId[];
export const TANK_ARCHETYPE_IDS = PARTY_ARCHETYPE_ORDER.filter((id) => PARTY_ARCHETYPES[id].role === "Tank");

const PARTY_NAMES = [
  "Ada", "Bram", "Cato", "Dara", "Eris", "Fenn", "Gale", "Hana", "Ivo", "Jora",
  "Kest", "Lio", "Mara", "Nox", "Oona", "Pax", "Quin", "Rhea", "Soren", "Tali",
] as const;

const RARITY_WEIGHTS_BY_WAVE: readonly (readonly number[])[] = [
  [55, 27, 12, 5, 1],
  [45, 30, 16, 7, 2],
  [35, 32, 20, 10, 3],
];

export function statsAtRarity(archetypeId: PartyArchetypeId, rarity: PartyRarity): PartyCombatStats {
  const base = PARTY_ARCHETYPES[archetypeId].baseStats;
  const tier = PARTY_RARITIES[rarity];
  return {
    maxHp: Math.round(base.maxHp * tier.healthMultiplier),
    singleTarget: base.singleTarget * tier.damageMultiplier,
    areaDamage: base.areaDamage * tier.damageMultiplier,
    defense: base.defense * tier.healthMultiplier,
    moveSpeed: base.moveSpeed,
  };
}

export function traitsAtRarity(archetypeId: PartyArchetypeId, rarity: PartyRarity): PartyTraitId[] {
  return [...PARTY_ARCHETYPES[archetypeId].traits.slice(0, PARTY_RARITIES[rarity].traitCount)];
}

function createCandidate(
  random: RandomState,
  waveIndex: number,
  offerIndex: number,
  name: string,
  archetypeId: PartyArchetypeId,
): { candidate: PartyDraftCandidate; random: RandomState } {
  const weights = RARITY_WEIGHTS_BY_WAVE[Math.max(0, Math.min(RARITY_WEIGHTS_BY_WAVE.length - 1, waveIndex))];
  const rarityRoll = weightedIndex(random, weights);
  const rarity = PARTY_RARITY_ORDER[rarityRoll.value];
  const archetype = PARTY_ARCHETYPES[archetypeId];
  return {
    candidate: {
      candidateId: `party-w${waveIndex + 1}-o${offerIndex + 1}-${archetypeId}-${rarityRoll.random.state.toString(36)}`,
      name,
      archetypeId,
      classId: archetype.classId,
      className: archetype.className,
      role: archetype.role,
      rarity,
      stats: statsAtRarity(archetypeId, rarity),
      traitIds: traitsAtRarity(archetypeId, rarity),
      color: archetype.color,
    },
    random: rarityRoll.random,
  };
}

export function generatePartyDraftOffers(
  source: RandomState,
  waveIndex: number,
): { offers: PartyDraftCandidate[]; random: RandomState } {
  let random = source;
  const tankRoll = randomInt(random, TANK_ARCHETYPE_IDS.length);
  random = tankRoll.random;
  const guaranteedTank = TANK_ARCHETYPE_IDS[tankRoll.value];
  const otherArchetypes = PARTY_ARCHETYPE_ORDER.filter((id) => id !== guaranteedTank);
  const shuffledArchetypes = shuffleWithRandom(random, otherArchetypes);
  random = shuffledArchetypes.random;
  const offerArchetypes = [guaranteedTank, ...shuffledArchetypes.values.slice(0, PARTY_OFFERS_PER_WAVE - 1)];
  const mixedArchetypes = shuffleWithRandom(random, offerArchetypes);
  random = mixedArchetypes.random;
  const shuffledNames = shuffleWithRandom(random, PARTY_NAMES);
  random = shuffledNames.random;

  const offers: PartyDraftCandidate[] = [];
  for (let index = 0; index < PARTY_OFFERS_PER_WAVE; index += 1) {
    const created = createCandidate(random, waveIndex, index, shuffledNames.values[index], mixedArchetypes.values[index]);
    offers.push(created.candidate);
    random = created.random;
  }
  return { offers, random };
}

export function recruitCandidate(candidate: PartyDraftCandidate): PartyRosterMember {
  return { ...candidate, instanceId: candidate.candidateId, hp: candidate.stats.maxHp };
}

export function canRecruitCandidate(roster: readonly PartyRosterMember[], recruitsThisWave: number): boolean {
  return roster.length < MAX_ACTIVE_ROSTER && recruitsThisWave < PARTY_RECRUITS_PER_WAVE;
}

export function nextPartyRarity(rarity: PartyRarity): PartyRarity | null {
  const index = PARTY_RARITY_ORDER.indexOf(rarity);
  return index >= 0 && index < PARTY_RARITY_ORDER.length - 1 ? PARTY_RARITY_ORDER[index + 1] : null;
}

export function upgradeRosterMemberRarity(member: PartyRosterMember): PartyRosterMember {
  const rarity = nextPartyRarity(member.rarity);
  if (!rarity) return member;
  const stats = statsAtRarity(member.archetypeId, rarity);
  const gainedMaxHp = stats.maxHp - member.stats.maxHp;
  return {
    ...member,
    rarity,
    stats,
    hp: member.hp <= 0 ? 0 : Math.min(stats.maxHp, member.hp + gainedMaxHp),
    traitIds: traitsAtRarity(member.archetypeId, rarity),
  };
}

const ALLY_COMBAT_SLOTS: readonly AllyCombatSlotId[] = ["brann", "nia", "orin", "vale"];

export function assignRosterToCombatSlots(roster: readonly PartyRosterMember[]): CombatPartyAssignment {
  const assignment: CombatPartyAssignment = { brann: null, nia: null, orin: null, vale: null };
  const available = roster.slice(0, MAX_ACTIVE_ROSTER);
  const tank = available.find((member) => member.role === "Tank") ?? available[0];
  if (!tank) return assignment;
  assignment.brann = tank;
  const remaining = available.filter((member) => member.instanceId !== tank.instanceId);
  for (let index = 0; index < remaining.length; index += 1) assignment[ALLY_COMBAT_SLOTS[index + 1]] = remaining[index];
  return assignment;
}
