using Newtonsoft.Json;
using REST_JSON_API;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
using UnityEngine.Networking;

public class GlobalBehaviour : MonoBehaviour
{
    public static GlobalBehaviour Instance
    {
        get => _Instance;
        set
        {
            if (_Instance == null)
                _Instance = value;
            else
                Destroy(value);
        }
    }
    private static GlobalBehaviour _Instance;

    //Make sure when using RGBA-Colors, the A-value of animationStartColor 
    //and animationEndColor is the same OR try with value = 255
    public static Color HintAnimationStartColor => Instance._hintAnimationStartColor;
    public static Color HintAnimationEndColor => Instance._hintAnimationEndColor;
    public static float HintAnimationDuration => Instance._hintAnimationDuration;
    public static float AnimationLerpDuration => Instance._AnimationLerpDuration;

    public static Color StageAccomplished => Instance._StageAccomplished;
    public static Color StageNotYetAccomplished => Instance._StageNotYetAccomplished;
    public static Color StageError => Instance._StageError;

    public static float GadgetLaserDistance => Instance._GadgetLaserDistance;
    public static float GadgetPhysicalDistance => Instance._GadgetPhysicalDistance;


    #region Unity Serialization
    [SerializeField] private Color _hintAnimationStartColor;
    [SerializeField] private Color _hintAnimationEndColor;
    [SerializeField] private float _hintAnimationDuration;
    [SerializeField] private float _AnimationLerpDuration;

    [SerializeField] private Color _StageAccomplished;
    [SerializeField] private Color _StageNotYetAccomplished;
    [SerializeField] private Color _StageError;

    [SerializeField] private float _GadgetLaserDistance;
    [SerializeField] private float _GadgetPhysicalDistance;
    #endregion

    private void Awake()
    {
        Instance = this;
        if (Instance != this)
            return;

        DontDestroyOnLoad(this);
        GetScrollsfromServer();
        GetContextfromServer();
    }

    //TODO: Move where appropiate

    public int tryScrollListTimes = 2;
    static public List<REST_JSON_API.Scroll> AvailableScrolls;
    public static IEnumerator InitiateScrolls = IEnumeratorExtensions.yield_break;

    private void GetScrollsfromServer()
    {
        StartCoroutine(InitiateScrolls = _GetScrollsfromServer());

        IEnumerator _GetScrollsfromServer()
        {
            //Try /scroll/listall endpoint when scroll/list is not working
            //UnityWebRequest request = UnityWebRequest.Get(CommunicationEvents.ServerAdress + "/scroll/listall");
            //Postman-Echo-Mock
            //UnityWebRequest request = UnityWebRequest.Get("https://019a8ea5-843a-498b-8d0c-778669aef987.mock.pstmn.io/get");

            System.DateTime requestTime = System.DateTime.UtcNow;
            //UnityWebRequest request = null;
            //for (int i = 0; i < this.tryScrollListTimes; i++)
            //{
            //    request = UnityWebRequest.Get(CommunicationEvents.ServerAdress + "/scroll/list");
            //    request.method = UnityWebRequest.kHttpVerbGET;

            //    yield return request.SendWebRequest();

            //    if (request.result == UnityWebRequest.Result.ConnectionError
            //     || request.result == UnityWebRequest.Result.ProtocolError)
            //    {
            //        Debug.LogWarning(request.error);
            //        Debug.Log("GET Scroll/list failed. Attempt: " + (i + 1).ToString());
            //    }
            //    else
            //        break;
            //}
            //while (request.result == UnityWebRequest.Result.InProgress)
            //    yield return null;

            System.DateTime answerTime = System.DateTime.UtcNow;

            string jsonString = null;

            //if (request.result == UnityWebRequest.Result.ConnectionError
            // || request.result == UnityWebRequest.Result.ProtocolError)
            //{
            //    Debug.LogWarning(request.error);
            //}
            //else
            //{
            //    CommunicationEvents.ServerRunning = true;

            //    while (!request.downloadHandler.isDone)
            //        yield return null;
            //    jsonString = request.downloadHandler.text;
            //}

            if (string.IsNullOrEmpty(jsonString)
             || jsonString.Equals("[]"))
            {
                jsonString = File.ReadAllText(Application.streamingAssetsPath + "/scrolls.json");
                Debug.Log("Using Fallback Scrolls: \n" + jsonString);
            }

            System.DateTime parseTime = System.DateTime.UtcNow;
            AvailableScrolls = JsonConvert.DeserializeObject<List<REST_JSON_API.Scroll>>(jsonString);
            Debug.Log(
                $"Scroll Times:\n" +
                $"Summ\t{(System.DateTime.UtcNow - requestTime).TotalMilliseconds}ms\n" +
                $"Server\t{(answerTime - requestTime).TotalMilliseconds}ms\n" +
                $"Download\t{(parseTime - answerTime).TotalMilliseconds}ms\n" +
                $"Parsing\t{(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms");

            yield break;
        }
    }

    public static FactRecorder Context = new();
    public static IEnumerator InitiateContext = IEnumeratorExtensions.yield_break;
    public static bool ContextLoaded = false;

    private void GetContextfromServer()
    {
        StartCoroutine(InitiateContext = _GetContextfromServer());

        IEnumerator _GetContextfromServer()
        {
            ContextLoaded = false;

            System.DateTime requestTime = System.DateTime.UtcNow;
            UnityWebRequest request = null;
            for (int i = 0; i < this.tryScrollListTimes; i++)
            {
                request = UnityWebRequest.Get(CommunicationEvents.ServerAdress + "/fact/list");
                request.method = UnityWebRequest.kHttpVerbGET;

                yield return request.SendWebRequest();

                if (request.result == UnityWebRequest.Result.ConnectionError
                 || request.result == UnityWebRequest.Result.ProtocolError)
                {
                    Debug.LogWarning(request.error);
                    Debug.Log("GET /fact/list failed. Attempt: " + (i + 1).ToString());
                }
                else
                    break;
            }
            while(request.result == UnityWebRequest.Result.InProgress)
                yield return null;

            System.DateTime answerTime = System.DateTime.UtcNow;

            string jsonString = null;

            if (request.result == UnityWebRequest.Result.ConnectionError
             || request.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogWarning(request.error);
            }
            else
            {
                CommunicationEvents.ServerRunning = true;

                while (!request.downloadHandler.isDone)
                    yield return null;
                jsonString = request.downloadHandler.text;
            }

            if (string.IsNullOrEmpty(jsonString)
             || jsonString.Equals("[]"))
            {
                jsonString = File.ReadAllText(Application.streamingAssetsPath + "/context.json");
                Debug.Log("Using Fallback Context: \n" + jsonString);
            }

            System.DateTime parseTime = System.DateTime.UtcNow;
            MMTFact[] context_facts = JsonConvert.DeserializeObject<MMTFact[]>(jsonString);
            System.DateTime parseEnd = System.DateTime.UtcNow;

            System.DateTime justParseTime = default;
            System.DateTime justAddinTime = default;

            bool samestep = false;
            foreach (MMTFact fact in context_facts)
            {
                if (Regex.IsMatch(fact.@ref.uri, ".*fact\\d+$") // one of ours
                 || Regex.IsMatch(fact.@ref.uri, ".*SituationTheory\\d+\\?.*") // one of ours
                 || fact.GetDefines() == null) // Scala rule?
                    continue;

                List<Fact> new_list = new();

                System.DateTime parse_time = System.DateTime.Now;
                yield return ParsingDictionary.parseFactDictionary[fact.getType()](new_list, fact);
                justParseTime += System.DateTime.Now - parse_time;
                
                if (new_list.Count == 0)
                {
                    Debug.LogWarning("Parsing on context-fact returned empty List -> One of the dependent facts does not exist or parsing failed");
                    continue;
                }

                System.DateTime addin_time = System.DateTime.Now;
                foreach (Fact new_fact in new_list)
                {
                    Context.Add(new_fact, out bool exists, samestep, null, null, true, true);
                    if (!exists)
                        samestep = true;

                    yield return null;
                }
                justAddinTime += System.DateTime.Now - addin_time;
            }

            Debug.Log(
                $"Context Times:\n" +
                $"Summ\t{(System.DateTime.UtcNow - requestTime).TotalMilliseconds}ms\n" +
                $"Server\t{(answerTime - requestTime).TotalMilliseconds}ms\n" +
                $"Download\t{(parseTime - answerTime).TotalMilliseconds}ms\n" +
                $"Parsing\t{(parseEnd - parseTime).TotalMilliseconds}ms\n" +
                $"FactParsing\t{justParseTime.Millisecond}ms\n" +
                $"FactAdding\t{justAddinTime.Millisecond}ms\n" +
                "");

            ContextLoaded = true;
            yield break;
        }
    }
}