import seedrandom = require('seedrandom');
import type {
  EncounterDefinition,
  EncounterResult,
  Perk,
  SurvivorState,
  SurvivorStats,
} from '@fog-explorer/api-interfaces';
import { applyPerkModifiers } from './perk-math';

export interface ResolverInput {
  seed: string;
  missionId: string;
  tickIndex: number;
  difficulty: number;
  encounter: EncounterDefinition;
  survivor: {
    id: string;
    state: SurvivorState;
    stats: SurvivorStats;
    perkSlots: Perk[];
    hookCount: number;
  };
}

const OBJECTIVES_BONUS = 0.02;
const ALTRUISM_BONUS = 0.02;
const DIFFICULTY_PENALTY = 0.12;
const ALTRUISM_TAG = 'altruistic';
const INJURY_CHANCE_BASE = 0.8;
const INJURY_CHANCE_FLOOR = 0.2;
const SURVIVAL_INJURY_REDUCTION = 0.05;

export function resolveEncounter(input: ResolverInput): EncounterResult {
  const rng = seedrandom(input.seed);

  let probability = input.encounter.baseProbability;
  probability -= (input.difficulty - 1) * DIFFICULTY_PENALTY;
  probability += input.survivor.stats.objectives * OBJECTIVES_BONUS;
  if (input.encounter.tags.includes(ALTRUISM_TAG)) {
    probability += input.survivor.stats.altruism * ALTRUISM_BONUS;
  }

  const { probability: finalProbability, applied } = applyPerkModifiers(
    probability,
    input.survivor.perkSlots,
    input.encounter.tags
  );

  const success = rng() < finalProbability;

  const survivorStateChange = success
    ? null
    : computeStateChange(
        rng,
        input.survivor.state,
        input.survivor.stats.survival,
        input.survivor.hookCount
      );

  return {
    missionId: input.missionId,
    survivorId: input.survivor.id,
    encounterKey: input.encounter.key,
    tickIndex: input.tickIndex,
    seed: input.seed,
    success,
    survivorStateChange,
    modifiersApplied: applied,
    logText: buildLogText(input.encounter.key, success, survivorStateChange),
  };
}

function computeStateChange(
  rng: seedrandom.PRNG,
  currentState: SurvivorState,
  survivalStat: number,
  hookCount: number
): EncounterResult['survivorStateChange'] {
  if (currentState === 'active') {
    const injuryChance = Math.max(
      INJURY_CHANCE_FLOOR,
      INJURY_CHANCE_BASE - survivalStat * SURVIVAL_INJURY_REDUCTION
    );
    if (rng() < injuryChance) {
      return { from: 'active', to: 'injured' };
    }
    return null;
  }

  if (currentState === 'injured') {
    return { from: 'injured', to: 'downed' };
  }

  if (currentState === 'downed' && hookCount >= 2) {
    return { from: 'downed', to: 'sacrificed' };
  }

  return null;
}

const STATE_CHANGE_TEXT: Record<string, string> = {
  'active->injured': 'Survivor was injured.',
  'injured->downed': 'Survivor was downed.',
  'downed->sacrificed': 'Survivor was sacrificed.',
};

function buildLogText(
  encounterKey: string,
  success: boolean,
  stateChange: EncounterResult['survivorStateChange']
): string {
  const outcome = success ? 'succeeded' : 'failed';
  const base = `${encounterKey}: ${outcome}.`;
  if (!stateChange) return base;
  const extra = STATE_CHANGE_TEXT[`${stateChange.from}->${stateChange.to}`];
  return extra ? `${base} ${extra}` : base;
}
