using BarRaider.SdTools;
using ClipTrimDotNet.Keys;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Newtonsoft.Json;
using SocketIOClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace ClipTrimDotNet.Client
{
    public class ClipTrimClient
    {
        private static ClipTrimClient? instance;
        public static ClipTrimClient Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ClipTrimClient();
                }
                return instance;
            }
        }

        //private HttpClient httpClient;
        private SocketIO? socket;

        public string HostName
        {
            get
            {
                //return $"http://localhost:5010/";
                return $"http://localhost:{GlobalSettings.Instance.PortNumber}/";
            }
        }

        private string? currentHostname = null;

        void CreateSocket()
        {
            Logger.Instance.LogMessage(TracingLevel.INFO, $"Starting ClipTrimClient on port {HostName}");
            socket = new SocketIO(new Uri(HostName));
            currentHostname = HostName;
            socket.Options.AutoUpgrade = false;
            //socket.Options.Path = "/socket.io";
            socket.Options.ConnectionTimeout = TimeSpan.FromSeconds(10);
            socket.Options.Reconnection = true;
            socket.On("full_data", ctx =>
            {
                try
                {
                    var response = ctx.GetValue<List<CollectionMetaData>>(0);
                    Logger.Instance.LogMessage(TracingLevel.INFO, $"full_data event {JsonConvert.SerializeObject(response)}");
                    Collections = response!;
                    Player.TickAll();
                    PageNavigator.TickAll();
                    //Logger.Instance.LogMessage(TracingLevel.INFO, $"Collections {JsonConvert.SerializeObject(Collections)}");
                }
                catch (Exception ex)
                {
                    Logger.Instance.LogMessage(TracingLevel.INFO, $"full_data error {ex.ToString()}");
                }
                return Task.CompletedTask;
            });
            socket.On("collection_updated", ctx =>
            {
                try
                {
                    var response = ctx.GetValue<CollectionMetaData>(0)!;
                    Logger.Instance.LogMessage(TracingLevel.INFO, $"collection_updated event {JsonConvert.SerializeObject(response)}");
                    int index = Collections.FindIndex(x => x.Id == response.Id);
                    if (index != -1)
                    {
                        Collections[index] = response;
                        Player.TickAll();
                        PageNavigator.TickAll();
                    }
                }
                catch
                {

                }

                return Task.CompletedTask;
            });

            socket.OnConnected += (sender, e) =>
            {
                Logger.Instance.LogMessage(TracingLevel.INFO, $"Socket connected: {e}");
            };

            socket.OnDisconnected += (sender, e) =>
            {
                Logger.Instance.LogMessage(TracingLevel.INFO, $"Socket disconnected: {e}");
                Task.Run(async () => await Connect());
            };
            Task.Run(async () => await Connect());
        }

        public ClipTrimClient()
        {
            //httpClient = new HttpClient()
            //{
            //    BaseAddress = new Uri("http://localhost:5010/"),
            //    Timeout = TimeSpan.FromSeconds(10)
            //};
            CreateSocket();
        }

        public async Task Connect()
        {
            if (socket is null) return;
            while (!socket.Connected)
            {
                try
                {
                    await socket.ConnectAsync();
                }
                catch
                {

                }
            }
        }

        public List<CollectionMetaData> Collections { get; private set; } = new List<CollectionMetaData>();
        public int SelectedCollection { get; private set; } = -1;

        public Dictionary<int, int> CollectionIndexes { get; private set; } = new();
        public int PageIndex
        {
            get
            {
                if (SelectedCollection == -1) return 0;
                if (!CollectionIndexes.ContainsKey(SelectedCollection))
                {
                    CollectionIndexes[SelectedCollection] = 0;
                }
                return CollectionIndexes[SelectedCollection];
            }
            set
            {
                if (SelectedCollection == -1) return;
                CollectionIndexes[SelectedCollection] = value;
            }
        }
        public bool PageMode { get; set; } = false;

        public int PageCount
        {
            get
            {
                if (SelectedCollection == -1) return 0;
                var collection = Collections[SelectedCollection];
                return (collection.Clips.Count - 1) / 10 + 1;
            }
        }
        public bool CanPageUp
        {
            get
            {
                if(PageMode) return false;
                if (SelectedCollection == -1) return false;
                return PageCount - PageIndex > 1;
            }
        }

        public bool CanPageDown
        {
            get
            {
                if (PageMode) return false;
                return PageIndex > 0;
            }
        }

        public void PageDown()
        {
            if (CanPageDown)
            {
                PageIndex--;
            }
        }

        public void PageUp()
        {
            if (CanPageUp)
            {
                PageIndex++;
            }
        }
        public List<string> GetCollectionNames()
        {
            //await GetMetadata();
            return Collections.Where(x => x.Name != "Uncategorized").Select(x => x.Name).ToList();
        }

        public string GetCurrentCollectionName()
        {
            if (SelectedCollection == -1) return "";
            return Collections[SelectedCollection].Name;
        }

        public string GetPlayerStringByCoordinateIndex(int index)
        {
            if (PageMode)
            {
                int pageNumber = index + 1;
                if(pageNumber <= PageCount)
                {
                    return pageNumber.ToString();
                }
                return "";
            }
            else
            {
                var collection = GetClipByPagedIndex(index);
                return collection?.Name ?? "";
            }
        }



        public ClipMetadata? GetClipByPagedIndex(int index)
        {
            SelectedCollection = Collections.FindIndex(x => x.Name == GlobalSettings.Instance.ProfileName);
            if (SelectedCollection == -1) return null;
            int clipIndex = PageIndex * 10 + index;
            var collection = Collections[SelectedCollection];
            if (clipIndex >= 0 && clipIndex < collection.Clips.Count)
            {
                return collection.Clips[clipIndex];
            }
            return null;
        }

        public async void PlayClip(int index)
        {
            if (PageMode)
            {
                if(index < 0 || index >= PageCount) return;
                PageIndex = index;
                PageMode = false;
                Player.TickAll();
                PageNavigator.TickAll();
            }
            else
            {
                if (socket is null) return;
                var metadata = GetClipByPagedIndex(index);
                if (metadata == null) return;
                //Logger.Instance.LogMessage(TracingLevel.INFO, $"playing clip:");
                await socket.EmitAsync("play_clip", new List<object>() { metadata });
            }
                
        }

        public async void SaveClip()
        {
            if (socket is null) return;
            CheckPort();
            await socket.EmitAsync("record_clip", new List<object>() { });
        }

        public async void CheckPort()
        {
            if (socket is null) return;
            //Logger.Instance.LogMessage(TracingLevel.INFO, $"Checking port {socket}");
            if (currentHostname != HostName || !socket.Connected)
            {
                //Logger.Instance.LogMessage(TracingLevel.INFO, $"port {socket}");
                if (socket.Connected)
                {
                    await socket.DisconnectAsync();
                }
                socket.Dispose();
                CreateSocket();
            }
        }
    }
}
