import type { HealerClassId, HealerMechanicState, MemberId, PartyMember } from "./types";

export interface BeaconHealResult {
  party: PartyMember[];
  directHealing: number;
  beaconHealing: number;
}

export interface TimeAnchorResult {
  party: PartyMember[];
  mechanic: HealerMechanicState;
  restored: number;
  anchored: boolean;
}

export interface TimeLoopResult {
  party: PartyMember[];
  mechanic: HealerMechanicState;
  restored: number;
}

export function healerMaxResource(classId: HealerClassId): number {
  if (classId === "druid") return 5;
  if (classId === "shaman") return 2;
  if (classId === "paladin" || classId === "chronomancer") return 3;
  return 0;
}

export function createHealerMechanicState(classId: HealerClassId): HealerMechanicState {
  return {
    resource: 0,
    maxResource: healerMaxResource(classId),
    flourishExpiresAt: 0,
    nextPulseAt: 0,
    beaconTargetId: null,
    beaconExpiresAt: 0,
    avengingCrusaderExpiresAt: 0,
    temporalAnchor: null,
    timeLoop: null,
  };
}

export function isBeaconOfLightTarget(
  memberId: MemberId,
  mechanic: HealerMechanicState,
  time: number,
): boolean {
  return mechanic.beaconTargetId === memberId && mechanic.beaconExpiresAt > time;
}

function healLiving(member: PartyMember, amount: number): PartyMember {
  if (member.hp <= 0 || amount <= 0) return member;
  return { ...member, hp: Math.min(member.maxHp, member.hp + amount) };
}

export function healTargetAndBeacon(
  party: readonly PartyMember[],
  targetIndex: number,
  amount: number,
  mechanic: HealerMechanicState,
  time: number,
  echoFraction = 0.4,
): BeaconHealResult {
  const target = party[targetIndex];
  if (!target || target.hp <= 0) return { party: [...party], directHealing: 0, beaconHealing: 0 };
  const next = [...party];
  const healedTarget = healLiving(target, amount);
  const directHealing = healedTarget.hp - target.hp;
  next[targetIndex] = healedTarget;

  let beaconHealing = 0;
  const beaconTargetId = mechanic.beaconTargetId;
  if (beaconTargetId
    && isBeaconOfLightTarget(beaconTargetId, mechanic, time)
    && beaconTargetId !== target.id) {
    const beaconIndex = next.findIndex((member) => member.id === beaconTargetId);
    const beacon = next[beaconIndex];
    if (beacon?.hp > 0) {
      const healedBeacon = healLiving(beacon, directHealing * echoFraction);
      beaconHealing = healedBeacon.hp - beacon.hp;
      next[beaconIndex] = healedBeacon;
    }
  }
  return { party: next, directHealing, beaconHealing };
}

export function placeOrRewindTimeAnchor(
  party: readonly PartyMember[],
  targetIndex: number,
  mechanic: HealerMechanicState,
  time: number,
  duration = 6,
): TimeAnchorResult {
  const target = party[targetIndex];
  if (!target || target.hp <= 0) return { party: [...party], mechanic, restored: 0, anchored: false };
  const anchor = mechanic.temporalAnchor;
  if (anchor?.targetId === target.id && anchor.expiresAt > time) {
    const restoredHp = Math.max(target.hp, Math.min(target.maxHp, anchor.hp));
    const next = [...party];
    next[targetIndex] = { ...target, hp: restoredHp };
    return {
      party: next,
      mechanic: { ...mechanic, temporalAnchor: null },
      restored: restoredHp - target.hp,
      anchored: false,
    };
  }
  return {
    party: [...party],
    mechanic: {
      ...mechanic,
      temporalAnchor: { targetId: target.id, hp: target.hp, expiresAt: time + duration },
    },
    restored: 0,
    anchored: true,
  };
}

export function startTimeLoop(party: readonly PartyMember[], mechanic: HealerMechanicState, restoresAt: number): HealerMechanicState {
  return {
    ...mechanic,
    timeLoop: {
      restoresAt,
      partyHp: Object.fromEntries(party.map((member) => [member.id, member.hp])) as Record<MemberId, number>,
    },
  };
}

export function resolveTimeLoop(
  party: PartyMember[],
  mechanic: HealerMechanicState,
  time: number,
): TimeLoopResult {
  const loop = mechanic.timeLoop;
  if (!loop || loop.restoresAt > time) return { party, mechanic, restored: 0 };
  let restored = 0;
  const next = party.map((member) => {
    if (member.hp <= 0) return member;
    const hp = Math.max(member.hp, Math.min(member.maxHp, loop.partyHp[member.id]));
    restored += hp - member.hp;
    return hp === member.hp ? member : { ...member, hp };
  });
  return { party: next, mechanic: { ...mechanic, timeLoop: null }, restored };
}

export function healMostInjured(party: readonly PartyMember[], amount: number): { party: PartyMember[]; targetId: MemberId | null; healing: number } {
  let targetIndex = -1;
  let lowestRatio = Number.POSITIVE_INFINITY;
  for (let index = 0; index < party.length; index += 1) {
    const member = party[index];
    if (member.hp <= 0 || member.hp >= member.maxHp) continue;
    const ratio = member.hp / member.maxHp;
    if (ratio < lowestRatio) {
      lowestRatio = ratio;
      targetIndex = index;
    }
  }
  if (targetIndex < 0) return { party: [...party], targetId: null, healing: 0 };
  const next = [...party];
  const before = next[targetIndex];
  next[targetIndex] = healLiving(before, amount);
  return { party: next, targetId: before.id, healing: next[targetIndex].hp - before.hp };
}

export function expireHealerMechanics(mechanic: HealerMechanicState, time: number): HealerMechanicState {
  let next = mechanic;
  if (next.beaconTargetId && next.beaconExpiresAt <= time) {
    next = { ...next, beaconTargetId: null, beaconExpiresAt: 0 };
  }
  if (next.temporalAnchor && next.temporalAnchor.expiresAt <= time) {
    next = { ...next, temporalAnchor: null };
  }
  return next;
}
