using RpgRoller.Contracts;
using RpgRoller.Domain;

namespace RpgRoller.Services;

public static class CampaignLogSummaryBuilder
{
    public static string BuildCompactLogSummary(IReadOnlyList<RollDieResult> dice, string? breakdown = null)
    {
        if (dice.Count == 0)
            return "No detail available.";

        if (dice.Any(die => IsRolemasterDieKind(die.Kind)))
            return BuildRolemasterCompactLogSummary(dice, breakdown);

        return string.Join(", ", dice.Select(die => die.Roll.ToString()));
    }

    public static string[]? BuildCompactLogEventBadges(RulesetKind ruleset, string? expression, IReadOnlyList<RollDieResult> dice, string? breakdown = null)
    {
        var badges = new List<string>();

        switch (ruleset)
        {
            case RulesetKind.D6:
                AddBadgeIfMissing(badges, dice.Any(die => die is { Wild: true, Roll: 6 }), "w6");
                AddBadgeIfMissing(badges, dice.Any(die => die is { Wild: true, Roll: 1 }), "w1");
                break;
            case RulesetKind.Dnd5e:
                if (!string.IsNullOrWhiteSpace(expression) && IsSingleD20Expression(expression))
                {
                    AddBadgeIfMissing(badges, dice.Any(die => die.Roll == 20), "n20");
                    AddBadgeIfMissing(badges, dice.Any(die => die.Roll == 1), "n1");
                }

                break;
            case RulesetKind.Rolemaster:
                AddBadgeIfMissing(badges, dice.Any(die => string.Equals(die.Kind, RollDieKinds.RolemasterOpenEndedInitial, StringComparison.Ordinal) && !die.SignedContribution.HasValue), "rf");
                AddBadgeIfMissing(badges, dice.Any(die => die.Roll == 100), "r100");
                AddBadgeIfMissing(badges, dice.Any(die => die.Roll == 66), "r66");
                AddRetryBadgeIfPresent(badges, breakdown);
                break;
        }

        return badges.Count == 0 ? null : badges.ToArray();
    }

    public static string? ExtractCustomRollExpression(string breakdown, string separator)
    {
        var separatorIndex = breakdown.IndexOf(separator, StringComparison.Ordinal);
        if (separatorIndex <= 0)
            return null;

        return breakdown[..separatorIndex];
    }

    private static string BuildRolemasterCompactLogSummary(IReadOnlyList<RollDieResult> dice, string? breakdown)
    {
        var retryBonus = RolemasterRetryPolicy.TryExtractRetryBonus(breakdown);
        var summaryDice = retryBonus.HasValue ? dice.Where(die => die.Attempt != 2).ToArray() : dice;
        var openEndedInitial = summaryDice.FirstOrDefault(die => string.Equals(die.Kind, RollDieKinds.RolemasterOpenEndedInitial, StringComparison.Ordinal));
        if (openEndedInitial is not null)
        {
            var highFollowUps = summaryDice.Where(die => string.Equals(die.Kind, RollDieKinds.RolemasterOpenEndedHigh, StringComparison.Ordinal)).Select(die => die.Roll.ToString()).ToArray();
            if (highFollowUps.Length > 0)
                return AppendRetryNote($"{openEndedInitial.Roll} + {string.Join(" + ", highFollowUps)} | open-ended high", retryBonus);

            var lowFollowUps = summaryDice.Where(die => string.Equals(die.Kind, RollDieKinds.RolemasterOpenEndedLowSubtract, StringComparison.Ordinal)).Select(die => die.Roll.ToString()).ToArray();
            if (lowFollowUps.Length > 0)
                return AppendRetryNote($"({RollBreakdownFormatter.FormatRolemasterTriggerRoll(openEndedInitial.Roll)}) {string.Join(" ", lowFollowUps.Select(roll => $"-{roll}"))} | open-ended low", retryBonus);

            return AppendRetryNote($"{openEndedInitial.Roll} | open-ended", retryBonus);
        }

        if (summaryDice.Any(die => string.Equals(die.Kind, RollDieKinds.RolemasterStandard, StringComparison.Ordinal)))
        {
            var preview = string.Join(" + ", summaryDice.Select(die => die.Roll.ToString()));
            return AppendRetryNote($"{preview} | rolemaster", retryBonus);
        }

        return AppendRetryNote(string.Join(", ", summaryDice.Select(die => die.Roll.ToString())), retryBonus);
    }

    private static bool IsRolemasterDieKind(string? kind)
    {
        return kind is RollDieKinds.RolemasterStandard or RollDieKinds.RolemasterOpenEndedInitial or RollDieKinds.RolemasterOpenEndedHigh or RollDieKinds.RolemasterOpenEndedLowSubtract;
    }

    private static void AddBadgeIfMissing(List<string> badges, bool condition, string code)
    {
        if (!condition || badges.Any(badge => string.Equals(badge, code, StringComparison.Ordinal)))
            return;

        badges.Add(code);
    }

    private static void AddRetryBadgeIfPresent(List<string> badges, string? breakdown)
    {
        var retryBonus = RolemasterRetryPolicy.TryExtractRetryBonus(breakdown);
        if (!retryBonus.HasValue)
            return;

        AddBadgeIfMissing(badges, true, retryBonus.Value == 5 ? "rs5" : "rs10");
    }

    private static string AppendRetryNote(string summary, int? retryBonus)
    {
        return retryBonus.HasValue ? $"{summary} | retry +{retryBonus.Value}" : summary;
    }

    private static bool IsSingleD20Expression(string expression)
    {
        var parsedExpression = DiceRules.ParseExpression(RulesetKind.Dnd5e, expression);
        return parsedExpression.Succeeded && parsedExpression.Value!.DiceCount == 1 && parsedExpression.Value.Sides == 20;
    }
}