using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using HarmonyLib;
using Svelto.DataStructures;

namespace CLre_server.WebStatus
{
    public class WebServer
    {
        private const uint DEFAULT_PORT = 5030;
        private const string DEFAULT_IP = "localhost";
        private readonly HttpListener _httpListener;

        public delegate void RequestHandler(HttpListenerContext ctx);
        
        private Dictionary<string, RequestHandler> _handlers = new Dictionary<string, RequestHandler>();

        public bool IsRunning
        {
            get => _httpListener.IsListening;
        }

        private string _ip_addr;
        private uint _port;

        public static WebServer MainInstance { get; internal set; }
        
        internal static List<Assembly> _assembliesToCheck = new List<Assembly>(new []{typeof(CLre).Assembly});

        public WebServer() : this(DEFAULT_IP, DEFAULT_PORT)
        {
        }
        
        public WebServer(string ip, uint port)
        {
            if (!HttpListener.IsSupported)
            {
                API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. It will fail to start.");
            }
            _httpListener = new HttpListener();
            _httpListener.Prefixes.Add($"http://{ip}:{port}/");
            _ip_addr = ip;
            _port = port;
        }

        internal static void Init()
        {
            if (Environment.GetCommandLineArgs().Contains("-web"))
            {
                API.Utility.Logging.Log("Starting status web server");
                StatusEndpoints.Init();
                MainInstance = new WebServer();
                MainInstance.Start();
            }
            else
            {
                API.Utility.Logging.Log("Not starting web server (use CLI argument -web to enable)");
            }
        }

        internal static void Deinit()
        {
            if (MainInstance != null) MainInstance.Stop();
            MainInstance = null;
        }
        
        public void Start()
        {
            LoadHandlers();
            try
            {
                _httpListener.Start();
            }
            catch (Exception e)
            {
                API.Utility.Logging.LogWarning(e);
                return;
            }

            HandleAllRequests().Run();
        }

        public void Stop()
        {
            try
            {
                _httpListener.Stop();
                _httpListener.Close();
            }
            catch (Exception e)
            {
                API.Utility.Logging.LogWarning(e);
                return;
            }
        }

        private IEnumerator HandleAllRequests()
        {
            API.Utility.Logging.MetaLog($"Started HTTP web server at http://{_ip_addr}:{_port}/");
            while (_httpListener.IsListening)
            {
                var awaiter = _httpListener.GetContextAsync();
                awaiter.GetAwaiter().OnCompleted(() => DoRequest(awaiter.Result));
                yield return null;
            }
            API.Utility.Logging.MetaLog("Terminated HTTP web server");
        }

        private void DoRequest(HttpListenerContext ctx)
        {
            string endpoint = ctx.Request.Url.LocalPath.ToLower();
#if DEBUG
            API.Utility.Logging.LogWarning($"Handling HTTP request {endpoint}");
#endif
            bool handled = false;
            foreach (string path in _handlers.Keys)
            {
                if (endpoint == path)
                {
                    handled = true;
                    _handlers[path](ctx);
                    break;
                }
            }

            if (!handled)
            {
                AssetEndpoints.Asset404(ctx);
            }
            //byte[] output = Encoding.UTF8.GetBytes(endpoint);
            //ctx.Response.OutputStream.Write(output, 0, output.Length);
            ctx.Response.Close();
        }

        private void LoadHandlers()
        {
            _handlers = new Dictionary<string, RequestHandler>();
            foreach (Assembly asm in _assembliesToCheck.ToArray())
            {
                foreach (Type t in asm.GetTypes())
                {
                    foreach (MethodInfo m in t.GetMethods(AccessTools.all))
                    {
                        WebEndpointAttribute attr = m.GetCustomAttribute<WebEndpointAttribute>();
                        if (attr != null)
                        {
                            // TODO validate that method signature matches that of RequestHandler
                            string key = attr.GetPath().ToLower();
                            API.Utility.Logging.MetaLog($"{t.FullName}:{m.Name} is handling {key}");
                            _handlers.Add(key, (RequestHandler) Delegate.CreateDelegate(typeof(RequestHandler), m));
                        }
                    }
                }
            }
        }
    }
}