// WebSocket.cs
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Decal.Adapter;
using Newtonsoft.Json;
using uTank2;

namespace MosswartMassacre
{
    internal static class SessionInfo
    {
    
        internal static readonly Guid Guid = Guid.NewGuid();

        internal static readonly string GuidString = Guid.ToString("N");
    }
    // 1) The envelope type for incoming commands
    public class CommandEnvelope
    {
        [JsonProperty("player_name")]
        public string PlayerName { get; set; }

        [JsonProperty("command")]
        public string Command { get; set; }
    }

    public static class WebSocket
    {
        // ─── configuration ──────────────────────────
        private static readonly Uri WsEndpoint = new Uri("wss://overlord.snakedesert.se/websocket/");
        private const string SharedSecret = "your_shared_secret";
        private const int IntervalSec = 5;
        private static string SessionId = "";

        // ─── runtime state ──────────────────────────
        private static ClientWebSocket _ws;
        private static CancellationTokenSource _cts;
        private static bool _enabled;
        private static readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
        private static readonly SynchronizationContext _uiCtx = SynchronizationContext.Current;

        /// <summary>
        /// Fires when a valid CommandEnvelope arrives for this character.
        /// </summary>
        public static event Action<CommandEnvelope> OnServerCommand;

        // ─── public API ─────────────────────────────

        public static void Start()
        {
            if (_enabled) return;
            _enabled = true;
            _cts = new CancellationTokenSource();

            PluginCore.WriteToChat("[WebSocket] connecting…");
            _ = Task.Run(ConnectAndLoopAsync);

        }

        public static void Stop()
        {
            if (!_enabled) return;
            _enabled = false;

            _cts.Cancel();
            _ws?.Abort();
            _ws?.Dispose();
            _ws = null;

            PluginCore.WriteToChat("[WebSocket] DISABLED");
        }

        // ─── connect / receive / telemetry loop ──────────────────────

        private static async Task ConnectAndLoopAsync()
        {
            while (_enabled && !_cts.IsCancellationRequested)
            {
                try
                {
                    // 1) Establish connection
                    _ws = new ClientWebSocket();
                    _ws.Options.SetRequestHeader("X-Plugin-Secret", SharedSecret);
                    await _ws.ConnectAsync(WsEndpoint, _cts.Token);
                    PluginCore.WriteToChat("[WebSocket] CONNECTED");
                    SessionId = $"{CoreManager.Current.CharacterFilter.Name}-{DateTime.UtcNow:yyyyMMdd-HHmmss}";

        // ─── Register this socket under our character name ───
        var registerEnvelope = new
                    {
                        type = "register",
                        player_name = CoreManager.Current.CharacterFilter.Name
                    };
                    var regJson = JsonConvert.SerializeObject(registerEnvelope);
                    await SendEncodedAsync(regJson, _cts.Token);
                    PluginCore.WriteToChat("[WebSocket] REGISTERED");

                    var buffer = new byte[4096];

                    // 2) Fire-and-forget receive loop
                    var receiveTask = Task.Run(async () =>
                    {
                        while (_ws.State == WebSocketState.Open && !_cts.Token.IsCancellationRequested)
                        {
                            WebSocketReceiveResult result;
                            try
                            {
                                result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cts.Token);
                            }
                            catch (OperationCanceledException)
                            {
                                break;
                            }
                            catch (Exception ex)
                            {
                                PluginCore.WriteToChat($"[WebSocket] receive error: {ex.Message}");
                                break;
                            }

                            if (result.MessageType == WebSocketMessageType.Close)
                                break;

                            var msg = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim();

                            // 3) Parse into CommandEnvelope
                            CommandEnvelope env;
                            try
                            {
                                env = JsonConvert.DeserializeObject<CommandEnvelope>(msg);
                            }
                            catch (JsonException)
                            {
                                continue;  // skip malformed JSON
                            }

                            // 4) Filter by this character name
                            if (string.Equals(
                                env.PlayerName,
                                CoreManager.Current.CharacterFilter.Name,
                                StringComparison.OrdinalIgnoreCase))
                            {
                                _uiCtx.Post(_ =>
                                {
                                    try
                                    {
                                        OnServerCommand?.Invoke(env);   // now on the correct thread
                                    }
                                    catch (Exception ex)
                                    {
                                        PluginCore.WriteToChat($"[CMD] {ex.Message}");
                                    }
                                }, null);
                            }
                        }
                    });

                    // 5) Inline telemetry loop
                    while (_ws.State == WebSocketState.Open && !_cts.Token.IsCancellationRequested)
                    {
                        var json = BuildPayloadJson();
                        await SendEncodedAsync(json, _cts.Token);

                        try
                        {
                            await Task.Delay(TimeSpan.FromSeconds(IntervalSec), _cts.Token);
                        }
                        catch (OperationCanceledException)
                        {
                            break;
                        }
                    }

                    // Wait for receive loop to finish
                    await receiveTask;
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    PluginCore.WriteToChat($"[WebSocket] error: {ex.Message}");
                }
                finally
                {
                    _ws?.Abort();
                    _ws?.Dispose();
                    _ws = null;
                }

                // Pause before reconnecting
                try { await Task.Delay(2000, CancellationToken.None); } catch { }
            }
        }


        // ─── fire-and-forget chat sender ────────────────────

        public static async Task SendChatTextAsync(int colorIndex, string chatText)
        {
            var envelope = new
            {
                type = "chat",
                timestamp = DateTime.UtcNow.ToString("o"),
                character_name = CoreManager.Current.CharacterFilter.Name,
                text = chatText,
                color = colorIndex
                
            };
            var json = JsonConvert.SerializeObject(envelope);
            await SendEncodedAsync(json, CancellationToken.None);
        }
        public static async Task SendSpawnAsync(string nsCoord, string ewCoord, string monster)
        {
            var envelope = new
            {
                type = "spawn",
                timestamp = DateTime.UtcNow.ToString("o"),
                character_name = CoreManager.Current.CharacterFilter.Name,
                mob = monster,
                ns = nsCoord,
                ew = ewCoord

            };
            var json = JsonConvert.SerializeObject(envelope);
            await SendEncodedAsync(json, CancellationToken.None);
        }
        public static async Task SendRareAsync(string rare)
        {
            var coords = Coordinates.Me;
            var envelope = new
            {
                type = "rare",
                timestamp = DateTime.UtcNow.ToString("o"),
                character_name = CoreManager.Current.CharacterFilter.Name,
                name = rare,
                ew = coords.EW,
                ns = coords.NS,
                z = coords.Z

            };
            var json = JsonConvert.SerializeObject(envelope);
            await SendEncodedAsync(json, CancellationToken.None);
        }

        public static async Task SendFullInventoryAsync(List<Mag.Shared.MyWorldObject> inventory)
        {
            var envelope = new
            {
                type = "full_inventory",
                timestamp = DateTime.UtcNow.ToString("o"),
                character_name = CoreManager.Current.CharacterFilter.Name,
                item_count = inventory.Count,
                items = inventory

            };
            var json = JsonConvert.SerializeObject(envelope);
            await SendEncodedAsync(json, CancellationToken.None);
        }

        // ─── shared send helper with locking ───────────────

        private static async Task SendEncodedAsync(string text, CancellationToken token)
        {
            await _sendLock.WaitAsync(token);
            try
            {
                if (_ws == null || _ws.State != WebSocketState.Open)
                    return;

                var bytes = Encoding.UTF8.GetBytes(text);
                await _ws.SendAsync(new ArraySegment<byte>(bytes),
                                    WebSocketMessageType.Text,
                                    true,
                                    token);
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat("[WebSocket] send error: " + ex.Message);
                _ws?.Abort();
                _ws?.Dispose();
                _ws = null;
            }
            finally
            {
                _sendLock.Release();
            }
        }

        // ─── payload builder ──────────────────────────────

        

        private static string BuildPayloadJson()
        {
            var tele = new ClientTelemetry();
            var coords = Coordinates.Me;
            var payload = new
            {
                type = "telemetry",
                character_name = CoreManager.Current.CharacterFilter.Name,
                char_tag = PluginCore.CharTag,
                session_id = SessionInfo.GuidString,
                timestamp = DateTime.UtcNow.ToString("o"),
                ew = coords.EW,
                ns = coords.NS,
                z = coords.Z,
                kills = PluginCore.totalKills,
                kills_per_hour = PluginCore.killsPerHour.ToString("F0"),
                onlinetime = (DateTime.Now - PluginCore.statsStartTime).ToString(@"dd\.hh\:mm\:ss"),
                deaths = PluginCore.sessionDeaths.ToString(),
                total_deaths = PluginCore.totalDeaths.ToString(),
                prismatic_taper_count = Utils.GetItemStackSize("Prismatic Taper").ToString(),
                vt_state = VtankControl.VtGetMetaState(),
                mem_mb = tele.MemoryBytes,
                cpu_pct = tele.GetCpuUsage(),
                mem_handles = tele.HandleCount

            };
            return JsonConvert.SerializeObject(payload);
        }
    }
}
