import { GameConfig } from "./config.js";
import { plotSizeFromLevel } from "./grid-utils.js";
import { LootTables, getColorNames, zeroAnimalWeights } from "./loot-tables.js";
import { ensureBotState } from "./bot-zoo.js";
import { buildDefaultRow1Cells, addStarterAnimals } from "./default-grid-layout.js";

export function defaultAnimalWeights() {
  const w = zeroAnimalWeights();
  w[getColorNames()[0]] = 1;
  return w;
}

export function normalizeZooWeights(legacy) {
  if (!legacy || typeof legacy !== "object") return defaultAnimalWeights();
  const keys = getColorNames();
  const w = zeroAnimalWeights();
  const map = { Basic: keys[0], Ocean: keys[5], Mountain: keys[10] };
  for (const [oldKey, val] of Object.entries(legacy)) {
    const newKey = map[oldKey] ?? oldKey;
    if (keys.includes(newKey)) w[newKey] = Number(val) || 0;
  }
  return w;
}

/**
 * @returns {import("./types.js").GameState}
 */
export function defaultState() {
  const [width, height] = plotSizeFromLevel(1);
  const worldZoos = buildDefaultWorldZoos();
  const cells = buildDefaultCells();
  const state = buildStatePayload(width, height, worldZoos, cells);
  addStarterAnimals(state);
  return state;
}

function buildDefaultWorldZoos() {
  const configZoos = GameConfig.WorldMap?.Zoos;
  if (configZoos && configZoos.length > 0) {
    const worldZoos = configZoos.map((z, i) => ({
      id: z.id,
      name: z.name,
      x: z.x,
      y: z.y,
      animalWeights: i === 0 ? defaultAnimalWeights() : normalizeZooWeights(z.animalWeights),
    }));
    worldZoos.forEach((zoo) => ensureBotState(zoo, zoo.id === "player"));
    return worldZoos;
  }
  return [{ id: "player", name: "Mon zoo", x: 25, y: 50, animalWeights: defaultAnimalWeights() }];
}

function buildDefaultCells() {
  return buildDefaultRow1Cells();
}

function getDefaultStats() {
  return { eggsPlaced: 0, animalsSold: 0, conveyorUpgrades: 0, plotUpgrades: 0, truckUpgrades: 0, coinsEarned: 0 };
}

function buildStatePayload(width, height, worldZoos, cells) {
  const grid = { width, height, cells };
  return {
    version: GameConfig.StateVersion,
    coins: 200,
    conveyorLevel: 1,
    plotLevel: 1,
    truckLevel: 1,
    grid,
    pendingEggTokens: [],
    nextTokenId: 1,
    conveyorOffers: [],
    lastOfferRefreshAt: 0,
    worldZoos,
    truckSale: undefined,
    worldTruckSales: [],
    lastEvolutionAt: Math.floor(Date.now() / 1000),
    laboratoryOffer: null,
    prestigeLevel: 0,
    timeOfDay: 6,
    gameDayTotal: 0,
    lastSeason: "spring",
    weather: "sun",
    lastWeatherChangeAt: 0,
    quests: [],
    lastQuestDay: "",
    stats: getDefaultStats(),
    mapZoom: 1, mapPanX: 0, mapPanY: 0, worldMapLevel: 1,
    autoMode: false,
    autoModeProfile: "balanced",
    researchPoints: 0,
    pendingBabies: [],
    receptionAnimals: [],
    saleListings: [],
    deathCountRecent: 0,
    birthCount: 0,
    reproductionTimers: [],
    visitorArrivals: [],
  };
}

const STORAGE_KEY = "builazoo_state";

/**
 * @param {import("./types.js").GameState} state
 */
export function saveState(state) {
  try {
    const toSave = { ...state };
    delete toSave.autoProfilePickerOpen;
    delete toSave.autoProfilePickerFamily;
    localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
  } catch (e) {
    console.error("saveState failed", e);
  }
}

/**
 * @returns {import("./types.js").GameState | null}
 */
export function loadState() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw === null || raw === undefined) return null;
    const data = JSON.parse(raw);
    if (!data || typeof data.coins !== "number" || !data.grid || typeof data.grid.cells !== "object") return null;
    applyLoadStateDefaults(data);
    normalizeLoadedCells(data.grid.cells);
    ensureSchoolCell(data);
    return data;
  } catch (e) {
    console.error("loadState failed", e);
    return null;
  }
}

function applyLoadStateDefaults(data) {
  applyLoadStateWorldZoos(data);
  applyLoadStateScalarDefaults(data);
  applyLoadStateLegacyCells(data);
}

function applyLoadStateWorldZoos(data) {
  if (data.coins < 100) data.coins = 200;
  if (data.pendingEggTokens === null || data.pendingEggTokens === undefined) data.pendingEggTokens = [];
  if (data.conveyorOffers === null || data.conveyorOffers === undefined) data.conveyorOffers = [];
  data.conveyorOffers = data.conveyorOffers.map((o) => ({ ...o, zooId: o.zooId ?? "player" }));
  if ((data.worldZoos === null || data.worldZoos === undefined) && GameConfig.WorldMap && GameConfig.WorldMap.Zoos) {
    data.worldZoos = [...GameConfig.WorldMap.Zoos];
  }
  if (data.worldZoos !== null && data.worldZoos !== undefined && Array.isArray(data.worldZoos)) {
    applyWorldZoosWeightsAndBots(data);
  }
  if (data.worldZoos === null || data.worldZoos === undefined) {
    data.worldZoos = [{ id: "player", name: "Mon zoo", x: 25, y: 50, animalWeights: defaultAnimalWeights() }];
  }
}

function applyWorldZoosWeightsAndBots(data) {
  const keys = getColorNames();
  data.worldZoos = data.worldZoos.map((z) => ({
    ...z,
    animalWeights: z.animalWeights && keys.some((k) => k in (z.animalWeights ?? {}))
      ? z.animalWeights
      : normalizeZooWeights(z.animalWeights),
  }));
  data.worldZoos.forEach((zoo) => ensureBotState(zoo, zoo.id === "player"));
}

/** Set data[key] to defaultVal when data[key] is null or undefined. defaultVal may be a function (called for value). */
function setScalarDefault(data, key, defaultVal) {
  if (data[key] === null || data[key] === undefined) {
    data[key] = typeof defaultVal === "function" ? defaultVal() : defaultVal;
  }
}

const LOAD_STATE_SCALAR_DEFAULTS = [
  ["worldTruckSales", []],
  ["lastEvolutionAt", () => Math.floor(Date.now() / 1000)],
  ["nextTokenId", 1],
  ["prestigeLevel", 0],
  ["timeOfDay", 6],
  ["gameDayTotal", 0],
  ["lastSeason", "spring"],
  ["weather", "sun"],
  ["lastWeatherChangeAt", 0],
  ["quests", []],
  ["lastQuestDay", ""],
  ["stats", { eggsPlaced: 0, animalsSold: 0, conveyorUpgrades: 0, plotUpgrades: 0, truckUpgrades: 0, coinsEarned: 0 }],
  ["truckLevel", 1],
  ["mapZoom", 1],
  ["mapPanX", 0],
  ["mapPanY", 0],
  ["worldMapLevel", 1],
  ["researchPoints", 0],
  ["pendingBabies", []],
  ["receptionAnimals", []],
  ["saleListings", []],
  ["deathCountRecent", 0],
  ["birthCount", 0],
  ["reproductionTimers", []],
  ["visitorArrivals", []],
];

function applyLoadStateScalarDefaults(data) {
  if (data.laboratoryOffer === undefined) data.laboratoryOffer = null;
  for (const [key, defaultVal] of LOAD_STATE_SCALAR_DEFAULTS) {
    setScalarDefault(data, key, defaultVal);
  }
  if (data.version !== GameConfig.StateVersion) data.version = GameConfig.StateVersion;
  data.autoProfilePickerOpen = false;
  data.autoProfilePickerFamily = undefined;
  if (data.attractivityBonusFromIncidents === null || data.attractivityBonusFromIncidents === undefined) {
    data.attractivityBonusFromIncidents = 0;
  }
}

function applyLoadStateLegacyCells(data) {
  if (data.grid.cells["2_1"] === null || data.grid.cells["2_1"] === undefined) data.grid.cells["2_1"] = { kind: "nursery", level: 1 };
  const c21 = data.grid.cells["2_1"];
  if (c21 && (c21.kind === "plotUpgrade" || c21.kind === "worldMapUpgrade")) data.grid.cells["2_1"] = { kind: "nursery", level: 1 };
  const c12 = data.grid.cells["1_2"];
  if (c12 && (c12.kind === "plotUpgrade" || c12.kind === "worldMapUpgrade")) delete data.grid.cells["1_2"];
}

function normalizeOneAnimalCell(cell, now) {
  if (cell.kind !== "animal") return;
  if (cell.id && !LootTables.Animals[cell.id]) cell.id = "c0_r0";
  if (cell.lastVisitedAt === null || cell.lastVisitedAt === undefined) cell.lastVisitedAt = now;
  if (cell.lastFedAt === null || cell.lastFedAt === undefined) cell.lastFedAt = cell.placedAt ?? now;
}

function normalizeOneEggCell(cell) {
  if (cell.kind === "egg" && cell.eggType && !LootTables.EggTypes[cell.eggType]) cell.eggType = "Color_1";
}

function normalizeLoadedCells(cells) {
  const now = Math.floor(Date.now() / 1000);
  for (const key of Object.keys(cells)) {
    const cell = cells[key];
    if (cell) {
      if (cell.kind === "animal") normalizeOneAnimalCell(cell, now);
      if (cell.kind === "egg") normalizeOneEggCell(cell);
    }
  }
}

function ensureSchoolCell(data) {
  const hasSchool = Object.values(data.grid.cells).some((c) => c && c.kind === "school");
  if (!hasSchool && !data.grid.cells["1_1"] && (data.conveyorLevel || 0) >= 1) {
    data.grid.cells["1_1"] = { kind: "school", level: data.conveyorLevel || 1 };
  }
}
