using System.Collections.Generic;

namespace FishDice.LuckyDice
{
    public sealed class LuckyDiceDefaultConfig
    {
        private readonly Dictionary<string, DiceSetConfig> _diceSets;

        private LuckyDiceDefaultConfig(
            Dictionary<string, DiceSetConfig> diceSets,
            IReadOnlyList<ComboRule> comboRules,
            IReadOnlyList<LuckyDiceCandidateConfig> luckyDiceCandidates,
            IReadOnlyList<TargetModeConfig> targetModeDefinitions)
        {
            _diceSets = diceSets;
            ComboRules = comboRules;
            LuckyDiceCandidates = luckyDiceCandidates;
            TargetModeDefinitions = targetModeDefinitions;
        }

        public IReadOnlyList<ComboRule> ComboRules { get; }

        public IReadOnlyList<LuckyDiceCandidateConfig> LuckyDiceCandidates { get; }

        public IReadOnlyList<TargetModeConfig> TargetModeDefinitions { get; }

        public DiceSetConfig GetDiceSet(string diceSetId)
        {
            DiceSetConfig diceSet;
            if (!_diceSets.TryGetValue(diceSetId, out diceSet))
            {
                throw new InvalidDiceSetException(diceSetId);
            }

            return diceSet;
        }

        public static LuckyDiceDefaultConfig Create()
        {
            return CreateValidated(LuckyDiceConfigValidationContext.CreateDefault());
        }

        public static LuckyDiceDefaultConfig CreateValidated(LuckyDiceConfigValidationContext validationContext)
        {
            return CreateValidated(
                DefaultComboRuleDefinitions,
                DefaultLuckyDiceCandidateDefinitions,
                DefaultTargetModeDefinitions,
                validationContext);
        }

        public static LuckyDiceDefaultConfig CreateValidated(
            IReadOnlyList<ComboRuleDefinition> ruleDefinitions,
            IReadOnlyList<LuckyDiceCandidateDefinition> candidateDefinitions,
            IReadOnlyList<TargetModeDefinition> targetModeDefinitions)
        {
            return CreateValidated(
                ruleDefinitions,
                candidateDefinitions,
                targetModeDefinitions,
                LuckyDiceConfigValidationContext.CreateDefault());
        }

        public static LuckyDiceDefaultConfig CreateValidated(
            IReadOnlyList<ComboRuleDefinition> ruleDefinitions,
            IReadOnlyList<LuckyDiceCandidateDefinition> candidateDefinitions,
            IReadOnlyList<TargetModeDefinition> targetModeDefinitions,
            LuckyDiceConfigValidationContext validationContext)
        {
            validationContext = validationContext ?? LuckyDiceConfigValidationContext.CreateDefault();
            var validation = LuckyDiceConfigValidator.Validate(
                ruleDefinitions,
                candidateDefinitions,
                targetModeDefinitions,
                validationContext.Matchers,
                validationContext.ActionExecutors);
            if (!validation.IsValid)
            {
                throw new InvalidLuckyDiceConfigException(validation);
            }

            var normalSet = new DiceSetConfig(
                LuckyDiceKeys.NormalDiceSetId,
                DiceSetType.Normal,
                diceCount: 2,
                faces: new[]
                {
                    DiceFace.NormalNumber(2),
                    DiceFace.NormalNumber(3),
                    DiceFace.NormalNumber(4),
                    DiceFace.NormalNumber(5),
                    DiceFace.NormalNumber(6),
                    DiceFace.NormalClover()
                },
                defaultResultSlotCount: null);

            var luckySet = new DiceSetConfig(
                LuckyDiceKeys.LuckyDiceSetId,
                DiceSetType.Lucky,
                diceCount: 1,
                faces: new[]
                {
                    DiceFace.LuckyResult(LuckyDiceKeys.RocketResultKey),
                    DiceFace.LuckyResult(LuckyDiceKeys.ThiefResultKey)
                },
                defaultResultSlotCount: 3);

            return new LuckyDiceDefaultConfig(
                new Dictionary<string, DiceSetConfig>
                {
                    { normalSet.DiceSetId, normalSet },
                    { luckySet.DiceSetId, luckySet }
                },
                ComboRuleDefinitionLoader.Load(ruleDefinitions),
                ToCandidateConfigs(candidateDefinitions),
                ToTargetModeConfigs(targetModeDefinitions));
        }

        private static LuckyDiceCandidateConfig[] ToCandidateConfigs(IReadOnlyList<LuckyDiceCandidateDefinition> definitions)
        {
            var result = new LuckyDiceCandidateConfig[definitions.Count];
            for (var i = 0; i < definitions.Count; i++)
            {
                var definition = definitions[i];
                result[i] = new LuckyDiceCandidateConfig(
                    definition.ResultKey,
                    definition.TargetKey,
                    definition.ResultSlotCount,
                    definition.Weight,
                    definition.Enabled,
                    definition.MinLevel,
                    definition.SourceFilter,
                    definition.IsDefault);
            }

            return result;
        }

        private static TargetModeConfig[] ToTargetModeConfigs(IReadOnlyList<TargetModeDefinition> definitions)
        {
            var result = new TargetModeConfig[definitions.Count];
            for (var i = 0; i < definitions.Count; i++)
            {
                var definition = definitions[i];
                result[i] = new TargetModeConfig(definition.ResultKey, definition.TargetKey, definition.ModeKey);
            }

            return result;
        }

        private static readonly ComboRuleDefinition[] DefaultComboRuleDefinitions =
        {
            new ComboRuleDefinition
            {
                RuleId = "normal_clover_clover_lucky_dice",
                Priority = 100,
                MatcherType = LuckyDiceKeys.ExactComboMatcher,
                ComboKey = "clover_clover",
                Actions = new[]
                {
                    new RollActionDefinition { ActionType = LuckyDiceKeys.TriggerLuckyDiceAction }
                }
            },
            new ComboRuleDefinition
            {
                RuleId = "normal_number_clover_bonus",
                Priority = 50,
                MatcherType = LuckyDiceKeys.ContainsFaceMatcher,
                FaceKey = LuckyDiceKeys.CloverFaceKey,
                RequiredCount = 1,
                Actions = new[]
                {
                    new RollActionDefinition { ActionType = LuckyDiceKeys.GrantRewardAction },
                    new RollActionDefinition
                    {
                        ActionType = LuckyDiceKeys.AddMultiplierAction,
                        Params = new[]
                        {
                            new ActionParamDefinition { Key = "reason", Value = "clover_bonus" }
                        }
                    }
                }
            },
            new ComboRuleDefinition
            {
                RuleId = "normal_all_numbers_reward",
                Priority = 10,
                MatcherType = LuckyDiceKeys.AllNumbersMatcher,
                Actions = new[]
                {
                    new RollActionDefinition { ActionType = LuckyDiceKeys.GrantRewardAction }
                }
            }
        };

        private static readonly LuckyDiceCandidateDefinition[] DefaultLuckyDiceCandidateDefinitions =
        {
            new LuckyDiceCandidateDefinition
            {
                ResultKey = LuckyDiceKeys.RocketResultKey,
                TargetKey = "slap_down",
                ResultSlotCount = 3,
                Weight = 1,
                Enabled = true,
                MinLevel = 0,
                SourceFilter = new[] { LuckyDiceKeys.NormalRollTriggerSource },
                IsDefault = true
            },
            new LuckyDiceCandidateDefinition
            {
                ResultKey = LuckyDiceKeys.ThiefResultKey,
                TargetKey = "treasure_heist",
                ResultSlotCount = 3,
                Weight = 1,
                Enabled = true,
                MinLevel = 0,
                SourceFilter = new[] { LuckyDiceKeys.NormalRollTriggerSource },
                IsDefault = false
            }
        };

        private static readonly TargetModeDefinition[] DefaultTargetModeDefinitions =
        {
            new TargetModeDefinition
            {
                ResultKey = LuckyDiceKeys.RocketResultKey,
                TargetKey = "slap_down",
                ModeKey = "slap_down_normal"
            },
            new TargetModeDefinition
            {
                ResultKey = LuckyDiceKeys.ThiefResultKey,
                TargetKey = "treasure_heist",
                ModeKey = "treasure_heist_normal"
            }
        };
    }
}
