import { GameConfig } from "./config.js";
import { LootTables, getAnimalToEggTypeMap } from "./loot-tables.js";
import { defaultAnimalWeights } from "./state.js";
import { getZooSkillLevel } from "./bot-zoo.js";
import { getConveyorUpgradeCost, getSchoolUpgradeCost, getTruckUpgradeCost } from "./economy.js";
import { pickId } from "./weighted-random.js";
import { cellKey } from "./grid-utils.js";

const ANIMAL_TO_EGG_TYPE = getAnimalToEggTypeMap();
const DEFAULT_ZOO_WEIGHTS = defaultAnimalWeights();

/**
 * Skill level = max level among school cells, or state.conveyorLevel for backward compat.
 * @param {import("./types.js").GameState} state
 * @returns {number}
 */
export function getSkillLevel(state) {
  let maxSchool = 0;
  for (const cell of Object.values(state.grid.cells)) {
    if (cell.kind === "school") maxSchool = Math.max(maxSchool, cell.level);
  }
  return maxSchool || state.conveyorLevel || 1;
}

/**
 * @param {number} conveyorLevel
 * @returns {Array<{ id: string, weight: number }>}
 */
function getEligibleEggTypes(conveyorLevel) {
  const entries = [];
  for (const [eggType, def] of Object.entries(LootTables.EggTypes)) {
    if (conveyorLevel >= def.minConveyorLevel)
      entries.push({ id: eggType, weight: 100 - def.minConveyorLevel * 8 });
  }
  return entries;
}

/**
 * Player zoo weights from grid: more animals of a type => more of that egg type at own zoo.
 * @param {import("./types.js").GameState} state
 * @returns {Record<string, number>}
 */
export function getPlayerZooWeights(state) {
  const colorKeys = Object.keys(LootTables.EggTypes);
  const w = Object.fromEntries(colorKeys.map((k) => [k, 0]));
  for (const cell of Object.values(state.grid.cells)) {
    if (cell.kind === "animal") {
      const eggType = ANIMAL_TO_EGG_TYPE[cell.id];
      if (eggType) w[eggType] = (w[eggType] ?? 0) + 1;
    }
  }
  return w;
}

/**
 * @param {import("./types.js").GameState} state
 * @param {object} zoo
 * @param {string} eggType
 * @returns {{ skillLevel: number, weight: number } | null}
 */
function getZooSkillAndWeightForEgg(state, zoo, eggType) {
  const skillLevel = zoo.id === "player" ? getSkillLevel(state) : getZooSkillLevel(zoo);
  const eggDef = LootTables.EggTypes[eggType];
  const minLevel = eggDef ? eggDef.minConveyorLevel : 1;
  if (skillLevel < minLevel) return null;
  const playerWeights = zoo.id === "player" ? getPlayerZooWeights(state) : (zoo.animalWeights ?? {});
  const weight = playerWeights[eggType] ?? 0;
  return weight > 0 ? { skillLevel, weight } : null;
}

/**
 * Zoos that can offer this egg type (skill level allows it and zoo has weight for it).
 * @param {import("./types.js").GameState} state
 * @param {string} eggType
 * @returns {Array<{ id: string, weight: number }>}
 */
function getZoosForEggType(state, eggType) {
  const zoos = state.worldZoos ?? [{ id: "player", name: "Mon zoo", x: 25, y: 50, animalWeights: DEFAULT_ZOO_WEIGHTS }];
  const entries = [];
  for (const zoo of zoos) {
    const info = getZooSkillAndWeightForEgg(state, zoo, eggType);
    if (info) entries.push({ id: zoo.id, weight: info.weight });
  }
  if (entries.length === 0) entries.push({ id: "player", weight: 1 });
  return entries;
}

/**
 * @param {import("./types.js").GameState} state
 * @param {number} nowUnix
 */
export function refreshOffers(state, nowUnix) {
  const rng = () => Math.random();
  const skillLevel = getSkillLevel(state);
  const pool = getEligibleEggTypes(skillLevel);
  const offers = [];
  for (let i = 0; i < GameConfig.Conveyor.OfferCount; i++) {
    const eggType = pickId(rng, pool);
    const eggDef = LootTables.EggTypes[eggType];
    const zooPool = getZoosForEggType(state, eggType);
    const zooId = zooPool.length ? pickId(rng, zooPool) : "player";
    offers.push({ eggType, price: eggDef.price, zooId });
  }
  const animalIds = Object.keys(LootTables.Animals ?? {});
  if (animalIds.length > 0) {
    const babyAnimalId = animalIds[Math.floor(rng() * animalIds.length)];
    const babyDef = LootTables.Animals[babyAnimalId];
    const babyPrice = babyDef ? Math.floor(50 + (babyDef.rarityLevel ?? 1) * 30) : 80;
    offers.push({ type: "baby", animalId: babyAnimalId, price: babyPrice, zooId: "player" });
    const adultAnimalId = animalIds[Math.floor(rng() * animalIds.length)];
    const adultDef = LootTables.Animals[adultAnimalId];
    const adultPrice = adultDef ? Math.floor(80 + (adultDef.rarityLevel ?? 1) * 40) : 120;
    offers.push({ type: "animal", animalId: adultAnimalId, price: adultPrice, zooId: "player" });
  }
  state.conveyorOffers = offers;
  state.lastOfferRefreshAt = nowUnix;
}

/**
 * @param {import("./types.js").GameState} state
 * @param {number} nowUnix
 * @returns {boolean}
 */
export function shouldRefresh(state, nowUnix) {
  return nowUnix - state.lastOfferRefreshAt >= GameConfig.Conveyor.RefreshSeconds;
}

/**
 * Upgrade school at cell (x,y). Returns [ok, reason].
 * @param {import("./types.js").GameState} state
 * @param {number} x
 * @param {number} y
 * @returns {[boolean, string?]}
 */
export function tryUpgradeSchool(state, x, y) {
  const key = cellKey(x, y);
  const cell = state.grid.cells[key];
  if (cell === null || cell === undefined || cell.kind !== "school") return [false, "NoSchool"];
  const maxLevel = (GameConfig.School && GameConfig.School.MaxLevel) || GameConfig.Conveyor.MaxLevel;
  if (cell.level >= maxLevel) return [false, "ConveyorMaxLevel"];
  const cost = getSchoolUpgradeCost(cell.level);
  if (state.coins < cost) return [false, "NotEnoughCoins"];
  state.coins -= cost;
  cell.level += 1;
  state.conveyorLevel = getSkillLevel(state);
  state.lastEvolutionAt = Math.floor(Date.now() / 1000);
  if (state.stats) state.stats.conveyorUpgrades = (state.stats.conveyorUpgrades ?? 0) + 1;
  return [true, undefined];
}

/**
 * Upgrade truck. Returns [ok, reason].
 * @param {import("./types.js").GameState} state
 * @returns {[boolean, string?]}
 */
export function tryUpgradeTruck(state) {
  const level = state.truckLevel ?? 1;
  const maxLevel = (GameConfig.Truck && GameConfig.Truck.MaxLevel) || 5;
  if (level >= maxLevel) return [false, "TruckMaxLevel"];
  const cost = getTruckUpgradeCost(level);
  if (state.coins < cost) return [false, "NotEnoughCoins"];
  state.coins -= cost;
  state.truckLevel = level + 1;
  state.lastEvolutionAt = Math.floor(Date.now() / 1000);
  if (state.stats) state.stats.truckUpgrades = (state.stats.truckUpgrades ?? 0) + 1;
  return [true, undefined];
}

/**
 * @param {import("./types.js").GameState} state
 * @returns {[boolean, string?]}
 */
export function tryUpgrade(state) {
  if (state.conveyorLevel >= GameConfig.Conveyor.MaxLevel) return [false, "ConveyorMaxLevel"];
  const cost = getConveyorUpgradeCost(state.conveyorLevel);
  if (state.coins < cost) return [false, "NotEnoughCoins"];
  state.coins -= cost;
  state.conveyorLevel += 1;
  state.lastEvolutionAt = Math.floor(Date.now() / 1000);
  if (state.stats) state.stats.conveyorUpgrades = (state.stats.conveyorUpgrades ?? 0) + 1;
  return [true, undefined];
}

/**
 * @param {import("./types.js").GameState} state
 * @param {string} eggType
 * @returns {{ eggType: string, price: number, zooId?: string } | null}
 */
export function findOffer(state, eggType) {
  return state.conveyorOffers.find((o) => o.eggType === eggType) ?? null;
}

/**
 * @param {import("./types.js").GameState} state
 * @param {string} [animalId]
 * @returns {{ type: "baby", animalId: string, price: number, zooId?: string } | null}
 */
export function findBabyOffer(state, animalId) {
  const o = state.conveyorOffers.find((x) => x.type === "baby" && (animalId === null || animalId === undefined || x.animalId === animalId));
  return o && o.type === "baby" ? o : null;
}

/**
 * @param {import("./types.js").GameState} state
 * @param {string} [animalId]
 * @returns {{ type: "animal", animalId: string, price: number, zooId?: string } | null}
 */
export function findAnimalOffer(state, animalId) {
  const o = state.conveyorOffers.find((x) => x.type === "animal" && (animalId === null || animalId === undefined || x.animalId === animalId));
  return o && o.type === "animal" ? o : null;
}

/**
 * Pick another zoo (not player) for truck sale animation.
 * @param {import("./types.js").GameState} state
 * @returns {string}
 */
export function pickSaleTargetZoo(state) {
  const zoos = (state.worldZoos ?? []).filter((z) => z.id !== "player");
  if (zoos.length === 0) return "player";
  return zoos[Math.floor(Math.random() * zoos.length)].id;
}
