using System;
using System.Collections.Generic;
using System.Linq;

namespace FishDice.LuckyDice
{
    public sealed class LuckyDiceFlowService
    {
        private readonly IRandomSource _random;

        public LuckyDiceFlowService(IRandomSource random)
        {
            _random = random ?? throw new ArgumentNullException(nameof(random));
        }

        public LuckyDiceSelectionResult Select(
            IReadOnlyList<LuckyDiceCandidateConfig> candidates,
            RollFlowRequest request,
            DiceSetConfig luckyDiceSetConfig)
        {
            var source = candidates ?? Array.Empty<LuckyDiceCandidateConfig>();
            var filtered = source
                .Where(candidate => candidate.Enabled)
                .Where(candidate => request.PlayerLevel >= candidate.MinLevel)
                .Where(candidate => candidate.SourceFilter == null ||
                    candidate.SourceFilter.Count == 0 ||
                    candidate.SourceFilter.Contains(request.TriggerSource))
                .ToArray();

            var fallbackType = "None";
            int? randomValue = null;
            var selected = PickWeighted(filtered, out randomValue);

            if (selected == null)
            {
                selected = filtered.FirstOrDefault(candidate => candidate.IsDefault);
                fallbackType = selected == null ? "NormalRewardFallback" : "DefaultResult";
            }

            var slotCount = selected == null ? 0 : selected.ResultSlotCount ?? luckyDiceSetConfig.DefaultResultSlotCount ?? 3;

            return new LuckyDiceSelectionResult(
                selected,
                source.Count,
                filtered.Length,
                randomValue,
                fallbackType,
                slotCount);
        }

        private LuckyDiceCandidateConfig PickWeighted(IReadOnlyList<LuckyDiceCandidateConfig> candidates, out int? randomValue)
        {
            var weighted = candidates.Where(candidate => candidate.Weight > 0).ToArray();
            var totalWeight = weighted.Sum(candidate => candidate.Weight);
            if (totalWeight <= 0)
            {
                randomValue = null;
                return null;
            }

            var roll = _random.Next(0, totalWeight);
            randomValue = roll;
            var cursor = 0;
            foreach (var candidate in weighted)
            {
                cursor += candidate.Weight;
                if (roll < cursor)
                {
                    return candidate;
                }
            }

            return weighted[weighted.Length - 1];
        }
    }
}
