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

namespace BingoBrain.Core
{
    public class UFdsa : MonoBehaviour
    {
        private class TimerTask
        {
            private UFdsa _mUFdsa;

            private string name;
            private TimerMode mode;
            private float startTime;
            private float duration;

            private bool isBreak = false;
            private float breakStart;
            private float breakDuration = 0;

            private Action timerEvent;
            private Action<object[]> timerArgsEvent;
            private object[] args = null;

            public float TimeLeft => Mathf.Max(0f, duration - (_mUFdsa.GetTime() - startTime) + breakDuration);

            public TimerTask(UFdsa uFdsa, string name, TimerMode mode, float startTime, float duration,
                Action handler)
            {
                this._mUFdsa = uFdsa;

                this.name = name;
                this.mode = mode;
                this.startTime = startTime;
                this.duration = duration;
                timerEvent = handler;
            }

            public TimerTask(UFdsa uFdsa, string name, TimerMode mode, float startTime, float duration,
                Action<object[]> handler, params object[] args)
            {
                this._mUFdsa = uFdsa;

                this.name = name;
                this.mode = mode;
                this.startTime = startTime;
                this.duration = duration;
                timerArgsEvent = handler;
                this.args = args;
            }

            public void Run()
            {
                if (isBreak)
                {
                    return;
                }

                if (TimeLeft > 0f)
                {
                    return;
                }

                if (mode == TimerMode.Normal)
                {
                    _mUFdsa.RemoveTimer(name);
                }
                else
                {
                    startTime = _mUFdsa.GetTime();
                    breakDuration = 0;
                }

                timerEvent?.Invoke();
                timerArgsEvent?.Invoke(args);
            }

            public void Break()
            {
                if (isBreak)
                {
                    return;
                }

                isBreak = true;
                breakStart = _mUFdsa.GetTime();
            }

            public void Resume()
            {
                if (!isBreak)
                {
                    return;
                }

                breakDuration += (_mUFdsa.GetTime() - breakStart);
                isBreak = false;
            }

            public static implicit operator bool(TimerTask timerTask)
            {
                return null != timerTask;
            }
        }

        private enum TimerMode
        {
            Normal,
            Repeat,
        }

        private Dictionary<string, TimerTask> addTimerList = new();
        private Dictionary<string, TimerTask> timerList = new();
        private List<string> destroyTimerList = new();

        [SerializeField] private string timerName;
        private TimerTimeType type = TimerTimeType.Null;

        public void SetTimer(string name, TimerTimeType type)
        {
            timerName = name + "_" + type;
            this.type = type;
        }

        private float GetTime()
        {
            return type switch
            {
                TimerTimeType.Time => Time.time,
                TimerTimeType.UnscaledTime => Time.unscaledTime,
                TimerTimeType.RealtimeSinceStartup => Time.realtimeSinceStartup,
                _ => 0
            };
        }

        private void Update()
        {
            if (destroyTimerList.Count > 0)
            {
                foreach (var i in destroyTimerList)
                {
                    timerList.Remove(i);
                }

                destroyTimerList.Clear();
            }

            if (addTimerList.Count > 0)
            {
                foreach (var i in addTimerList.Where(i => i.Value != null))
                {
                    timerList[i.Key] = i.Value;
                }

                addTimerList.Clear();
            }

            if (timerList.Count > 0)
            {
                foreach (var timer in timerList.Values)
                {
                    if (timer == null)
                    {
                        return;
                    }

                    timer.Run();
                }
            }
        }

        public bool AddTimer(string key, float duration, Action handler)
        {
            return InternalAddTimer(key, TimerMode.Normal, duration, handler);
        }

        public bool AddTimer(string key, float duration, Action<object[]> handler, params object[] args)
        {
            return InternalAddTimer(key, TimerMode.Normal, duration, handler, args);
        }

        public bool AddRepeatTimer(string key, float duration, Action handler)
        {
            return InternalAddTimer(key, TimerMode.Repeat, duration, handler);
        }

        public bool AddRepeatTimer(string key, float duration, Action<object[]> handler, params object[] args)
        {
            return InternalAddTimer(key, TimerMode.Repeat, duration, handler, args);
        }

        private bool InternalAddTimer(string key, TimerMode mode, float duration, Action handler)
        {
            if (string.IsNullOrEmpty(key))
            {
                return false;
            }

            if (duration <= 0.0f)
            {
                if (mode == TimerMode.Normal)
                {
                    handler();
                }
                else
                {
                    Debug.LogError("[HeavyTimer]Add Repeat Timer Error: Time is Not Greater Than Zero");
                }

                return false;
            }

            var timer = new TimerTask(this, key, mode, GetTime(), duration, handler);
            addTimerList[key] = timer;
            return true;
        }

        private bool InternalAddTimer(string key, TimerMode mode, float duration, Action<object[]> handler,
            params object[] args)
        {
            if (string.IsNullOrEmpty(key))
            {
                return false;
            }

            if (duration <= 0.0f)
            {
                if (mode == TimerMode.Normal)
                {
                    handler(args);
                }
                else
                {
                    Debug.LogError("[HeavyTimer]Add Repeat Timer Error: Time is Not Greater Than Zero");
                }

                return false;
            }

            var timer = new TimerTask(this, key, mode, GetTime(), duration, handler, args);
            addTimerList[key] = timer;
            return true;
        }

        public void BreakTimerWithPrefix(string prefix)
        {
            if (timerList is { Count: > 0 })
            {
                var arr = new string[timerList.Count];
                timerList.Keys.CopyTo(arr, 0);

                foreach (var t in arr)
                {
                    if (t.StartsWith(prefix))
                    {
                        BreakTimer(t);
                    }
                }
            }
        }

        public void BreakTimer(string key)
        {
            if (!timerList.ContainsKey(key))
            {
                return;
            }

            var timer = timerList[key];
            timer.Break();
        }

        public void ResumeTimerWithPrefix(string prefix)
        {
            if (timerList is { Count: > 0 })
            {
                var arr = new string[timerList.Count];
                timerList.Keys.CopyTo(arr, 0);

                foreach (var t in arr)
                {
                    if (t.StartsWith(prefix))
                    {
                        ResumeTimer(t);
                    }
                }
            }
        }

        public void ResumeTimer(string key)
        {
            if (!timerList.ContainsKey(key))
            {
                return;
            }

            var timer = timerList[key];
            timer.Resume();
        }

        public void ClearTimerWithPrefix(string prefix)
        {
            if (timerList is { Count: > 0 })
            {
                foreach (var timerKey in timerList.Keys.Where(timerKey => timerKey.StartsWith(prefix)))
                {
                    RemoveTimer(timerKey);
                }
            }
        }

        public bool RemoveTimer(string key)
        {
            if (!timerList.ContainsKey(key))
            {
                return false;
            }

            if (!destroyTimerList.Contains(key))
            {
                destroyTimerList.Add(key);
            }

            return true;
        }

        public bool IsRunning(string key)
        {
            return timerList.ContainsKey(key);
        }

        public float GetTimerLeft(string key)
        {
            if (!timerList.ContainsKey(key))
            {
                return 0.0f;
            }

            var timer = timerList[key];
            return timer.TimeLeft;
        }

        public float GetTimerLeftWithPrefix(string prefix)
        {
            if (timerList is { Count: > 0 })
            {
                var arr = new string[timerList.Count];
                timerList.Keys.CopyTo(arr, 0);

                return (from t in arr where t.StartsWith(prefix) select GetTimerLeft(t)).FirstOrDefault();
            }

            return 0.0f;
        }

        public void ClearAll()
        {
            addTimerList.Clear();
            timerList.Clear();
            destroyTimerList.Clear();
        }

        public void Destroy()
        {
            Destroy(this);
        }

        private void OnDestroy()
        {
            ClearAll();
            addTimerList = null;
            timerList = null;
            destroyTimerList = null;
        }
    }
}