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

namespace ChillConnect
{
    public class NormalTimer : MonoBehaviour
    {
        private class TimerTask
        {
            private NormalTimer mNormalTimer;

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

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

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

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

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

                if (TimeLeft > 0f)
                {
                    return;
                }

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

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

            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 RemoveTimer(string key)
        {
            if (!timerList.ContainsKey(key))
            {
                return false;
            }

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

            return true;
        }

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

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