using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using YachtDice.Modifiers.Core;
using YachtDice.Modifiers.Definition;
using YachtDice.Modifiers.Runtime;

namespace YachtDice.Modifiers.Pipeline
{
    public class ModifierPipeline
    {
        private readonly ModifierRegistry registry;
        private readonly List<EffectEntry> effectBuffer = new();
        private bool isExecuting;
        private readonly Queue<(TriggerType trigger, ModifierContext context)> deferredQueue = new();

        private const int MaxRecursionDepth = 1;
        private int currentDepth;

        public bool TracingEnabled { get; set; }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
            = true;
#endif

        public ModifierPipeline(ModifierRegistry registry)
        {
            this.registry = registry;
        }

        public async UniTask<ModifierContext> Execute(TriggerType trigger, ModifierContext context)
        {
            if (isExecuting)
            {
                if (currentDepth >= MaxRecursionDepth)
                {
                    Debug.LogWarning($"[ModifierPipeline] Max recursion depth reached for trigger {trigger}. Dropping.");
                    return context;
                }

                currentDepth++;
            }

            isExecuting = true;
            context.Trigger = trigger;

            PipelineTrace trace = null;
            if (TracingEnabled)
            {
                trace = new PipelineTrace { Trigger = trigger };
                context.DebugLog ??= new List<string>();
            }

            effectBuffer.Clear();

            // Snapshot active modifiers to avoid modification during iteration
            var activeSnapshot = registry.Active;

            // Gather eligible effects
            for (int i = 0; i < activeSnapshot.Count; i++)
            {
                var inst = activeSnapshot[i];
                var behaviors = inst.Definition.Behaviors;

                for (int b = 0; b < behaviors.Count; b++)
                {
                    var behavior = behaviors[b];
                    if (behavior == null) continue;
                    if (behavior.Trigger != trigger) continue;

                    // Evaluate conditions with tracing
                    bool passed = true;
                    string failedCondition = null;

                    var conditions = behavior.Conditions;
                    for (int c = 0; c < conditions.Count; c++)
                    {
                        if (conditions[c] == null) continue;
                        if (!conditions[c].Evaluate(context, inst))
                        {
                            passed = false;
                            failedCondition = conditions[c].name;
                            break;
                        }
                    }

                    if (trace != null)
                        trace.AddConditionResult(inst.Definition.Id, behavior.name, passed, failedCondition);

                    if (!passed) continue;

                    // Collect effects
                    var effects = behavior.Effects;
                    for (int e = 0; e < effects.Count; e++)
                    {
                        if (effects[e] == null) continue;
                        effectBuffer.Add(new EffectEntry
                        {
                            Effect = effects[e],
                            Instance = inst,
                        });
                    }
                }
            }

            // Sort by Phase -> Priority -> Id
            effectBuffer.Sort(ModifierComparer.Default);

            // Execute sequentially
            for (int i = 0; i < effectBuffer.Count; i++)
            {
                var entry = effectBuffer[i];
                await entry.Effect.Apply(context, entry.Instance);

                if (trace != null)
                    trace.AddEffectApplied(entry.Instance.Definition.Id, entry.Effect.name, entry.Effect.Phase);
            }

            if (trace != null)
            {
                string traceStr = trace.ToString();
                context.DebugLog.Add(traceStr);
                Debug.Log(traceStr);
            }

            isExecuting = false;
            currentDepth = 0;

            return context;
        }
    }
}
