using System;
using System.Collections.Generic;
using System.Linq;
using FishDice.LuckyDice;

namespace FishDice.Demo
{
    public static class LuckyDiceDemoRunner
    {
        public static LuckyDiceDemoSnapshot RunScenario(LuckyDiceDemoScenario scenario)
        {
            if (scenario == null)
            {
                throw new ArgumentNullException(nameof(scenario));
            }

            return Run(scenario, new ScriptedRandomSource(scenario.RandomValues));
        }

        public static LuckyDiceDemoSnapshot RunRandom()
        {
            var scenario = new LuckyDiceDemoScenario(
                "Random Roll",
                "Uses the default FishDice runtime config and System.Random.");

            return Run(scenario, new SystemRandomSource());
        }

        public static LuckyDiceDemoSnapshot RunRandom(IRandomSource random)
        {
            var scenario = new LuckyDiceDemoScenario(
                "Random Roll",
                "Uses the default FishDice runtime config and the supplied random source.");

            return Run(scenario, random);
        }

        private static LuckyDiceDemoSnapshot Run(LuckyDiceDemoScenario scenario, IRandomSource random)
        {
            var flow = LuckyDiceRuntimeFactory.CreateDefault(random).CreateDefaultFlow();
            var result = flow.Execute(new RollFlowRequest(
                $"demo-{DateTime.UtcNow:yyyyMMddHHmmssfff}",
                LuckyDiceKeys.NormalDiceSetId,
                LuckyDiceKeys.NormalRollTriggerSource,
                playerLevel: 0,
                isTutorial: false));

            var faceLine = "Faces: " + string.Join(" + ", result.RollResult.Faces.Select(face => face.Key));
            var comboLine = "Combo: " + result.ComboFacts.NormalizedKey;
            var ruleLine = "Rules: " + JoinOrDash(result.MatchedRules.Select(rule => rule.RuleId));
            var effectLine = "Effects: " + JoinOrDash(result.Effects.Select(effect => effect.EffectType));
            var luckyDiceLine = FormatLuckyDice(result.LuckyDiceResult);
            var targetModeLine = "Target Mode: " + (result.TargetMode == null ? "-" : result.TargetMode.ModeKey);
            var summary = FormatSummary(result);
            var normalFaces = result.RollResult.Faces.Select(face => face.Key).ToArray();
            var luckyDiceSlots = result.LuckyDiceResult == null || result.LuckyDiceResult.SlotResultKeys == null
                ? new string[0]
                : result.LuckyDiceResult.SlotResultKeys.ToArray();
            var outcomeKind = ResolveOutcomeKind(result);

            return new LuckyDiceDemoSnapshot(
                scenario.Title,
                scenario.Description,
                faceLine,
                comboLine,
                ruleLine,
                effectLine,
                luckyDiceLine,
                targetModeLine,
                summary,
                normalFaces,
                luckyDiceSlots,
                outcomeKind);
        }

        private static string FormatLuckyDice(LuckyDiceSelectionResult result)
        {
            if (result == null || result.Selected == null)
            {
                return "Lucky Dice: -";
            }

            return $"Lucky Dice: {result.Selected.ResultKey} x{result.SlotCount}";
        }

        private static string FormatSummary(RollFlowResult result)
        {
            if (result.TargetMode != null)
            {
                return $"Entered {result.TargetMode.TargetKey} via {result.TargetMode.ModeKey}.";
            }

            if (result.Effects.OfType<MultiplierEffect>().Any())
            {
                return "Granted reward with Clover bonus multiplier.";
            }

            if (result.Effects.OfType<RewardEffect>().Any())
            {
                return "Granted normal reward.";
            }

            return "No configured outcome.";
        }

        private static string ResolveOutcomeKind(RollFlowResult result)
        {
            if (result.TargetMode != null)
            {
                return "lucky_dice";
            }

            if (result.Effects.OfType<MultiplierEffect>().Any())
            {
                return "clover_bonus";
            }

            if (result.Effects.OfType<RewardEffect>().Any())
            {
                return "normal_reward";
            }

            return "none";
        }

        private static string JoinOrDash(IEnumerable<string> values)
        {
            var materialized = values.Where(value => !string.IsNullOrEmpty(value)).ToArray();
            return materialized.Length == 0 ? "-" : string.Join(", ", materialized);
        }
    }
}
