using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using EliteBGS.LogGenerator;
using System.Reflection;

namespace EliteBGS;

public class DiscordLogGenerator {
    protected List<LogFormatter> formatters = new List<LogFormatter>() {
        new MissionFormat(),
        new MurderFormat(),
        new VoucherFormat(),
        new ThargoidFormatter(),
        new CombatZoneFormat(),
        new KillBondsFormat(),
        new CartographicsFormat(),
        new MicroResourcesFormat(),
        new MarketBuyFormat(),
        new CargoSoldFormatter(),
        new VistaGenomicsFormat(),
        new SearchAndRescueFormat(),
    };

    protected virtual string GetToolVersion() {
        Version v = Assembly.GetCallingAssembly().GetName().Version;
        string ver;
        if (v == null) {
            ver = "v?.?.?";
        } else {
            ver = "v" + v.ToString(3);
        }
        return string.Format("EliteBGS {0}", ver);
    }

    protected virtual DateTime? GetDateOfEarliestEntry(Objective objective) {
        var it = objective
            .Transactions
            .OrderBy(x => x.CompletedAtDateTime)
            .FirstOrDefault()
            ;
        if (it != null) {
            return it.CompletedAtDateTime;
        }
        return null;
    }

    protected virtual DateTime? GetDateOfLatestEntry(Objective objective) {
        var it = objective
            .Transactions
            .OrderByDescending(x => x.CompletedAtDateTime)
            .FirstOrDefault()
            ;
        if (it != null) {
            return it.CompletedAtDateTime;
        }
        return null;
    }

    protected virtual string GenerateSummary(Objective objective) {
        StringBuilder sb = new StringBuilder();

        foreach (var formatter in formatters) {
            string summary = "";

            try {
                summary = formatter.GenerateSummary(objective);
            } catch (NotImplementedException) {
            }

            if (string.IsNullOrEmpty(summary)) {
                continue;
            }

            if (sb.Length > 0) {
                sb.Append("; ");
            }

            sb.Append(summary);
        }

        return sb.ToString();
    }

    protected virtual string GenerateHeader() {
        return "";
    }

    protected virtual string GenerateFooter() {
        return "\n";
    }

    protected virtual string GenerateObjectiveHeader(Objective objective) {
        StringBuilder log = new StringBuilder();

        string location;

        if (!string.IsNullOrEmpty(objective.System) && !string.IsNullOrEmpty(objective.Faction)) {
            location = string.Format("{0}, {1}", objective.System, objective.Faction);
        } else if (!string.IsNullOrEmpty(objective.System)) {
            location = objective.System;
        } else {
            location = "Unknown Location";
        }

        int legacycount = objective.Transactions
                .Where(x => x.IsLegacy)
                .Count()
                ;

        string summary = GenerateSummary(objective);

        var earliest = GetDateOfEarliestEntry(objective);
        var latest = GetDateOfLatestEntry(objective);
        if (earliest != null && latest != null) {
            log.AppendFormat("**Date:** {0} - {1}\n",
                GetDateOfEarliestEntry(objective),
                GetDateOfLatestEntry(objective)
                );
        }
        log.AppendFormat("**Target:** {0}\n", location);
        if (!string.IsNullOrEmpty(summary)) {
            log.AppendFormat("**Summary:** {0}\n", summary);
        }
        if (legacycount > 0) {
            log.AppendFormat("**Warning:** Some actions were performed on ED Legacy\n");
        }
        log.AppendLine("```");

        return log.ToString();
    }

    protected virtual string GenerateObjectiveFooter(Objective objective) {
        return "```\n";
    }

    /// <summary>
    /// Is called to do final adjustments to the log body of a given objective.
    /// </summary>
    /// <param name="objective">Objective in question.</param>
    /// <param name="log">Final log as generated.</param>
    /// <returns>The transformed log.</returns>
    protected virtual string TransformFinalLogForObjective(Objective objective, string log) {
        return log;
    }

    public virtual string Name {
        get { return "GenericLog"; }
    }

    protected virtual string BotHeader() {
        var sb = new StringBuilder();
        sb.AppendFormat("**Bot-Header:** {0}; {1}\n", GetToolVersion(), this.Name);
        return sb.ToString();
    }

    public virtual string GenerateDiscordLog(Report report) {
        StringBuilder log = new StringBuilder();

        if (report == null) {
            return "";
        }

        var objectives = report.Objectives
            .Where(x => x.IsEnabled && x.Transactions.Count() > 0)
            ;

        if (objectives == null || objectives.Count() <= 0) {
            return "";
        }

        log.AppendFormat("{0}", BotHeader());
        log.AppendFormat("{0}", GenerateHeader());

        foreach (Objective objective in objectives) {
            StringBuilder objlog = new StringBuilder();

            log.AppendFormat("{0}", GenerateObjectiveHeader(objective));

            foreach (LogFormatter formatter in formatters) {
                string text = formatter.GenerateLog(objective);
                text = text.Trim();
                if (!string.IsNullOrEmpty(text)) {
                    objlog.AppendFormat("{0}\n\n", text);
                }
            }

            string finallog = objlog.ToString().Trim();
            finallog = TransformFinalLogForObjective(objective, finallog);

            log.AppendFormat("{0}\n", finallog);

            log.AppendFormat("{0}", GenerateObjectiveFooter(objective));
        }

        log.AppendFormat("{0}", GenerateFooter());

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

    public virtual string[] SplitLog(string log, int maxcount = 2000) {
        throw new NotImplementedException();
    }

    protected string[] SplitLogWithHeader(string log, string header, int maxcount = 2000) {
        string[] lines = log.Split("\n");
        List<string> chunks = new();
        string chunk = string.Empty;

        // Optimisation
        if (log.Length <= maxcount) {
            return new string[] { log };
        }

        // First split the log into its headers
        // skip first bot header line
        for (int i = 1; i < lines.Length; i++) {
            string line = lines[i];

            if (line.StartsWith(header) && !string.IsNullOrEmpty(chunk)) {
                chunks.Add(chunk.Trim());
                chunk = string.Empty;
            }

            chunk = chunk + "\n" + line;
        }

        int curchunk = 0;
        string botheader = BotHeader().Trim() + "\n";
        // Leave room for botheader and some leeway
        int maxlength = (2000 - botheader.Length - 10);

        // Then try to collate chunks
        for (curchunk = 0; curchunk < chunks.Count; ++curchunk) {
            int count = chunks[curchunk].Length;
            while (count < maxlength && (curchunk+1) < chunks.Count) {
                count += chunks[curchunk + 1].Length + 2;
                if (count < maxlength) {
                    chunks[curchunk] += "\n";
                    chunks[curchunk] += chunks[curchunk + 1];
                    chunks.RemoveAt(curchunk + 1);
                }
            }
        }

        // Readd bott headers
        for (int i = 0; i < chunks.Count; i++) {
            chunks[i] = chunks[i].Insert(0, botheader);
        }

        // Now finally check if any one chunk is bigger than max length
        for (int i = 0; i < chunks.Count; i++) {
            if (chunks[i].Length > maxcount) {
                throw new ApplicationException(
                    string.Format("a chunk turned out bigger than {0}", maxcount));
            }
        }

        return chunks.ToArray();
    }
}
