using System.Text.Json.Serialization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging;
using VipCoreApi;
using static VipCoreApi.IVipCoreApi;

namespace VIP_Particle;

public class VipParticle : BasePlugin
{
    public override string ModuleAuthor => "thesamefabius";
    public override string ModuleName => "[VIP] Particle (PRIVATE)";
    public override string ModuleVersion => "v1.0.0";

    private IVipCoreApi? _api;
    private readonly PluginCapability<IVipCoreApi> _pluginCapability = new("vipcore:core");
    private Particle _particle;

    public override void OnAllPluginsLoaded(bool hotReload)
    {
        Logger.LogInformation("VIP Particle Load");

        _api = _pluginCapability.Get();
        if (_api == null) return;

        _particle = new Particle(this, _api);
        _api.RegisterFeature(_particle, FeatureType.Selectable);
    }

    public override void Unload(bool hotReload)
    {
        Logger.LogInformation("VIP Particle Unload");
        _api?.UnRegisterFeature(_particle);
    }
}

public class Particle : VipFeatureBase
{
    private readonly BasePlugin _basePlugin;
    public override string Feature => "particle";

    private readonly Config _config;
    private readonly PlayerParticle[] _playerParticle = new PlayerParticle[65];

    public Particle(BasePlugin basePlugin, IVipCoreApi api) : base(api)
    {
        _basePlugin = basePlugin;
        _config = LoadConfig<Config>("vip_particle");

        basePlugin.RegisterListener<Listeners.OnServerPrecacheResources>(manifest =>
        {
            foreach (var particle in _config.Particles)
            {
                basePlugin.Logger.LogInformation("Precache {p}", particle.Value.Path);
                manifest.AddResource(particle.Value.Path);
            }
        });

        basePlugin.RegisterEventHandler<EventPlayerDisconnect>((@event, info) =>
        {
            var player = @event.Userid;
            if (!player.IsValid || player.IsBot) return HookResult.Continue;
            
            basePlugin.Logger.LogInformation("Player disconnect start");
            RemoveActiveParticle(player);
            _playerParticle[player.Slot] = new PlayerParticle();
            basePlugin.Logger.LogInformation("Player disconnect end");
            
            return HookResult.Continue;
        });

        basePlugin.RegisterEventHandler<EventPlayerDeath>(EventPlayerDeath);
        basePlugin.RegisterEventHandler<EventCsIntermission>((@event, info) =>
        {
            foreach (var player in Utilities.GetPlayers())
            {
                RemoveActiveParticle(player);
            }

            return HookResult.Continue;
        });
    }

    public override void OnPlayerLoaded(CCSPlayerController player, string group)
    {
        _basePlugin.Logger.LogInformation("Player {name} loaded", player.PlayerName);
        _playerParticle[player.Slot] = new PlayerParticle
        {
            UniqueId = PlayerHasFeature(player)
                ? GetPlayerCookie<string>(player.SteamID, "particle_unique_id")
                : string.Empty
        };
        _basePlugin.Logger.LogInformation("Player {name} initialized", player.PlayerName);
    }

    public override void OnSelectItem(CCSPlayerController player, FeatureState state)
    {
        _basePlugin.Logger.LogInformation("Player {name} select particle item", player.PlayerName);
        var menu = new CenterHtmlMenu(GetTranslatedText("particle.menu.title"));
        var playerParticle = _playerParticle[player.Slot];

        menu.AddMenuOption(GetTranslatedText("particle.menu.off"), (controller, option) =>
        {
            _basePlugin.Logger.LogInformation("Player {name} disabled the particle", player.PlayerName);
            playerParticle.UniqueId = string.Empty;
            RemoveActiveParticle(controller);

            SetPlayerCookie(controller.SteamID, "particle_unique_id", "-");
            PrintToChat(controller, GetTranslatedText("particle.off"));
        }, string.IsNullOrEmpty(playerParticle.UniqueId) || playerParticle.UniqueId is "-");

        foreach (var (key, value) in _config.Particles)
        {
            menu.AddMenuOption(value.Name, (controller, option) =>
            {
                _basePlugin.Logger.LogInformation("Player {name} enable the particle {particle_name}",
                    player.PlayerName, value.Name);
                playerParticle.UniqueId = key;
                CreateParticle(controller, value.Path);
                SetPlayerCookie(controller.SteamID, "particle_unique_id", key);

                PrintToChat(controller, GetTranslatedText("particle.set", value.Name));
            }, playerParticle.UniqueId == key);
        }

        MenuManager.OpenCenterHtmlMenu(_basePlugin, player, menu);
    }

    public override void OnPlayerSpawn(CCSPlayerController player)
    {
        _basePlugin.Logger.LogInformation("{event} event triggered", "OnPlayerSpawn");
        if (!player.IsValid) return;
        if (!PlayerHasFeature(player)) return;

        var particle = _playerParticle[player.Slot];
        if (string.IsNullOrEmpty(particle.UniqueId) || particle.UniqueId == "-")
        {
            _basePlugin.Logger.LogInformation("Particle creation for {name} is {cancel}", player.PlayerName,
                "canceled");
            return;
        }

        if (!_config.Particles.TryGetValue(particle.UniqueId, out var particleConfig)) return;
        
        CreateParticle(player, particleConfig.Path);
        _basePlugin.Logger.LogInformation("{success} creating a particle for a {name}", "Successfully", player.PlayerName);
    }

    private HookResult EventPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
    {
        _basePlugin.Logger.LogInformation("{event} event triggered", "EventPlayerDeath");
        var player = @event.Userid;
        if (player == null || !player.IsValid) return HookResult.Continue;
        if (!PlayerHasFeature(player)) return HookResult.Continue;
        
        var particle = _playerParticle[player.Slot];
        
        if (string.IsNullOrEmpty(particle.UniqueId) || particle.UniqueId == "-")
        {
            _basePlugin.Logger.LogInformation("Particle deletion for the {name} is {cancel}", player.PlayerName, "canceled");
            return HookResult.Continue;
        }

        RemoveActiveParticle(player);

        return HookResult.Continue;
    }

    private void CreateParticle(CCSPlayerController player, string path)
    {
        _basePlugin.Logger.LogInformation("Create new particle {path}", path);
        RemoveActiveParticle(player);

        var particle = Utilities.CreateEntityByName<CParticleSystem>("info_particle_system");
        if (particle == null) return;

        particle.EffectName = path;

        var playerPawn = player.PlayerPawn.Value;

        particle.DispatchSpawn();
        particle.Teleport(playerPawn?.AbsOrigin!, new QAngle(90, 0, 0), new Vector());
        Server.NextFrame(() =>
        {
            particle.FireInput("Start");
            particle.FireInput("SetParent", "!activator", playerPawn);
        });

        _playerParticle[player.Slot].Particle = particle;
        _basePlugin.Logger.LogInformation("a new particle was {success} created", "successfully");
    }

    private void RemoveActiveParticle(CCSPlayerController player)
    {
        _basePlugin.Logger.LogInformation("Player {name} remove active particle: start", player.PlayerName);
        var particle = _playerParticle[player.Slot];
        if (particle.Particle is { IsValid: true })
        {
            particle.Particle.Remove();
            particle.Particle = null;
            _basePlugin.Logger.LogInformation("Player {name} remove active particle: end", player.PlayerName);
        }
    }
}

public class Config
{
    public Dictionary<string, ParticleConfig> Particles { get; set; } = new()
    {
        ["dust_burning_engine_fire_glow"] = new ParticleConfig
        {
            Name = "Fire Glow",
            Path = "particles/maps/de_dust/dust_burning_engine_fire_glow.vpcf"
        },
        ["burning_fire_test"] = new ParticleConfig
        {
            Name = "Fire Test",
            Path = "particles/dev/burning_fire_test.vpcf"
        },
        ["barrel_burning_engine_smoke_dark"] = new ParticleConfig
        {
            Name = "Smoke Dark",
            Path = "particles/burning_fx/barrel_burning_engine_smoke_dark.vpcf"
        }
    };
}

public class PlayerParticle
{
    public string UniqueId { get; set; } = string.Empty;
    public CParticleSystem? Particle { get; set; }
}

public class ParticleConfig
{
    public string Name { get; set; } = string.Empty;
    public string Path { get; set; } = string.Empty;
}

как улучшить код? На русском