using System;
using System.Collections.Generic;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Unity;
using NitroxModel.DataStructures.Util;
using NitroxServer.GameLogic.Entities;

namespace NitroxServer.GameLogic;

public class EscapePodManager
{
    private const int PLAYERS_PER_ESCAPEPOD = 50;

    private readonly EntityRegistry entityRegistry;
    private readonly ThreadSafeDictionary<ushort, EscapePodWorldEntity> escapePodsByPlayerId = new();
    private EscapePodWorldEntity podForNextPlayer;
    private readonly string seed;

    private readonly RandomStartGenerator randomStart;

    public EscapePodManager(EntityRegistry entityRegistry, RandomStartGenerator randomStart, string seed)
    {
        this.seed = seed;
        this.randomStart = randomStart;
        this.entityRegistry = entityRegistry;

        List<EscapePodWorldEntity> escapePods = entityRegistry.GetEntities<EscapePodWorldEntity>();

        InitializePodForNextPlayer(escapePods);
        InitializeEscapePodsByPlayerId(escapePods);
    }

    public NitroxId AssignPlayerToEscapePod(ushort playerId, out Optional<EscapePodWorldEntity> newlyCreatedPod)
    {
        newlyCreatedPod = Optional.Empty;

        if (escapePodsByPlayerId.TryGetValue(playerId, out EscapePodWorldEntity podEntity))
        {
            return podEntity.Id;
        }

        if (IsPodFull(podForNextPlayer))
        {
            newlyCreatedPod = Optional.Of(CreateNewEscapePod());
            podForNextPlayer = newlyCreatedPod.Value;
        }

        podForNextPlayer.Players.Add(playerId);
        escapePodsByPlayerId[playerId] = podForNextPlayer;

        return podForNextPlayer.Id;
    }

    private EscapePodWorldEntity CreateNewEscapePod()
    {
        EscapePodWorldEntity escapePod = new(GetStartPosition(), new NitroxId(), new EscapePodMetadata(false, false));

        escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "5c06baec-0539-4f26-817d-78443548cc52", new NitroxTechType("Radio"), 0, null, escapePod.Id));
        escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "c0175cf7-0b6a-4a1d-938f-dad0dbb6fa06", new NitroxTechType("MedicalCabinet"), 0, null, escapePod.Id));
        escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "9f16d82b-11f4-4eeb-aedf-f2fa2bfca8e3", new NitroxTechType("Fabricator"), 0, null, escapePod.Id));
        escapePod.ChildEntities.Add(new InventoryEntity(0, new NitroxId(), new NitroxTechType("SmallStorage"), null, escapePod.Id, []));

        entityRegistry.AddOrUpdate(escapePod);

        return escapePod;
    }

    private NitroxVector3 GetStartPosition()
    {
        List<EscapePodWorldEntity> escapePods = entityRegistry.GetEntities<EscapePodWorldEntity>();

        Random rnd = new(seed.GetHashCode());
        NitroxVector3 position = randomStart.GenerateRandomStartPosition(rnd);

        if (escapePods.Count == 0)
        {
            return position;
        }

        foreach (EscapePodWorldEntity escapePodModel in escapePods)
        {
            if (position == NitroxVector3.Zero)
            {
                break;
            }

            if (escapePodModel.Transform.Position != position)
            {
                return position;
            }
        }

        float xNormed = (float)rnd.NextDouble();
        float zNormed = (float)rnd.NextDouble();

        if (xNormed < 0.3f)
        {
            xNormed = 0.3f;
        }
        else if (xNormed > 0.7f)
        {
            xNormed = 0.7f;
        }

        if (zNormed < 0.3f)
        {
            zNormed = 0.3f;
        }
        else if (zNormed > 0.7f)
        {
            zNormed = 0.7f;
        }

        NitroxVector3 lastEscapePodPosition = escapePods[escapePods.Count - 1].Transform.Position;

        float x = xNormed * 100 - 50;
        float z = zNormed * 100 - 50;

        return new NitroxVector3(lastEscapePodPosition.X + x, 0, lastEscapePodPosition.Z + z);
    }

    private void InitializePodForNextPlayer(List<EscapePodWorldEntity> escapePods)
    {
        foreach (EscapePodWorldEntity pod in escapePods)
        {
            if (!IsPodFull(pod))
            {
                podForNextPlayer = pod;
                return;
            }
        }

        podForNextPlayer = CreateNewEscapePod();
    }

    private void InitializeEscapePodsByPlayerId(List<EscapePodWorldEntity> escapePods)
    {
        escapePodsByPlayerId.Clear();
        foreach (EscapePodWorldEntity pod in escapePods)
        {
            foreach (ushort playerId in pod.Players)
            {
                escapePodsByPlayerId[playerId] = pod;
            }
        }
    }

    private static bool IsPodFull(EscapePodWorldEntity pod)
    {
        return pod.Players.Count >= PLAYERS_PER_ESCAPEPOD;
    }
}
