import { tryPlaceEgg, getNurseryCellKeysOrdered } from "./zoo.js";
import { tryUpgradeSchool } from "./conveyor.js";
import {
  tryBuildNursery,
  tryBuildSouvenirShop,
  tryBuildResearch,
  tryBuildBilleterie,
  tryBuildFood,
  tryBuildReception,
  tryBuildBiomeChangeColor,
  tryBuildBiomeChangeTemp,
  tryUpgradeNursery,
  tryUpgradeSouvenirShop,
  tryUpgradeResearch,
  tryUpgradeBilleterie,
  tryUpgradeFood,
  tryUpgradeReception,
  tryUpgradeBiomeChangeColor,
  tryUpgradeBiomeChangeTemp,
} from "./placement.js";
import { t, errorMessage } from "./texts-fr.js";
import { attachDragListeners, handleCellDropNurseryReceptionToken, handleCellDropEggTypeOrMove } from "./ui-grid-drag.js";

const BUILD_FNS = [
  ["nursery", tryBuildNursery],
  ["shop", tryBuildSouvenirShop],
  ["research", tryBuildResearch],
  ["billeterie", tryBuildBilleterie],
  ["food", tryBuildFood],
  ["reception", tryBuildReception],
  ["biomeColor", tryBuildBiomeChangeColor],
  ["biomeTemp", tryBuildBiomeChangeTemp],
];

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, emptyCellChoice: { x: number, y: number } | null, selectedTokenId: number | null, lastActionWasDrop: boolean, clampSelection: () => void, selected: { x: number, y: number } }} ctx
 * @param {string} choice
 * @param {number} x
 * @param {number} y
 */
export function handleCellClickChoice(ctx, choice, x, y) {
  const { state, setError } = ctx;
  for (const [name, fn] of BUILD_FNS) {
    if (choice === name) {
      const [ok, reason] = fn(state, x, y);
      if (!ok) setError(String(t.errorPrefix).replace("%s", errorMessage[reason] ?? reason));
      else setError("");
      ctx.emptyCellChoice.current = null;
      return;
    }
  }
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void }} ctx
 * @param {import("./types.js").GridCell} cell
 * @param {number} x
 * @param {number} y
 * @returns {boolean}
 */
export function handleCellClickUpgradeShops(ctx, cell, x, y) {
  const { state, setState, setError, playSound } = ctx;
  if (cell.kind === "souvenirShop") {
    const [ok, reason] = tryUpgradeSouvenirShop(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "SouvenirShopMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  if (cell.kind === "research") {
    const [ok, reason] = tryUpgradeResearch(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "ResearchMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  if (cell.kind === "billeterie") {
    const [ok, reason] = tryUpgradeBilleterie(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "BilleterieMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  if (cell.kind === "food") {
    const [ok, reason] = tryUpgradeFood(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "FoodMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  return false;
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void }} ctx
 * @param {import("./types.js").GridCell} cell
 * @param {{ x: number, y: number, key: string }} pos
 * @returns {boolean}
 */
export function handleCellClickUpgradeRest(ctx, cell, pos) {
  const { x, y, key } = pos;
  const { state, setState, setError, playSound } = ctx;
  if (cell.kind === "reception") {
    const hasAnimal = (state.receptionAnimals ?? []).some((r) => r.receptionCellKey === key);
    if (!hasAnimal) {
      const [ok, reason] = tryUpgradeReception(state, x, y);
      if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "ReceptionMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    }
    setState();
    return true;
  }
  if (cell.kind === "biomeChangeColor") {
    const [ok, reason] = tryUpgradeBiomeChangeColor(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "BiomeChangeColorMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  if (cell.kind === "biomeChangeTemp") {
    const [ok, reason] = tryUpgradeBiomeChangeTemp(state, x, y);
    if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "BiomeChangeTempMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return true;
  }
  if (cell.kind === "school") {
    const [ok, reason] = tryUpgradeSchool(state, x, y);
    if (!ok) { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); } else { setError(""); playSound("schoolUpgrade"); }
    setState();
    return true;
  }
  return false;
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, selectedTokenId: number | null, emptyCellChoice: { x: number, y: number } | null }} ctx
 * @param {boolean} empty
 * @param {number} x
 * @param {number} y
 */
export function handleCellClickPlaceOrSelect(ctx, empty, x, y) {
  const { state, setState, setError, playSound } = ctx;
  const nurseryKeys = getNurseryCellKeysOrdered(state);
  let firstTokenId = null;
  for (const k of nurseryKeys) {
    const c = state.grid.cells[k];
    if (c && c.kind === "nursery" && c.tokenId !== null && c.tokenId !== undefined) {
      firstTokenId = c.tokenId;
      break;
    }
  }
  const tokenId = ctx.selectedTokenId.current ?? firstTokenId;
  if (empty && tokenId !== null && tokenId !== undefined) {
    const nowUnix = Math.floor(Date.now() / 1000);
    const [ok, reason] = tryPlaceEgg(state, { tokenId, x, y, nowUnix });
    if (ok) { ctx.selectedTokenId.current = null; setError(""); playSound("place"); } else { setError(String(t.errorPrefix).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    setState();
    return;
  }
  if (empty) ctx.emptyCellChoice.current = { x, y };
  setState();
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, gridEl: HTMLElement }} ctx
 * @param {{ div: HTMLElement, cell: import("./types.js").GridCell | null | undefined, x: number, y: number, key: string }} opts
 */
export function attachCellListeners(ctx, opts) {
  const { div, cell, x, y, key } = opts;
  attachDragListeners(ctx, opts);
  div.addEventListener("dragover", (e) => {
    e.preventDefault();
    const hasEggType = e.dataTransfer.types.includes("application/x-builazoo-eggtype");
    const hasTokenId = e.dataTransfer.types.includes("application/x-builazoo-tokenid");
    const hasNurseryKey = e.dataTransfer.types.includes("application/x-builazoo-nursery-cell-key");
    const hasReceptionKey = e.dataTransfer.types.includes("application/x-builazoo-reception-cell-key");
    e.dataTransfer.dropEffect = hasEggType || hasTokenId ? "copy" : "move";
    if ((cell === null || cell === undefined) && (hasEggType || hasTokenId || hasNurseryKey || hasReceptionKey)) {
      div.classList.add("dragover");
    }
  });
  div.addEventListener("dragleave", () => div.classList.remove("dragover"));
  div.addEventListener("drop", (e) => {
    e.preventDefault();
    div.classList.remove("dragover");
    handleCellDrop(ctx, e, { toX: Number(div.dataset.x), toY: Number(div.dataset.y), cell });
  });
  div.addEventListener("click", (e) => handleCellClick(ctx, { e, cell, x, y, key }));
  div.addEventListener("keydown", (e) => {
    if (e.key === "Enter" || e.key === " ") { e.preventDefault(); div.click(); }
  });
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, gridEl: HTMLElement }} ctx
 * @param {DragEvent} e
 * @param {{ toX: number, toY: number, cell: import("./types.js").GridCell | null | undefined }} target
 */
function handleCellDrop(ctx, e, target) {
  const { toX, toY, cell } = target;
  const empty = cell === null || cell === undefined;
  if (handleCellDropNurseryReceptionToken(ctx, e, { x: toX, y: toY, empty })) return;
  handleCellDropEggTypeOrMove(ctx, e, { x: toX, y: toY, empty });
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, emptyCellChoice: { x: number, y: number } | null, selectedTokenId: number | null, lastActionWasDrop: boolean, clampSelection: () => void, selected: { x: number, y: number } }} ctx
 * @param {{ e: Event, cell: import("./types.js").GridCell | null | undefined, x: number, y: number, key: string }} opts
 */
function handleCellClick(ctx, opts) {
  const { e, cell, x, y, key } = opts;
  const { state, setState, setError, playSound } = ctx;
  if (ctx.lastActionWasDrop.current) {
    ctx.lastActionWasDrop.current = false;
    return;
  }
  const choiceBtn = e.target.closest(".cell-choice-btn");
  const empty = cell === null || cell === undefined;
  if (choiceBtn && empty && ctx.emptyCellChoice.current && ctx.emptyCellChoice.current.x === x && ctx.emptyCellChoice.current.y === y) {
    handleCellClickChoice(ctx, choiceBtn.dataset.choice, x, y);
    setState();
    return;
  }
  if (cell !== null && cell !== undefined && cell.kind === "nursery" && cell.tokenId !== null && cell.tokenId !== undefined) {
    ctx.selectedTokenId.current = cell.tokenId;
    setState();
    return;
  }
  if (cell !== null && cell !== undefined && cell.kind === "nursery" && (cell.tokenId === null || cell.tokenId === undefined)) {
    const hasBaby = (state.pendingBabies ?? []).some((p) => p.nurseryCellKey === key);
    if (!hasBaby) {
      const [ok, reason] = tryUpgradeNursery(state, x, y);
      if (ok) { setError(""); playSound("upgrade"); } else if (reason !== "NurseryMaxLevel") { setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); }
    }
    setState();
    return;
  }
  if (handleCellClickUpgrade(ctx, { cell, x, y, key })) return;
  ctx.selected.x = x;
  ctx.selected.y = y;
  ctx.clampSelection();
  if (empty && ctx.emptyCellChoice.current && ctx.emptyCellChoice.current.x === x && ctx.emptyCellChoice.current.y === y) {
    ctx.emptyCellChoice.current = null;
    setState();
    return;
  }
  handleCellClickPlaceOrSelect(ctx, empty, x, y);
}

/**
 * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void }} ctx
 * @param {{ cell: import("./types.js").GridCell | null | undefined, x: number, y: number, key: string }} opts
 * @returns {boolean}
 */
function handleCellClickUpgrade(ctx, opts) {
  const { cell, x, y, key } = opts;
  if (cell === null || cell === undefined) return false;
  if (handleCellClickUpgradeShops(ctx, cell, x, y)) return true;
  if (handleCellClickUpgradeRest(ctx, cell, { x, y, key })) return true;
  return false;
}

export { handleCellDrop, handleCellClick };
