using EDPlayerJournal.Entries;

namespace EDPlayerJournal.BGS;

internal class TransactionParserContext {
    /// <summary>
    /// List of commander names seen in the logs. May be empty.
    /// </summary>
    public List<string> Commanders { get; } = new();

    /// <summary>
    /// Name of the current system the player is in.
    /// </summary>
    public string? CurrentSystem { get; set; }

    /// <summary>
    /// 64 bit address of the current system.
    /// </summary>
    public ulong? CurrentSystemAddress { get; set; }

    /// <summary>
    /// Controlling faction of the current system.
    /// </summary>
    public string? ControllingFaction { get; set; }

    /// <summary>
    /// Name of the current station the player is docked at.
    /// </summary>
    public string? CurrentStation { get; set; }

    /// <summary>
    /// Faction that owns the current station.
    /// </summary>
    public string? StationOwner { get; set; }

    /// <summary>
    /// Whether the player is currently on foot, or in an SRV/ship.
    /// </summary>
    public bool IsOnFoot { get; set; } = false;

    /// <summary>
    /// Type of the current instance after a SupercruiseDestinationDropEntry.
    /// This is null if there is no current instance, or the current instance
    /// is not known.
    /// </summary>
    public string? CurrentInstanceType { get; set; } = null;

    /// <summary>
    /// Last faction that awarded the player with combat bonds.
    /// </summary>
    public string? LastRecordedAwardingFaction { get; set; }

    /// <summary>
    /// Highest combat bond seen so far.
    /// </summary>
    public ulong? HighestCombatBond { get; set; }

    public bool HaveSeenCapShip { get; set; } = false;
    public bool HaveSeenAlliedCaptain { get; set; } = false;
    public bool HaveSeenEnemyCaptain { get; set; } = false;
    public bool HaveSeenSpecOps { get; set; } = false;
    public bool HaveSeenAlliedCorrespondent { get; set; } = false;
    public bool HaveSeenEnemyCorrespondent { get; set; } = false;

    /// <summary>
    /// Current Odyssey settlement.
    /// </summary>
    public string? Settlement { get; set; } = null;

    /// <summary>
    /// Returns true if the current session is legacy
    /// </summary>
    public bool IsLegacy { get; set; } = false;

    /// <summary>
    /// How many on foot kills were done.
    /// </summary>
    public ulong OnFootKills { get; set; } = 0;

    /// <summary>
    /// How many ship kills were done.
    /// </summary>
    public ulong ShipKills { get; set; } = 0;

    /// <summary>
    /// Thargoid scouts killed
    /// </summary>
    public ulong ThargoidScoutKills { get; set; } = 0;

    /// <summary>
    /// Thargoid interceptor kills
    /// </summary>
    public ulong ThargoidInterceptorKills { get; set; } = 0;

    /// <summary>
    /// Whether we have seen an AX warzone NPC talk to us with ReceiveText
    /// </summary>
    public bool HaveSeenAXWarzoneNPC { get; set; } = false;

    /// <summary>
    /// A list of accepted missions index by their mission ID
    /// </summary>
    public Dictionary<ulong, Mission> AcceptedMissions { get; } = new();
    public Dictionary<ulong, Location> AcceptedMissionLocation { get; } = new();
    /// <summary>
    /// A way to lookup a system by its system id
    /// </summary>
    public Dictionary<ulong, string> SystemsByID { get; } = new();
    /// <summary>
    /// A list of factions present in the given star system
    /// </summary>
    public Dictionary<string, List<Faction>> SystemFactions { get; } = new();
    /// <summary>
    /// To which faction a given named NPC belonged to.
    /// </summary>
    public Dictionary<string, string> NPCFaction { get; } = new();
    /// <summary>
    /// Buy costs for a given commodity
    /// </summary>
    public Dictionary<string, long> BuyCost = new();

    /// <summary>
    /// Called when the player leaves an instance, either through logout, FSD jump or
    /// supercruise instance.
    /// </summary>
    public void LeftInstance() {
        CurrentInstanceType = null;
        Settlement = null;
    }

    public void DiscernCombatZone(TransactionList transactions, Entry e) {
        string? grade = CombatZones.DifficultyLow;
        string cztype;
        ulong highest = HighestCombatBond ?? 0;
        string? faction = LastRecordedAwardingFaction;

        if (HighestCombatBond == null &&
            LastRecordedAwardingFaction == null &&
            HaveSeenAXWarzoneNPC == false &&
            CurrentInstanceType == null) {
            return;
        }

        if (OnFootKills > 0 || IsOnFoot == true) {
            cztype = CombatZones.GroundCombatZone;
            // High on foot combat zones have enforcers that bring 80k a pop
            if (highest >= 60000) {
                grade = CombatZones.DifficultyHigh;
            } else if (highest >= 30000) {
                // In medium conflict zones, the enforcers are worth 30k
                grade = CombatZones.DifficultyMedium;
            } else {
                grade = CombatZones.DifficultyLow;
            }
        } else if (CurrentInstanceType != null) {
            if (!Instances.IsWarzone(CurrentInstanceType)) {
                return;
            }
            if (LastRecordedAwardingFaction == null && 
                Instances.IsHumanWarzone(CurrentInstanceType)) {
                transactions.AddIncomplete(new CombatZone(), 
                    "Could not discern for whom you fought for, " +
                    "as it seems you haven't killed anyone in the ship combat zone.",
                    e);
                return;
            }
            // If we have information about the current instance being a warship use that
            // information to determine the warzone.
            if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneLow)) {
                cztype = CombatZones.ShipCombatZone;
                grade = CombatZones.DifficultyLow;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneMedium)) {
                cztype = CombatZones.ShipCombatZone;
                grade = CombatZones.DifficultyMedium;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneHigh)) {
                cztype = CombatZones.ShipCombatZone;
                grade = CombatZones.DifficultyHigh;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidLow)) {
                cztype = CombatZones.AXCombatZone;
                grade = CombatZones.DifficultyLow;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidMedium)) {
                cztype = CombatZones.AXCombatZone;
                grade = CombatZones.DifficultyMedium;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidHigh)) {
                cztype = CombatZones.AXCombatZone;
                grade = CombatZones.DifficultyHigh;
            } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidVeryHigh)) {
                cztype = CombatZones.AXCombatZone;
                grade = CombatZones.DifficultyVeryHigh;
            } else {
                transactions.AddIncomplete(new CombatZone(),
                    "Unknown conflict zone difficulty",
                    e);
                return;
            }
        } else if (ShipKills > 0 && !IsOnFoot) {
            // Ship combat zones can be identified by the amount of kills
            if (ShipKills > 20) {
                grade = CombatZones.DifficultyHigh;
            } else if (ShipKills > 10) {
                grade = CombatZones.DifficultyMedium;
            }

            // Cap ship, means a high conflict zone
            if (HaveSeenCapShip) {
                grade = CombatZones.DifficultyHigh;
            } else {
                int warzoneNpcs = new List<bool>() { 
                    HaveSeenEnemyCaptain, 
                    HaveSeenEnemyCorrespondent, 
                    HaveSeenSpecOps 
                }
                .Where(x => x == true)
                .Count()
                ;

                if (warzoneNpcs >= 1 && grade == CombatZones.DifficultyLow) {
                    grade = CombatZones.DifficultyMedium;
                }
            }
            cztype = CombatZones.ShipCombatZone;
        } else if ((ThargoidScoutKills > 0 && ThargoidInterceptorKills > 0) ||
                   HaveSeenAXWarzoneNPC == true) {
            // Could be a thargoid combat zones if interceptors and scouts are there
            cztype = CombatZones.AXCombatZone;
            // Still unknown
            grade = null;
        } else {
            transactions.AddIncomplete(new CombatZone(), "Failed to discern combat zone type", e);
            return;
        }

        CombatZone zone = new CombatZone() {
            System = CurrentSystem,
            Faction = faction,
            IsLegacy = IsLegacy,
            Settlement = Settlement,
            Grade = grade,
            Type = cztype,
            // Sad truth is, if HaveSeenXXX is false, we just don't know for certain
            CapitalShip = HaveSeenCapShip ? true : null,
            SpecOps = HaveSeenSpecOps ? true : null,
            EnemyCorrespondent = HaveSeenEnemyCorrespondent ? true : null,
            AlliedCorrespondent = HaveSeenAlliedCorrespondent ? true : null,
            EnemyCaptain = HaveSeenEnemyCaptain ? true : null,
            AlliedCaptain = HaveSeenAlliedCaptain ? true : null,
        };
        zone.Entries.Add(e);
        transactions.Add(zone);
    }

    public void RecordCombatBond(FactionKillBondEntry e) {
        if (HighestCombatBond == null || e.Reward > HighestCombatBond) {
            HighestCombatBond = e.Reward;
        }

        LastRecordedAwardingFaction = e.AwardingFaction;

        if (IsOnFoot) {
            ++OnFootKills;
        } else {
            ++ShipKills;
        }
    }

    public void ResetCombatZone() {
        HighestCombatBond = null;
        HaveSeenCapShip = false;
        HaveSeenAlliedCaptain = false;
        HaveSeenEnemyCaptain = false;
        HaveSeenAlliedCorrespondent = false;
        HaveSeenEnemyCorrespondent = false;
        HaveSeenSpecOps = false;
        LastRecordedAwardingFaction = null;
        OnFootKills = 0;
        ShipKills = 0;
        ThargoidInterceptorKills = 0;
        ThargoidScoutKills = 0;
        HaveSeenAXWarzoneNPC = false;
    }

    public void BoughtCargo(string? cargo, long? cost) {
        if (cargo == null || cost == null) {
            return;
        }

        BuyCost[cargo] = cost.Value;
    }

    public List<Faction>? GetFactions(string? system) {
        if (system == null || !SystemFactions.ContainsKey(system)) {
            return null;
        }

        return SystemFactions[system];
    }

    public void MissionAccepted(MissionAcceptedEntry? entry) {
        if (entry == null) {
            return;
        }

        MissionAccepted(entry.Mission);
    }

    public void MissionAccepted(Mission? mission) {
        if (CurrentSystem == null || CurrentSystemAddress == null) {
            throw new Exception("Mission accepted without knowing where.");
        }

        if (mission == null) {
            throw new Exception("Mission is null");
        }

        AcceptedMissions.TryAdd(mission.MissionID, mission);

        Location location = new() {
            StarSystem = CurrentSystem,
            SystemAddress = CurrentSystemAddress.Value,
            Station = (CurrentStation ?? ""),
        };

        AcceptedMissionLocation.TryAdd(mission.MissionID, location);
    }
}
