import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { Augmentation } from "../Augmentation/Augmentation";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";

import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { Player } from "../Player";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Settings } from "../Settings/Settings";
import {
  getHackingWorkRepGain,
  getFactionSecurityWorkRepGain,
  getFactionFieldWorkRepGain,
} from "../PersonObjects/formulas/reputation";

import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal";
import { FactionNames } from "./data/FactionNames";
import { SFC32RNG } from "../Casino/RNG";

export function inviteToFaction(faction: Faction): void {
  Player.receiveInvite(faction.name);
  faction.alreadyInvited = true;
  if (!Settings.SuppressFactionInvites) {
    InvitationEvent.emit(faction);
  }
}

export function joinFaction(faction: Faction): void {
  if (faction.isMember) return;
  faction.isMember = true;
  Player.factions.push(faction.name);
  const allFactions = Object.values(FactionNames).map((faction) => faction as string);
  Player.factions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b));
  const factionInfo = faction.getInfo();

  //Determine what factions you are banned from now that you have joined this faction
  for (const enemy of factionInfo.enemies) {
    if (Factions[enemy] instanceof Faction) {
      Factions[enemy].isBanned = true;
    }
  }
  for (let i = 0; i < Player.factionInvitations.length; ++i) {
    if (Player.factionInvitations[i] == faction.name || Factions[Player.factionInvitations[i]].isBanned) {
      Player.factionInvitations.splice(i, 1);
      i--;
    }
  }
}

//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
export function hasAugmentationPrereqs(aug: Augmentation): boolean {
  return aug.prereqs.every((aug) => Player.hasAugmentation(aug));
}

export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
  const hasPrereqs = hasAugmentationPrereqs(aug);
  const augCosts = aug.getCost(Player);
  if (!hasPrereqs) {
    const txt = `You must first purchase or install ${aug.prereqs
      .filter((req) => !Player.hasAugmentation(req))
      .join(",")} before you can purchase this one.`;
    if (sing) {
      return txt;
    } else {
      dialogBoxCreate(txt);
    }
  } else if (augCosts.moneyCost !== 0 && Player.money < augCosts.moneyCost) {
    const txt = "You don't have enough money to purchase " + aug.name;
    if (sing) {
      return txt;
    }
    dialogBoxCreate(txt);
  } else if (fac.playerReputation < augCosts.repCost) {
    const txt = "You don't have enough faction reputation to purchase " + aug.name;
    if (sing) {
      return txt;
    }
    dialogBoxCreate(txt);
  } else if (augCosts.moneyCost === 0 || Player.money >= augCosts.moneyCost) {
    const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
    if (aug.name == AugmentationNames.NeuroFluxGovernor) {
      queuedAugmentation.level = aug.getLevel(Player);
    }
    Player.queuedAugmentations.push(queuedAugmentation);

    Player.loseMoney(augCosts.moneyCost, "augmentations");

    if (sing) {
      return "You purchased " + aug.name;
    } else if (!Settings.SuppressBuyAugmentationConfirmation) {
      dialogBoxCreate(
        "You purchased " +
          aug.name +
          ". Its enhancements will not take " +
          "effect until they are installed. To install your augmentations, go to the " +
          "'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
          "augmentations will now be more expensive.",
      );
    }
  } else {
    dialogBoxCreate(
      "Hmm, something went wrong when trying to purchase an Augmentation. " +
        "Please report this to the game developer with an explanation of how to " +
        "reproduce this.",
    );
  }
  return "";
}

export function processPassiveFactionRepGain(numCycles: number): void {
  for (const name of Object.keys(Factions)) {
    if (name === Player.currentWorkFactionName) continue;
    if (!Factions.hasOwnProperty(name)) continue;
    const faction = Factions[name];
    if (!faction.isMember) continue;
    // No passive rep for special factions
    const info = faction.getInfo();
    if (!info.offersWork()) continue;
    // No passive rep for gangs.
    if (Player.getGangName() === name) continue;
    // 0 favor = 1%/s
    // 50 favor = 6%/s
    // 100 favor = 11%/s
    const favorMult = Math.min(0.1, faction.favor / 1000 + 0.01);
    // Find the best of all possible favor gain, minimum 1 rep / 2 minute.
    const hRep = getHackingWorkRepGain(Player, faction);
    const sRep = getFactionSecurityWorkRepGain(Player, faction);
    const fRep = getFactionFieldWorkRepGain(Player, faction);
    const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1 / 120);

    faction.playerReputation += rate * numCycles * Player.faction_rep_mult * BitNodeMultipliers.FactionPassiveRepGain;
  }
}

export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Faction): string[] => {
  // If player has a gang with this faction, return (almost) all augmentations
  if (player.hasGangWith(faction.name)) {
    let augs = Object.values(StaticAugmentations);

    // Remove special augs
    augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationNames.CongruityImplant);

    if (player.bitNodeN === 2) {
      // TRP is not available outside of BN2 for Gangs
      augs.push(StaticAugmentations[AugmentationNames.TheRedPill]);
    }

    const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`);
    // Remove faction-unique augs that don't belong to this faction
    const uniqueFilter = (a: Augmentation): boolean => {
      // Keep all the non-unique one
      if (a.factions.length > 1) {
        return true;
      }
      // Keep all the ones that this faction has anyway.
      if (faction.augmentations.includes(a.name)) {
        return true;
      }

      return rng() >= 1 - BitNodeMultipliers.GangUniqueAugs;
    };
    augs = augs.filter(uniqueFilter);

    return augs.map((a) => a.name);
  }

  return faction.augmentations.slice();
};
