import { AVAILABLE_BOSS_IDS, BOSS_DEFINITIONS, BOSS_GROUP_BY_ID, bossGroupFor, type BossGroupId } from "../bossCatalog";
import type { BossId } from "../types";

export type LootRarity = "common" | "uncommon" | "rare" | "epic" | "legendary";
export type DifficultySlug = "initiate" | "veteran" | "champion" | "mythic" | "ascendant";

export interface DifficultyDefinition {
  slug: DifficultySlug;
  name: string;
  itemLevel: number;
  rarity: LootRarity;
  glyph: string;
  dropPrefix: string;
  healthMultiplier: number;
  damageMultiplier: number;
}

export interface MaterialStack {
  id: string;
  name: string;
  quantity: number;
  rarity: LootRarity;
  itemLevel: number;
  glyph: string;
}

export interface GroupDrop {
  kind: "group-drop";
  id: string;
  groupId: BossGroupId;
  difficultySlug: DifficultySlug;
  name: string;
  rarity: LootRarity;
  itemLevel: number;
  glyph: string;
  chanceLabel: string;
}

export interface BossPetDrop {
  kind: "pet";
  id: string;
  bossId: BossId;
  name: string;
  rarity: "legendary";
  glyph: string;
  dropRate: number;
  chanceLabel: string;
}

export interface GroupDropTable {
  groupId: BossGroupId;
  drops: Record<DifficultySlug, GroupDrop>;
  entries: readonly GroupDrop[];
}

export interface CollectionLog {
  dropsFound: Record<string, number>;
  petsFound: Record<string, number>;
}

export interface BossRewardAward {
  bossId: BossId;
  drop: GroupDrop;
  quantity: number;
  quantityAfter: number;
  duplicate: boolean;
  pet: BossPetDrop | null;
  petQuantityAfter: number;
}

export const BOSS_PET_DROP_RATE = 1 / 500;

export const DIFFICULTIES: readonly DifficultyDefinition[] = [
  { slug: "initiate", name: "Initiate", itemLevel: 1, rarity: "common", glyph: "R", dropPrefix: "Raw", healthMultiplier: 1, damageMultiplier: 1 },
  { slug: "veteran", name: "Veteran", itemLevel: 10, rarity: "uncommon", glyph: "G", dropPrefix: "Green", healthMultiplier: 1.45, damageMultiplier: 1.25 },
  { slug: "champion", name: "Champion", itemLevel: 15, rarity: "rare", glyph: "B", dropPrefix: "Blue", healthMultiplier: 1.7, damageMultiplier: 1.45 },
  { slug: "mythic", name: "Mythic", itemLevel: 20, rarity: "epic", glyph: "P", dropPrefix: "Purple", healthMultiplier: 2.25, damageMultiplier: 1.85 },
  { slug: "ascendant", name: "Ascendant", itemLevel: 25, rarity: "legendary", glyph: "O", dropPrefix: "Orange", healthMultiplier: 2.8, damageMultiplier: 2.25 },
] as const;

export const DIFFICULTY_BY_SLUG = Object.fromEntries(
  DIFFICULTIES.map((difficulty) => [difficulty.slug, difficulty]),
) as Record<DifficultySlug, DifficultyDefinition>;

const DIFFICULTY_ID_PREFIX: Record<DifficultySlug, string> = {
  initiate: "raw",
  veteran: "green",
  champion: "blue",
  mythic: "purple",
  ascendant: "orange",
};

function createGroupDropTable(groupId: BossGroupId): GroupDropTable {
  const group = BOSS_GROUP_BY_ID[groupId];
  const drops = Object.fromEntries(DIFFICULTIES.map((difficulty) => [
    difficulty.slug,
    {
      kind: "group-drop" as const,
      id: `${DIFFICULTY_ID_PREFIX[difficulty.slug]}-${groupId}-drop`,
      groupId,
      difficultySlug: difficulty.slug,
      name: `${difficulty.dropPrefix} Group ${group.letter} Drop`,
      rarity: difficulty.rarity,
      itemLevel: difficulty.itemLevel,
      glyph: difficulty.glyph,
      chanceLabel: "Guaranteed 1-3",
    },
  ])) as Record<DifficultySlug, GroupDrop>;
  return {
    groupId,
    drops,
    entries: DIFFICULTIES.map((difficulty) => drops[difficulty.slug]),
  };
}

function createBossPetDrop(bossId: BossId): BossPetDrop {
  const boss = BOSS_DEFINITIONS[bossId];
  return {
    kind: "pet",
    id: `${bossId}-pet`,
    bossId,
    name: `${boss.name} Pet`,
    rarity: "legendary",
    glyph: boss.icon,
    dropRate: BOSS_PET_DROP_RATE,
    chanceLabel: "1 in 500",
  };
}

export const GROUP_DROP_TABLES = Object.fromEntries(
  Object.keys(BOSS_GROUP_BY_ID).map((groupId) => [groupId, createGroupDropTable(groupId as BossGroupId)]),
) as Record<BossGroupId, GroupDropTable>;

export const BOSS_PET_DROPS = Object.fromEntries(
  AVAILABLE_BOSS_IDS.map((bossId) => [bossId, createBossPetDrop(bossId)]),
) as Record<BossId, BossPetDrop>;

export function groupDropTable(groupId: BossGroupId): GroupDropTable {
  return GROUP_DROP_TABLES[groupId];
}

export function groupDrop(groupId: BossGroupId, difficultySlug: DifficultySlug): GroupDrop {
  return GROUP_DROP_TABLES[groupId].drops[difficultySlug];
}

export function bossGroupDrop(bossId: BossId, difficultySlug: DifficultySlug): GroupDrop {
  return groupDrop(bossGroupFor(bossId).id, difficultySlug);
}

export function bossPetDrop(bossId: BossId): BossPetDrop {
  return BOSS_PET_DROPS[bossId];
}

export function groupDropQuantity(random: () => number = Math.random): number {
  const roll = random();
  if (roll < 0.15) return 3;
  if (roll < 0.5) return 2;
  return 1;
}

export function rollBossReward(
  bossId: BossId,
  difficultySlug: DifficultySlug,
  inventory: readonly MaterialStack[],
  collectionLog: CollectionLog,
  random: () => number = Math.random,
): { award: BossRewardAward; inventory: MaterialStack[]; collectionLog: CollectionLog } {
  const drop = bossGroupDrop(bossId, difficultySlug);
  const pet = bossPetDrop(bossId);
  const quantity = groupDropQuantity(random);
  const existing = inventory.find((item) => item.id === drop.id);
  const inventoryAfter = existing
    ? inventory.map((item) => item.id === drop.id ? { ...item, quantity: item.quantity + quantity } : { ...item })
    : [...inventory.map((item) => ({ ...item })), { id: drop.id, name: drop.name, quantity, rarity: drop.rarity, itemLevel: drop.itemLevel, glyph: drop.glyph }];
  const petAwarded = random() < pet.dropRate;
  const petQuantityAfter = (collectionLog.petsFound[pet.id] ?? 0) + Number(petAwarded);
  return {
    award: {
      bossId,
      drop,
      quantity,
      quantityAfter: (existing?.quantity ?? 0) + quantity,
      duplicate: Boolean(existing),
      pet: petAwarded ? pet : null,
      petQuantityAfter,
    },
    inventory: inventoryAfter,
    collectionLog: {
      dropsFound: {
        ...collectionLog.dropsFound,
        [drop.id]: (collectionLog.dropsFound[drop.id] ?? 0) + quantity,
      },
      petsFound: petAwarded
        ? { ...collectionLog.petsFound, [pet.id]: petQuantityAfter }
        : { ...collectionLog.petsFound },
    },
  };
}

export function normalizeDifficultySlug(value: unknown): DifficultySlug {
  return typeof value === "string" && value in DIFFICULTY_BY_SLUG
    ? value as DifficultySlug
    : "initiate";
}

export function createEmptyCollectionLog(): CollectionLog {
  return { dropsFound: {}, petsFound: {} };
}
