using System.Collections.Generic;
using System.Linq;
using System.Text;
using EDPlayerJournal.BGS;

namespace EliteBGS.LogGenerator;

public class MissionFormat : LogFormatter {
    private string GenerateFailedLog(Objective objective) {
        var missions = objective.EnabledOfType<MissionFailed>();

        if (missions.Count <= 0) {
            return "";
        }

        StringBuilder builder = new StringBuilder();

        var grouping = missions
                        .GroupBy(x => x.Mission.IsOnFoot)
                        ;

        foreach (var group in grouping) {
            int amount = group.Count();

            if (group.Key) {
                builder.AppendFormat("Failed {0} On Foot Mission(s)\n", amount);
            } else {
                builder.AppendFormat("Failed {0} Ship Mission(s)\n", amount);
            }
        }

        return builder.ToString().Trim();
    }

    private string GenerateFailedSummary(Objective objective) {
        var missions = objective.EnabledOfType<MissionFailed>();

        if (missions.Count <= 0) {
            return "";
        }

        StringBuilder sb = new();

        int onFootFails = missions.Where(x => x.Mission.IsOnFoot).Count();
        int shipFails = missions.Where(x => !x.Mission.IsOnFoot).Count();

        sb.Append("Fails: ");
        if (onFootFails > 0) {
            sb.AppendFormat("{0} Ground", onFootFails);
        }

        if (shipFails > 0) {
            if (onFootFails > 0) {
                sb.Append(", ");
            }
            sb.AppendFormat("{0} Ship", shipFails);
        }

        return sb.ToString();
    }

    public string GenerateLog(Objective objective) {
        Dictionary<string, Dictionary<string, int>> collated = new();
        Dictionary<string, ulong> passengers = new();
        StringBuilder output = new StringBuilder();
        long total_influence = 0;

        var missions = objective.EnabledOfType<MissionCompleted>();
        var support = objective.EnabledOfType<InfluenceSupport>();
        var failed = objective.EnabledOfType<MissionFailed>();

        if ((missions == null || missions.Count == 0) &&
            (support == null || support.Count == 0) &&
            (failed == null || failed.Count == 0)) {
            return "";
        }

        foreach (MissionCompleted m in missions) {
            if (!collated.ContainsKey(m.MissionName)) {
                collated[m.MissionName] = new Dictionary<string, int>();
            }
            if (!collated[m.MissionName].ContainsKey(m.Influence)) {
                collated[m.MissionName][m.Influence] = 0;
            }

            ++collated[m.MissionName][m.Influence];

            total_influence += m.Influence.Length;

            if (m.AcceptedEntry != null &&
                m.AcceptedEntry.Mission != null &&
                m.AcceptedEntry.Mission.IsPassengerMission) {
                if (!passengers.ContainsKey(m.MissionName)) {
                    passengers[m.MissionName] = 0;
                }
                passengers[m.MissionName] += (m.AcceptedEntry.Mission.PassengerCount ?? 0);
            }
        }

        foreach (var mission in collated) {
            output.AppendFormat("{0}: ", mission.Key);
            output.Append("(");
            foreach (var influence in mission.Value.OrderBy(x => x.Key.Length)) {
                output.AppendFormat("Inf{0} x{1}, ", influence.Key, influence.Value);
            }
            output.Remove(output.Length - 2, 2); // remove last ", "
            output.Append(")");

            if (passengers.ContainsKey(mission.Key)) {
                output.AppendFormat(" ({0} Passengers)", passengers[mission.Key]);
            }

            output.Append("\n");
        }

        output.Append("\n");

        // Handle failed missions, and add them to the log and influence tally
        string failedlog = GenerateFailedLog(objective);
        if (!string.IsNullOrEmpty(failedlog)) {
            output.Append(failedlog);
            output.Append("\n");
        }
        total_influence += failed.Sum(x => x.InfluenceAmount);

        foreach (InfluenceSupport inf in support) {
            output.Append(inf.ToString());
            output.Append("\n");
            total_influence += inf.Influence.InfluenceAmount;
        }

        if (support.Count() > 0) {
            output.Append("\n");
        }

        if (total_influence != 0) {
            output.AppendFormat("Total Influence: {0}", total_influence);
        }

        return output.ToString().Trim();
    }

    public string GenerateSummary(Objective objective) {
        long influence = objective
            .EnabledOfType<MissionCompleted>()
            .Sum(x => x.Influence.Length)
            ;
        long support = objective
            .EnabledOfType<InfluenceSupport>()
            .Sum(x => x.Influence.InfluenceAmount)
            ;
        long failed = objective
            .EnabledOfType<MissionFailed>()
            .Sum(x => x.InfluenceAmount)
            ;

        if (influence == 0 && support == 0 && failed == 0) {
            return "";
        }

        string failedsummary = GenerateFailedSummary(objective);
        string summary = string.Format("INF: {0}", influence + support + failed);

        if (!string.IsNullOrEmpty(failedsummary)) {
            string.Join("; ", summary, failedsummary);
        }

        return summary;
    }
}