/**
 * Visitor incidents (thirst, bin full, bench required, animal too far, want photo).
 * Appear more often during wait phases; resolve by click for bonus, or timeout applies penalty.
 */

import { GameConfig } from "./config.js";

/** Incident type keys for i18n and display. */
export const INCIDENT_TYPES = ["thirst", "bin", "bench", "animalFar", "photo"];

/** Emoji per incident type for bubble display. */
export const INCIDENT_EMOJI = { thirst: "💧", bin: "🗑️", bench: "🪑", animalFar: "🦌", photo: "📷" };

/**
 * True when truck (egg or sale) is in progress.
 * @param {import("./types.js").GameState} state
 * @returns {boolean}
 */
function hasTruckWait(state) {
  if (state.eggPurchaseTruck && state.eggPurchaseTruck.startAt) return true;
  if (state.truckSale && state.truckSale.startAt) return true;
  return false;
}

/**
 * True when there are API sales as buyer undelivered with pending validation.
 * @param {import("./types.js").GameState} state
 * @returns {boolean}
 */
function hasApiUndeliveredWait(state) {
  const api = state.salesFromApi;
  if (!api || !api.asBuyerUndelivered || api.asBuyerUndelivered.length === 0) return false;
  const nowMs = Date.now();
  for (const s of api.asBuyerUndelivered) {
    const validatedAtMs = s.validated_at ? new Date(s.validated_at).getTime() : 0;
    const pending = (s.status === "sold" || s.status === "validated") && validatedAtMs > nowMs;
    if (pending) return true;
  }
  return false;
}

/**
 * True when player is in a wait phase (truck moving, sale pending validation, etc.).
 * @param {import("./types.js").GameState} state
 * @returns {boolean}
 */
export function isInWaitPhase(state) {
  return hasTruckWait(state) || hasApiUndeliveredWait(state);
}

/**
 * Expire timed-out incidents and apply penalty. Returns indices to remove from arrivals.
 * @param {import("./types.js").VisitorArrival[]} arrivals
 * @param {{ nowUnix: number, timeoutSec: number, penalty: number, stateRef: { attractivityBonusFromIncidents: number } }} opts
 * @returns {number[]}
 */
function expireIncidents(arrivals, opts) {
  const { nowUnix, timeoutSec, penalty, stateRef } = opts;
  const toRemove = [];
  for (let i = 0; i < arrivals.length; i++) {
    const v = arrivals[i];
    if (v.incidentType !== null && v.incidentType !== undefined) {
      if (nowUnix - (v.incidentSince ?? nowUnix) >= timeoutSec) {
        stateRef.attractivityBonusFromIncidents = (stateRef.attractivityBonusFromIncidents ?? 0) - penalty;
        toRemove.push(i);
      }
    }
  }
  return toRemove;
}

/**
 * Spawn new incidents on visitors without one (by chance).
 * @param {import("./types.js").VisitorArrival[]} arrivals
 * @param {number} chance
 * @param {number} nowUnix
 */
function spawnIncidents(arrivals, chance, nowUnix) {
  for (let i = 0; i < arrivals.length; i++) {
    const v = arrivals[i];
    const hasNoIncident = v.incidentType === null || v.incidentType === undefined;
    if (hasNoIncident && Math.random() < chance) {
      v.incidentType = INCIDENT_TYPES[Math.floor(Math.random() * INCIDENT_TYPES.length)];
      v.incidentSince = nowUnix;
    }
  }
}

/**
 * @returns {{ baseChance: number, waitMult: number, timeoutSec: number, penalty: number }}
 */
function getIncidentConfig() {
  const cfg = GameConfig.Visitor;
  return {
    baseChance: cfg?.IncidentChanceBase ?? 0.002,
    waitMult: cfg?.IncidentChanceWaitMultiplier ?? 4,
    timeoutSec: cfg?.IncidentTimeoutSeconds ?? 45,
    penalty: cfg?.IncidentUnresolvedAttractivityPenalty ?? 0.2,
  };
}

/**
 * Spawn and expire incidents. Call after tickVisitorArrivals.
 * @param {import("./types.js").GameState} state
 * @param {number} nowUnix
 */
export function tickVisitorIncidents(state, nowUnix) {
  const arrivals = state.visitorArrivals ?? [];
  const { baseChance, waitMult, timeoutSec, penalty } = getIncidentConfig();
  const inWait = isInWaitPhase(state);
  const chance = inWait ? baseChance * waitMult : baseChance;
  const toRemove = expireIncidents(arrivals, { nowUnix, timeoutSec, penalty, stateRef: state });
  for (let r = toRemove.length - 1; r >= 0; r--) {
    arrivals.splice(toRemove[r], 1);
  }
  spawnIncidents(arrivals, chance, nowUnix);
}

/**
 * Apply resolve bonus (coins + attractivity) to state. Mutates state and v.
 * @param {import("./types.js").GameState} state
 * @param {import("./types.js").VisitorArrival} v
 */
function applyResolveBonus(state, v) {
  const cfg = GameConfig.Visitor;
  const coinBonus = cfg?.IncidentResolveCoinBonus ?? 8;
  const attractivityBonus = cfg?.IncidentResolveAttractivityBonus ?? 0.15;
  state.coins += coinBonus;
  state.attractivityBonusFromIncidents = (state.attractivityBonusFromIncidents ?? 0) + attractivityBonus;
  if (state.stats) state.stats.coinsEarned = (state.stats.coinsEarned ?? 0) + coinBonus;
  delete v.incidentType;
  delete v.incidentSince;
}

/**
 * Resolve incident for visitor at index: clear incident, add coins and attractivity bonus. Mutates state.
 * @param {import("./types.js").GameState} state
 * @param {number} visitorIndex
 * @returns {boolean} true if an incident was resolved
 */
export function resolveIncident(state, visitorIndex) {
  const arrivals = state.visitorArrivals ?? [];
  const v = arrivals[visitorIndex];
  if (!v || (v.incidentType === null || v.incidentType === undefined)) return false;
  applyResolveBonus(state, v);
  return true;
}
