import { getCargoUsed, sellableCargo } from "./sliceEconomy";
import type { GameSliceSession, SliceService } from "./types";
import { isBeltPoi, isStationPoi, readablePoiName } from "./sliceWorld";

export type SliceActionId =
  | "undock"
  | "approachTarget"
  | "dock"
  | "startMining"
  | "stopMining"
  | "openRefining"
  | "openFitting"
  | "openMarket"
  | "startCombat";

export type SliceAction = {
  id: SliceActionId;
  label: string;
  enabled: boolean;
  reason: string | null;
  tone?: "primary" | "normal" | "danger" | "debug";
};

export type SliceFacts = {
  activePoiName: string;
  cargoUsed: number;
  freeCargo: number;
  oreQuantity: number;
  hasRefinableOre: boolean;
  hasSellableCargo: boolean;
  isDocked: boolean;
  isAtBelt: boolean;
  isAtStation: boolean;
  isBusy: boolean;
  canMine: boolean;
};

const busyReason = (session: GameSliceSession) => {
  const operation = session.activeOperation;
  if (!operation) return "Another ship operation is already in progress.";
  if (operation.kind === "travel") return `Approaching ${operation.target.name}.`;
  if (operation.kind === "mining") return `Mining cycle active at ${operation.targetName}.`;
  if (operation.kind === "docking") return `Docking at ${operation.stationName}.`;
  if (operation.kind === "undocking") return "Undocking sequence active.";
  return "Another ship operation is already in progress.";
};

export function getSliceFacts(session: GameSliceSession): SliceFacts {
  const cargoUsed = getCargoUsed(session);
  const oreQuantity = session.cargo.find((item) => item.item === "Veldspar")?.quantity ?? 0;
  const isDocked = Boolean(session.dockedStationPoiId);
  const isAtBelt = isBeltPoi(session.currentPoiId);
  const isAtStation = isStationPoi(session.currentPoiId);
  const isBusy = Boolean(session.activeOperation);
  return {
    activePoiName: session.selectedTarget?.poiId === session.currentPoiId ? session.selectedTarget.name : readablePoiName(session.currentPoiId),
    cargoUsed,
    freeCargo: Math.max(0, session.cargoCapacity - cargoUsed),
    oreQuantity,
    hasRefinableOre: oreQuantity >= 333,
    hasSellableCargo: sellableCargo(session.cargo).length > 0,
    isDocked,
    isAtBelt,
    isAtStation,
    isBusy,
    canMine: isAtBelt && !isDocked && !isBusy && cargoUsed < session.cargoCapacity,
  };
}

export function canUseStationService(session: GameSliceSession, service: SliceService): boolean {
  const facts = getSliceFacts(session);
  if (!facts.isDocked || facts.isBusy) return false;
  if (service === "refining") return facts.hasRefinableOre;
  if (service === "market") return facts.hasSellableCargo;
  return true;
}

export function getActionBlockedReason(session: GameSliceSession, actionId: SliceActionId): string | null {
  const facts = getSliceFacts(session);
  const busy = session.activeOperation ? busyReason(session) : null;

  switch (actionId) {
    case "undock":
      if (busy) return busy;
      return facts.isDocked ? null : "Docking clamps are already released.";
    case "approachTarget":
      if (busy) return busy;
      if (facts.isDocked) return "Undock before local navigation.";
      if (!session.selectedTarget) return "Select a local target first.";
      if (session.selectedTarget.poiId && session.currentPoiId === session.selectedTarget.poiId) return `Already holding at ${session.selectedTarget.name}.`;
      return null;
    case "dock":
      if (busy) return busy;
      if (facts.isDocked) return "Already docked.";
      return facts.isAtStation ? null : "Approach the station grid before docking.";
    case "startMining":
      if (busy) return busy;
      if (!facts.isAtBelt) return "Approach the asteroid belt before mining.";
      if (facts.isDocked) return "Undock before activating mining lasers.";
      if (facts.freeCargo <= 0) return "Cargo hold is full.";
      return null;
    case "stopMining":
      return session.activeOperation?.kind === "mining" ? null : "No mining cycle is active.";
    case "openRefining":
      if (busy) return busy;
      if (!facts.isDocked) return "Dock at the station to use refining.";
      return facts.hasRefinableOre ? null : "Refining needs at least 333 Veldspar.";
    case "openFitting":
      if (busy) return busy;
      return facts.isDocked ? null : "Dock at the station to use fitting.";
    case "openMarket":
      if (busy) return busy;
      if (!facts.isDocked) return "Dock at the station to use the market.";
      return facts.hasSellableCargo ? null : "No sellable cargo is available.";
    case "startCombat":
      if (busy) return busy;
      return facts.isDocked ? "Undock before starting a combat trial." : null;
  }
}

const action = (session: GameSliceSession, id: SliceActionId, label: string, tone: SliceAction["tone"] = "normal"): SliceAction => {
  const reason = getActionBlockedReason(session, id);
  return { id, label, enabled: reason === null, reason, tone };
};

export function getContextualSliceActions(session: GameSliceSession): SliceAction[] {
  const facts = getSliceFacts(session);

  if (session.activeOperation?.kind === "mining") {
    return [action(session, "stopMining", "Stop Mining", "danger")];
  }

  if (session.activeOperation) {
    const baseId: SliceActionId = session.activeOperation.kind === "travel" ? "approachTarget" : session.activeOperation.kind === "docking" ? "dock" : "undock";
    const label = session.activeOperation.kind === "travel" ? "Approach Target" : session.activeOperation.kind === "docking" ? "Dock" : "Undock";
    return [action(session, baseId, label, "primary")];
  }

  if (facts.isDocked) {
    return [
      action(session, "undock", "Undock", "primary"),
      action(session, "openRefining", "Refining"),
      action(session, "openFitting", "Fitting"),
      action(session, "openMarket", "Market"),
    ];
  }

  const actions: SliceAction[] = [];
  if (session.selectedTarget && (!session.selectedTarget.poiId || session.currentPoiId !== session.selectedTarget.poiId)) {
    actions.push(action(session, "approachTarget", `Approach ${session.selectedTarget.name}`, "primary"));
  }
  if (facts.isAtStation) actions.push(action(session, "dock", "Dock", "primary"));
  if (facts.isAtBelt) actions.push(action(session, "startMining", "Start Mining", "primary"));
  actions.push(action(session, "startCombat", "Start Combat Trial"));
  return actions;
}
