using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.Events;
using Utils;

namespace Managers
{
    public class Calendar : SingletonComponent<Calendar>
    {
        [SerializeField] private int minTimeForOffer = 600;
        [SerializeField] private int maxTimeForOffer = 1200;
        [SerializeField] private int dividerTimeForOffer = 15;

        DateTime currentTime;

        DateTime gameStartedTime;

        UnityEvent<DateTime> timerUpdateEvent = new UnityEvent<DateTime>();
        UnityEvent<int> weekEndedEvent = new UnityEvent<int>();
        UnityEvent dayEndedEvent = new UnityEvent();

        DataManager dataManager;

        private void Start()
        {
            dataManager = DataManager.Instance;

            dataManager.SubscribeOnLoad(delegate
            {
                if (!dataManager.HasAccount)
                    return;

                currentTime = dataManager.CurrentTime;
                gameStartedTime = dataManager.GameStartTime;

                //currentTime = currentTime.AddHours(12);

                StartTimer();
            });            
        }

        public void Stop() => StopAllCoroutines();

        public void StartTimer()
        {
            StartCoroutine(Timer());
        }

        public void StartCalendar()
        {
            currentTime = DateTime.Now;

            gameStartedTime = currentTime;

            dataManager.InitTime(currentTime, gameStartedTime);

            StartTimer();
        }

        public int GetDaysCountFromGameStart()
        {
            return (int)(currentTime - gameStartedTime).TotalDays;
        }

        IEnumerator Timer()
        {
            int secondsToWait = 60;
            int gameSpeed = 60;

            var waiter = new WaitForSeconds(secondsToWait / gameSpeed);

            while (true)
            {
                yield return waiter;

                currentTime = currentTime.AddSeconds(secondsToWait);
                Debug.Log(currentTime);
                dataManager.CurrentTime = currentTime;

                timerUpdateEvent.Invoke(currentTime);

                //to test!!!
                if (currentTime.DayOfWeek == DayOfWeek.Monday && currentTime.Hour == 0 && currentTime.Minute == 0)        
                    weekEndedEvent.Invoke(GetWeeksCount());

                if (currentTime.Hour == 0 && currentTime.Minute == 0)
                    weekEndedEvent.Invoke(GetWeeksCount());
            }
        }

        public DateTime GetCurrentTime() => currentTime;

        public DateTime GetRandomData()
        {
            var date = currentTime;
            date = date.AddDays(UnityEngine.Random.Range(0, 3));

            var timeRange = GetRange(minTimeForOffer, maxTimeForOffer, dividerTimeForOffer);
            var time = timeRange[UnityEngine.Random.Range(0, timeRange.Count)];
            var hours = time / 60;
            var mins = time % 60;

            var finalDate = new DateTime(date.Year, date.Month, date.Day, hours, mins, 0);

            //date = date.AddHours(UnityEngine.Random.Range(0, 24));
            //date = date.AddMinutes(UnityEngine.Random.Range(0, 60));

            return finalDate;
        }

        public DateTime GetRandomWorkTime()
        {
            var minutes = UnityEngine.Random.Range(minTimeForOffer, maxTimeForOffer);

            var time = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day);
            time = time.AddMinutes(minutes);

            return time;
        }

        private List<int> GetRange(int start, int end, int divider)
        {
            var range = new List<int>();
            int curr = start;
            while (curr <= end)
            {
                range.Add(curr);
                curr += divider;
            }

            return range;
        }

        public int GetWeeksCount() => WeekCountToTime(currentTime);

        //to test!!!!
        public int WeekCountToTime(DateTime time)
        {
            DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
            if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
            {
                time = time.AddDays(3);
            }

            // Return the week of our adjusted day
            return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        }

        public void SubscribeOnTimer(UnityAction<DateTime> action) => timerUpdateEvent.AddListener(action);

        public void UnSubscribeOnTimer(UnityAction<DateTime> action) => timerUpdateEvent.RemoveListener(action);

        public void SubscribeOnDayEnded(UnityAction action) => dayEndedEvent.AddListener(action);

        public void SubscribeOnWeekEnded(UnityAction<int> action) => weekEndedEvent.AddListener(action);

        public void UnSubscribeOnWeekEnded(UnityAction<int> action) => weekEndedEvent.RemoveListener(action);
    }
}