using System.Diagnostics;
using Friflo.Engine.ECS;
using LittleSim.Content;
using LittleSim.Net;
using LittleSim.Scenes;
using LittleSim.Server;
using MrGameEng.Core;
using MrGameEng.Graphics;
using MrGameEng.Net;

// Дедикейтед-сервер LittleSim: мир без окна и GPU на HeadlessHost движка.
//   dotnet run --project src/LittleSim.Server                      — fast-forward N дней
//   dotnet run --project src/LittleSim.Server -- --days 30         — то же, явное число дней
//   dotnet run --project src/LittleSim.Server -- --listen [--port N]  — онлайн-мир (WebSocket)
//   dotnet run --project src/LittleSim.Server -- --probe [--port N]   — проверка: подключиться
//                                          к серверу и показать реплицированных жителей

var days = ReadOption("--days", 10);
var ticksPerSecond = ReadOption("--tps", 60);
var port = ReadOption("--port", NetSchema.DefaultPort);

Log.MessageLogged += (level, message) => Console.WriteLine($"[{level}] {message}");

if (Array.IndexOf(args, "--probe") >= 0)
{
    return await Probe(port);
}

var content = GameContent.Load(buildAtlases: false);
Log.Info(
    $"Моды: {string.Join(", ", content.Mods.Select(m => m.ToString()))} | "
        + $"дефов: {content.Defs.All<TerrainDef>().Count} террейна, "
        + $"{content.Defs.All<PlantDef>().Count} растений, "
        + $"{content.Defs.All<PawnDef>().Count} жителей"
);

if (Array.IndexOf(args, "--listen") >= 0)
{
    using var socketServer = new WebSocketServer(port);
    socketServer.Start();
    using var host = new HeadlessHost(
        new HeadlessHostOptions { TicksPerSecond = ticksPerSecond, Realtime = true },
        new HeadlessWorldScene(content, socketServer)
    );
    using var cancel = new CancellationTokenSource();
    Console.CancelKeyPress += (_, eventArgs) =>
    {
        eventArgs.Cancel = true;
        cancel.Cancel();
    };
    Log.Info(
        $"Мир онлайн: ws://localhost:{port}, {HeadlessWorldScene.PawnCount} жителей, "
            + $"{ticksPerSecond} тиков/с. Клиент: dotnet run --project src/LittleSim -- --connect. "
            + "Ctrl+C — остановка."
    );
    host.Run(cancel.Token);
    Log.Info($"Сервер остановлен на тике {host.TickCount}.");
    return 0;
}

using (
    var host = new HeadlessHost(
        new HeadlessHostOptions { TicksPerSecond = ticksPerSecond, Realtime = false },
        new HeadlessWorldScene(content)
    )
)
{
    var ticks = (long)((double)days * WorldScene.SecondsPerDay * ticksPerSecond);
    var stopwatch = Stopwatch.StartNew();
    host.RunTicks(ticks);
    Log.Info(
        $"{days} игровых дней за {stopwatch.Elapsed.TotalSeconds:F1} с реального времени "
            + $"({ticks} тиков, {ticks / Math.Max(stopwatch.Elapsed.TotalSeconds, 0.001):F0} тиков/с)"
    );
}

return 0;

// Подключается к работающему серверу, секунду слушает снапшоты и показывает жителей —
// консольная проверка репликации без игрового клиента.
async Task<int> Probe(int probePort)
{
    var store = new EntityStore();
    var replication = new ReplicationClient(NetSchema.Create(), store);
    var uri = new Uri($"ws://localhost:{probePort}/");
    Log.Info($"Подключение к {uri}…");
    WebSocketClient connection;
    try
    {
        connection = await WebSocketClient.ConnectAsync(
            uri,
            new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token
        );
    }
    catch (Exception exception)
    {
        Log.Error($"Не подключилось: {exception.GetBaseException().Message}");
        return 1;
    }

    using (connection)
    {
        await Task.Delay(1000);
        replication.Pump(connection);
        var first = SamplePositions(store);
        Log.Info($"Снапшот получен: {replication.EntityCount} жителей");

        await Task.Delay(2000);
        replication.Pump(connection);
        var second = SamplePositions(store);
        for (var i = 0; i < first.Count; i++)
        {
            Log.Info(
                $"  житель {first[i].NetId}: ({first[i].X:F0}, {first[i].Y:F0}) → "
                    + $"({second[i].X:F0}, {second[i].Y:F0})"
            );
        }

        var moved = first.Where((p, i) => p.X != second[i].X || p.Y != second[i].Y).Count();
        Log.Info($"Двигались {moved} из {first.Count} показанных — мир жив.");
        return replication.EntityCount > 0 && moved > 0 ? 0 : 1;
    }
}

static List<(int NetId, float X, float Y)> SamplePositions(EntityStore store)
{
    var result = new List<(int, float, float)>();
    foreach (var entity in store.Entities)
    {
        if (result.Count == 5)
        {
            break;
        }

        if (entity.HasComponent<NetId>() && entity.HasComponent<Transform2D>())
        {
            var position = entity.GetComponent<Transform2D>().Position;
            result.Add((entity.GetComponent<NetId>().Value, position.X, position.Y));
        }
    }

    return result;
}

int ReadOption(string name, int fallback)
{
    var index = Array.IndexOf(args, name);
    return index >= 0 && index + 1 < args.Length && int.TryParse(args[index + 1], out var value)
        ? value
        : fallback;
}
