using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

namespace LittleSim.Web.Pages
{
    public partial class Index
    {
        [Inject]
        private NavigationManager Navigation { get; set; } = null!;

        private LittleSimWebGame? _game;
        private string _serverAddress = "";
        private Uri? _pendingServer; // запрошен Connect до создания игры — подключим в первом тике
        private ConnectionStatus _lastStatus = ConnectionStatus.Idle;
        private int _lastCount = -1;
        private int _frame;

        protected override void OnAfterRender(bool firstRender)
        {
            base.OnAfterRender(firstRender);

            if (firstRender)
            {
                _serverAddress = DefaultServerAddress();
                StateHasChanged();
                JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
            }
        }

        // Гонится из requestAnimationFrame (index.html). Игру создаём лениво в первом тике —
        // мира она не создаёт, лишь ждёт адрес сервера из формы.
        [JSInvokable]
        public void TickDotNet()
        {
            if (_game == null)
            {
                _game = new LittleSimWebGame();
                _game.Run();
            }

            _game.Tick();

            if (_pendingServer is not null)
            {
                _game.Connect(_pendingServer);
                _pendingServer = null;
            }

            // Перерисовываем оверлей только при смене статуса (или изредка — чтобы освежить счётчик),
            // а не каждый кадр rAF.
            var status = _game.Status;
            var count = _game.EntityCount;
            if (
                status != _lastStatus
                || (status == ConnectionStatus.Connected && count != _lastCount && _frame % 15 == 0)
            )
            {
                _lastStatus = status;
                _lastCount = count;
                InvokeAsync(StateHasChanged);
            }

            _frame++;
        }

        private void OnAddressKey(KeyboardEventArgs e)
        {
            if (e.Key == "Enter")
            {
                Connect();
            }
        }

        private void Connect()
        {
            if (!Uri.TryCreate(_serverAddress?.Trim(), UriKind.Absolute, out var uri))
            {
                return; // адрес не похож на ws://… — игнорируем
            }

            if (_game is not null)
            {
                _game.Connect(uri);
            }
            else
            {
                _pendingServer = uri;
            }
        }

        private string StatusText =>
            _lastStatus switch
            {
                ConnectionStatus.Connecting => "подключение…",
                ConnectionStatus.Connected => $"подключено · {_lastCount} жит.",
                ConnectionStatus.Reconnecting => "переподключение…",
                _ => "не подключено",
            };

        private string StatusClass =>
            _lastStatus switch
            {
                ConnectionStatus.Connected => "ok",
                ConnectionStatus.Connecting or ConnectionStatus.Reconnecting => "warn",
                _ => "idle",
            };

        // Адрес по умолчанию: ?server=ws://host:port из URL страницы, иначе хост страницы на порту сервера.
        private string DefaultServerAddress()
        {
            var page = new Uri(Navigation.Uri);
            var query = page.Query.TrimStart('?');
            foreach (var pair in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
            {
                var separator = pair.IndexOf('=');
                if (separator > 0 && pair[..separator] == "server")
                {
                    return Uri.UnescapeDataString(pair[(separator + 1)..]);
                }
            }

            return new UriBuilder
            {
                Scheme = "ws",
                Host = page.Host,
                Port = WebNetSchema.DefaultPort,
            }.Uri.ToString();
        }
    }
}
