using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using Rust.Ai.Gen2;
using UnityEngine;
using ProtoBuf;

/**********************************************************************
*
*   2.4.0   -   Minor adjustments
*               Fixed most chatmessages in corrected language
*               Removed a hardcoded debug
*               Added Killfeed text allignment (left/right) to cfg and methods
*               Fix for dupe tags
*               Added support for wolf2 (make sure to add it to the cfg file manualy)
*               Added API : Interface.Call("OnKillFeedMessageReceived" , text);
*   2.4.1   -   Added permission simplekillfeed.exclude
*   2.4.2   -   Added more checks for The new animal AI
*   2.4.3   -   Added support for the new animals (add them to the cfg manually)
*               (Tiger , Panther , Crocodile , snake.entity
*   2.4.4   -   Fix for npc NRE
*               Made sure snake corpses are not triggering the feed
*               Fixed deaths by npc showing double
*           
***********************************************************************/
namespace Oxide.Plugins
{
    [Info("Simple Kill Feed", "Krungh Crow", "2.4.4")]
    [Description("A kill feed, that displays various kill events .")]
    public class SimpleKillFeed : RustPlugin
    {
        #region Fields

        [PluginReference] private Plugin BetterChat , Clans;
        private bool _isClansReborn;
        private bool _isClansIo;
        private bool _isClansMevent;
        private readonly Dictionary<uint, string> _itemNameMapping = new Dictionary<uint, string>();
        private GameObject _holder;
        private KillQueue _killQueue;
        private static SKFConfig configdata;
        private static SimpleKillFeedData _data;

        const string Exclude_Perm = "simplekillfeed.exclude";


        #endregion

        #region Config

        private class SKFConfig
        {
            [JsonProperty("Show Traps and Entitys in Kill Feed")]
            public bool EnableEntityFeed;
            [JsonProperty("Show Animals kills (default true)")]
            public bool EnableAnimalFeed;
            [JsonProperty("Show Npcs kills (Default true)")]
            public bool EnableNpcFeed;
            [JsonProperty("Show suicides (Default: true)")]
            public bool EnableSuicides;
            [JsonProperty("Show Deaths by Animals (Default: true)")]
            public bool EnableAnimal;
            [JsonProperty("Show Deaths by Cold (Default: true)")]
            public bool EnableCold;
            [JsonProperty("Show deaths by Drowning (Default: true)")]
            public bool EnableDrowning;
            [JsonProperty("Show Deaths by Fall (Default: true)")]
            public bool EnableFall;
            [JsonProperty("Show Deaths by Hunger (Default: true)")]
            public bool EnableHunger;
            [JsonProperty("Show Deaths by Electricution (Default: true)")]
            public bool EnableElectricution;
            [JsonProperty("Show Deaths by Radiation (Default: true)")]
            public bool EnableRadiationKills;
            [JsonProperty("Chat Icon Id (Steam profile ID)")]
            public ulong IconId;
            [JsonProperty("Max messages in feed (Default: 5)")]
            public int MaxFeedMessages;
            [JsonProperty("Max player name length in feed (Default: 18)")]
            public int MaxPlayerNameLength;
            [JsonProperty("Feed message TTL in seconds (Default: 7)")]
            public int FeedMessageTtlSec;
            [JsonProperty("Allow kill messages in chat along with kill feed")]
            public bool EnableChatFeed;
            [JsonProperty("Log PvP Kill events")]
            public bool EnableLogging;
            [JsonProperty("Height ident (space between messages). Default: 0.0185")]
            public float HeightIdent;
            [JsonProperty("Feed Position - Anchor Max. (Default: 0.995 0.986")]
            public string AnchorMax;
            [JsonProperty("Feed Position - Anchor Min. (Default: 0.723 0.964")]
            public string AnchorMin;
            [JsonProperty("Font size of kill feed (Default: 12)")]
            public int FontSize;
            [JsonProperty("Default textanchor (left or right)")]
            public string TextAnchor;
            [JsonProperty("Outline Text Size (Default: 0.5 0.5)")]
            public string OutlineSize;
            [JsonProperty("Default color for distance (if too far from any from the list). Default: #FF8000")]
            public string DefaultDistanceColor;
            [JsonProperty("Distance Colors List (Certain color will apply if distance is <= than specified)")]
            public DistanceColor[] DistanceColors;
            [JsonProperty("Custom Entity Names, you can remove or add more!")]
            public Dictionary<string, string> Ents = new Dictionary<string, string>();
            [JsonProperty("Custom Animal Names, you can remove or add more!")]
            public Dictionary<string, string> Animal = new Dictionary<string, string>();
            [JsonProperty("Custom Weapon Names, you can add more!")]
            public Dictionary<string, string> Weapons = new Dictionary<string, string>();
            [JsonProperty("Custom Npc Names, you can add more!")]
            public Dictionary<string, string> Npcs = new Dictionary<string, string>();

            [OnDeserialized]
            internal void OnDeserialized(StreamingContext ctx) => Array.Sort(DistanceColors, (o1, o2) => o1.DistanceThreshold.CompareTo(o2.DistanceThreshold));

            public class DistanceColor
            {
                public int DistanceThreshold;
                public string Color;
                public bool TestDistance(int distance) => distance <= DistanceThreshold;
            }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            configdata = Config.ReadObject<SKFConfig>();

            if (string.IsNullOrEmpty(configdata.TextAnchor))
            {
                configdata.TextAnchor = "right";
                SaveConfig();
                Puts("Adding TextAnchor to the cfg");
            }
        }

        protected override void LoadDefaultConfig()
        {
            configdata = new SKFConfig
            {
                EnableEntityFeed = true,
                EnableAnimalFeed = true,
                EnableNpcFeed = true,
                EnableFall = true ,
                EnableHunger = true ,
                EnableElectricution = true,
                EnableSuicides = true,
                EnableAnimal = true ,
                EnableCold = true ,
                EnableDrowning = true,
                EnableRadiationKills = true,
                IconId = 76561197960839785UL,
                MaxFeedMessages = 5,
                MaxPlayerNameLength = 18,
                FeedMessageTtlSec = 7,
                EnableChatFeed = true,
                EnableLogging = false,
                HeightIdent = 0.0185f,
                AnchorMax = "0.995 0.986",
                AnchorMin = "0.723 0.964",
                FontSize = 12,
                TextAnchor = "right" ,
                OutlineSize = "0.5 0.5",
                DefaultDistanceColor = "#FF8000",
                DistanceColors = new[]
                {
                    new SKFConfig.DistanceColor
                    {
                        Color = "#FFFFFF",
                        DistanceThreshold = 50
                    },
                    new SKFConfig.DistanceColor
                    {
                        Color = "#91D6FF",
                        DistanceThreshold = 100
                    },
                    new SKFConfig.DistanceColor
                    {
                        Color = "#FFFF00",
                        DistanceThreshold = 150
                    }
                },
                Ents = new Dictionary<string, string>()
                {
                    { "autoturret_deployed","Auto Turret" },
                    { "flameturret.deployed","Flame Turret"},
                    { "guntrap.deployed","Gun Trap"},
                    { "landmine","Landmine"},
                    { "beartrap","Bear Trap"},
                    { "sam_site_turret_deployed","Sam Site Turret"},
                    { "patrolhelicopter","Helicopter"},
                    { "bradleyapc","Bradley APC"}
                },
                Animal = new Dictionary<string, string>()
                {
                    { "bear","Bear" },
                    { "polarbear","PolarBear" },
                    { "wolf","Wolf" },
                    { "wolf2", "Wild wolf"},
                    { "stag","Stag"},
                    { "boar","Boar" },
                    { "chicken","Chicken" },
                    { "horse","Horse"},
                    { "simpleshark","Shark" }
                },
                Weapons = new Dictionary<string, string>()
                {
                    { "Assault Rifle","Ak-47" },
                    { "LR-300 Assault Rifle","LR-300" },
                    { "L96 Rifle","L96" },
                    { "Bolt Action Rifle","Bolt" },
                    { "Semi-Automatic Rifle","Semi-AR" },
                    { "Semi-Automatic Pistol","Semi-AP" },
                    { "Spas-12 Shotgun","Spas-12" },
                    { "M92 Pistol","M92" }
                },
                Npcs = new Dictionary<string, string>()
                {
                    { "scientist","Scientist Npc" }
                }
            };
        }

        protected override void SaveConfig() => Config.WriteObject(configdata);

        #endregion

        #region Data (ProtoBuf)

        [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
        private class SimpleKillFeedData
        {
            public HashSet<ulong> DisabledUsers = new HashSet<ulong>();
        }

        private void LoadData()
        {
            _data = ProtoStorage.Load<SimpleKillFeedData>(nameof(SimpleKillFeed)) ?? new SimpleKillFeedData();
        }

        private void SaveData()
        {
            if (_data == null) return;
            ProtoStorage.Save(_data, nameof(SimpleKillFeed));
        }

        #endregion

        #region ChatCommand

        [ChatCommand("feed")]
        private void ToggleFeed(BasePlayer player)
        {
            if (!_data.DisabledUsers.Contains(player.userID))
            {
                _data.DisabledUsers.Add(player.userID);
                Player.Message(player, _("Disabled", player), null, configdata.IconId);
            }
            else
            {
                _data.DisabledUsers.Remove(player.userID);
                Player.Message(player, _("Enabled", player), null, configdata.IconId);
            }
        }

        #endregion

        #region Oxide Hooks

        private void OnServerInitialized()
        {
            _isClansReborn = Clans != null && Clans.Author.Contains("k1lly0u");
            _isClansIo = Clans != null && Clans.Author.Contains("playrust.io / dcode");
            _isClansMevent = Clans != null && Clans.Author.Contains("mevent");
            foreach (var blueprint in ItemManager.bpList.Where(bp => bp.targetItem.category == ItemCategory.Weapon || bp.targetItem.category == ItemCategory.Tool))
            {
                var md = blueprint.targetItem.GetComponent<ItemModEntity>();
                if (!md)
                    continue;
                if (!_itemNameMapping.ContainsKey(md.entityPrefab.resourceID))
                    _itemNameMapping.Add(md.entityPrefab.resourceID , blueprint.targetItem.displayName.english);
            }
            _holder = new GameObject("SKFHolder");
            UnityEngine.Object.DontDestroyOnLoad(_holder);
            _killQueue = _holder.AddComponent<KillQueue>();
            _killQueue.SetPlugin(this);//Add the plugin to the class
            Pool.ResizeBuffer<KillEvent>(configdata.MaxFeedMessages);
            permission.RegisterPermission(Exclude_Perm , this);
        }
        private void Init() => LoadData();

        private void Unload()
        {
            _killQueue = null;
            UnityEngine.Object.Destroy(_holder);
            _holder = null;
            for (var i = 0; i < configdata.MaxFeedMessages; i++)
                KillQueue.RemoveKillCui($"kf-{i}");
            configdata = null;
            Pool.IPoolCollection value;
            Pool.Directory.TryRemove(typeof(KillEvent), out value);
            SaveData();
            _data = null;
        }

        #endregion

        #region Oxide Hooks (Deaths)

        private void OnEntityDeath(BaseEntity victim , HitInfo hitInfo)
        {
            if (victim == null || victim is BasePlayer) return;
            if (!(victim is BaseAnimalNPC || victim is BaseNPC2 || victim is SnakeHazard) || !IsAnimal(victim)) return;

            BasePlayer attacker = hitInfo.InitiatorPlayer;
            if (attacker == null || attacker.IsNpc || IsAnimal(attacker) || IsZombieHorde(attacker)) return;
            if (!attacker.userID.IsSteamId()) return;
            if (!configdata.EnableAnimalFeed) return;

            // Determine victim name based on its type
            string VictimName;
            if (victim is BaseAnimalNPC animalVictim)
            {
                configdata.Animal.TryGetValue(animalVictim.ShortPrefabName , out VictimName);
            }
            else if (victim is BaseNPC2 npcVictim)
            {
                if (npcVictim.name.Contains("Wolf2"))
                    VictimName = "Wolf2";
                else
                    configdata.Animal.TryGetValue(npcVictim.ShortPrefabName , out VictimName);
            }
            else if (victim is SnakeHazard npcVictimSnake)
            {
                if (victim.OwnerID != 0) return;
                victim.OwnerID = attacker.userID;
                if (!configdata.Animal.TryGetValue(npcVictimSnake.ShortPrefabName , out VictimName))
                    VictimName = "Snake";
            }
            else
            {
                return;
            }
            if (string.IsNullOrEmpty(VictimName))
                VictimName = "Unknown";

            string AttackerName = SanitizeName(GetClan(attacker) + attacker.displayName);
            string WeaponName = GetCustomWeaponName(hitInfo);
            int distance = (int)Vector3.Distance(attacker.transform.position , victim.transform.position);

            string projectileName = hitInfo.ProjectilePrefab?.name;
            if (!string.IsNullOrEmpty(projectileName) && projectileName.StartsWith("ballista" , System.StringComparison.OrdinalIgnoreCase))
                WeaponName = "Ballista";

            _killQueue.OnDeath(attacker , null , string.Format(_("MsgFeedKillAnimalFromPlayer") , AttackerName , VictimName , WeaponName , GetDistanceColor(distance) , distance));
        }

        private void OnEntityDeath(SimpleShark victim, HitInfo hitInfo)
        {
            if (victim == null || victim is BasePlayer || !IsAnimal(victim)) return;
            BasePlayer attacker = hitInfo.InitiatorPlayer;

            if (attacker == null || attacker.IsNpc || IsAnimal(attacker) || IsZombieHorde(attacker)) return;
            if (!configdata.EnableAnimalFeed) return;

            string VictimName = configdata.Animal[victim.ShortPrefabName];
            string AttackerName = SanitizeName(GetClan(attacker) + attacker.displayName);
            string WeaponName = GetCustomWeaponName(hitInfo);
            var distance = (int)Vector3.Distance(attacker.transform.position, victim.transform.position);
            _killQueue.OnDeath(attacker, null, string.Format(_("MsgFeedKillAnimalFromPlayer"), AttackerName, VictimName, WeaponName, GetDistanceColor(distance), distance));

        }

        private void OnPlayerDeath(BasePlayer victim , HitInfo hitInfo)
        {
            try
            {
                if (victim == null) return;

                var wAttacker = victim.lastAttacker?.ToPlayer();

                if (!victim.userID.IsSteamId() && !configdata.EnableNpcFeed) return;

                string projectileName = hitInfo?.ProjectilePrefab?.name;

                if (!string.IsNullOrEmpty(projectileName) && projectileName.Contains("ballista" , StringComparison.OrdinalIgnoreCase))
                {
                    OnBallista(victim);
                    return;
                }

                if (hitInfo?.Initiator is BaseAnimalNPC || hitInfo?.Initiator is BaseNPC2)
                {
                    if (!configdata.EnableAnimal) return;
                    if (hitInfo.Initiator is BaseNPC2 && !configdata.EnableNpcFeed) return;
                    OnKilledByAnimal(hitInfo.Initiator , victim);
                    return;
                }

                if (IsTrap(hitInfo?.Initiator))
                {
                    if (!configdata.EnableEntityFeed) return;
                    OnKilledByEnt(hitInfo.Initiator , victim);
                    return;
                }

                if (victim.lastAttacker is NPCAutoTurret)
                {
                    if (!configdata.EnableEntityFeed) return;
                    OnSentry(victim);
                    return;
                }

                if (victim.lastAttacker?.ToString().Contains("cactus") == true)
                {
                    if (!victim.userID.IsSteamId() && !configdata.EnableNpcFeed) return;
                    OnCactus(victim);
                    return;
                }

                if (!wAttacker?.userID.IsSteamId() == true && !victim.userID.IsSteamId()) return;

                if ((wAttacker?.userID.IsSteamId() == true && !victim.userID.IsSteamId()) && !configdata.EnableNpcFeed) return;

                if (hitInfo == null && (wAttacker != null && victim.IsWounded()))
                {
                    OnDeathFromWounds(wAttacker , victim);
                    return;
                }

                if (victim.lastAttacker?.ToPlayer() == victim)
                {
                    if (!configdata.EnableSuicides) return;
                    OnSuicide(victim);
                    return;
                }

                if (!victim.userID.IsSteamId() && hitInfo?.Initiator is HotAirBalloon) return;

                if (hitInfo?.WeaponPrefab != null && hitInfo.WeaponPrefab.prefabID == 3032863244)
                {
                    if (!configdata.EnableEntityFeed) return;
                    OnKilledByBradley(victim);
                    return;
                }

                if (hitInfo != null)
                {
                    if (IsRadiation(hitInfo))
                    {
                        if (!configdata.EnableRadiationKills) return;
                        OnKilledByRadiation(victim);
                        return;
                    }

                    if (IsFall(hitInfo) && victim.IsWounded())
                    {
                        if (!configdata.EnableFall) return;
                        DeathByFall(victim);
                        return;
                    }

                    if (IsCold(hitInfo))
                    {
                        if (!configdata.EnableCold) return;
                        OnFrozen(victim);
                        return;
                    }

                    if (IsDrowning(hitInfo))
                    {
                        if (!configdata.EnableDrowning) return;
                        OnDrowning(victim);
                        return;
                    }

                    if (IsHunger(hitInfo) || IsThirst(hitInfo) || IsPoison(hitInfo))
                    {
                        if (!configdata.EnableHunger) return;
                        OnHunger(victim);
                        return;
                    }

                    if (IsShock(hitInfo))
                    {
                        if (!configdata.EnableElectricution) return;
                        OnShock(victim);
                        return;
                    }
                }

                int distance = 0;
                if (hitInfo != null)
                {
                    distance = !hitInfo.IsProjectile()
                        ? (int)Vector3.Distance(hitInfo.PointStart , hitInfo.HitPositionWorld)
                        : (int)hitInfo.ProjectileDistance;

                    if (IsCar(hitInfo)) distance = 0;
                }

                var attacker = hitInfo?.InitiatorPlayer;
                if (attacker == null) return;

                if (IsFlame(hitInfo))
                {
                    OnBurned(attacker , victim);
                }
                else if (!IsDrowning(hitInfo))
                {
                    OnKilled(attacker , victim , hitInfo , distance);
                }
                else
                {
                    OnKilled(attacker , victim , null , distance);
                }
            }
            catch
            {
                Puts("something else ?");
                if (hitInfo?.WeaponPrefab != null)
                {
                    uint id = hitInfo.WeaponPrefab.prefabID;
                    if (id == 1913489021 || id == 3871024251 || id == 362061794 || id == 4081160652 || id == 1034606242)
                    {
                        Puts("Catapult source");
                        int distance = !hitInfo.IsProjectile()
                            ? (int)Vector3.Distance(hitInfo.PointStart , hitInfo.HitPositionWorld)
                            : (int)hitInfo.ProjectileDistance;
                        OnKilled(victim.lastAttacker?.ToPlayer() , victim , hitInfo , distance);
                        return;
                    }
                }
            }
        }

        #endregion

        #region LanguageAPI

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string , string>
            {
            { "MsgAttacker", "You killed <color=#ff686b>{0}</color> from {1}m in <color=#ff686b>{2}</color>."},
            {"MsgVictim", "<color=#ff686b>{0}</color> killed you from {1}m with their {2} to <color=#ff686b>{3}</color>."},
            {"MsgFeedKill", "<color=#00ff00>{0}</color> killed <color=#ff686b>{1}</color>, <color=#ff686b>{2}</color>, <color=#ff686b>{3}</color><color={4}>({5}m)</color>"},
            {"MsgFeedKillNpc", "<color=#00ff00>{0}</color> killed <color=#ff686b>{1}</color>, <color={2}>({3}m)</color>"},
            {"MsgFeedKillAnimalFromPlayer", "<color=#00ff00>{0}</color> killed a <color=#ff686b>{1}</color>, <color=#ff686b>{2}</color>, <color={3}>({4}m)</color>"},

            {"MsgAtkWounded", "You wounded <color=#ff686b>{0}</color> till death."},
            {"MsgVictimWounded", "<color=#ff686b>{0}</color> has wounded you till death."},
            {"MsgFeedWounded", "<color=#00ff00>{0}</color> finished <color=#ff686b>{1}</color>"},

            {"MsgAtkBurned", "You burned <color=#ff686b>{0}</color> alive!"},
            {"MsgVictimBurned", "<color=#ff686b>{0}</color> has burned you alive!"},
            {"MsgFeedBurned", "<color=#00ff00>{0}</color> burned <color=#ff686b>{1}</color>!"},

            {"MsgFeedKillBrad", "<color=#ff686b>{0}</color> was killed by a <color=orange>Shell</color>"},
            {"MsgFeedKillEnt", "<color=#ff686b>{0}</color> was killed by <color=orange>{1}</color>"},
            {"MsgFeedKillAnimal", "<color=#ff686b>{0}</color> was killed by <color=orange>{1}</color>"},

            {"MsgFeedKillSuicide", "<color=#ff686b>{0}</color> committed <color=orange>Suicide</color>"},
            {"MsgFeedKillRadiation", "<color=#ff686b>{0}</color> died to <color=orange>Radiation</color>"},
            {"MsgFeedKillFall", "<color=#ff686b>{0}</color> died by <color=orange>Fall</color>"},
            {"MsgFeedKillCold", "<color=#ff686b>{0}</color> died by <color=orange>Cold</color>"},
            {"MsgFeedKillDrowned", "<color=#ff686b>{0}</color> <color=orange>Drowned</color>"},
            {"MsgFeedKillHunger", "<color=#ff686b>{0}</color> <color=orange>Starved</color>"},
            {"MsgFeedKillShock", "<color=#ff686b>{0}</color> got <color=orange>Electrocuted</color>"},
            {"MsgFeedKillSentry", "<color=#ff686b>{0}</color> got killed by <color=orange>Outpost</color>"},
            {"MsgFeedKillCactus", "<color=#ff686b>{0}</color> died to a <color=orange>Cactus</color>"},
            {"MsgFeedKillBallista", "<color=#ff686b>{0}</color> Got pierced by a <color=orange>Ballista</color>"},
            {"MsgFeedKillCatapult", "<color=#ff686b>{0}</color> Got crushed by a <color=orange>Catapult</color>"},

            {"Enabled", "KillFeed Enabled"},
            {"Disabled", "KillFeed Disabled"}
            } , this);

            lang.RegisterMessages(new Dictionary<string , string>
            {
            {"MsgAttacker", "Du hast <color=#ff686b>{0}</color> aus {1}m in <color=#ff686b>{2}</color> getötet."},
            {"MsgVictim", "<color=#ff686b>{0}</color> hat dich aus {1}m mit ihrer {2} zu <color=#ff686b>{3}</color> getötet."},
            {"MsgFeedKill", "<color=#00ff00>{0}</color> hat <color=#ff686b>{1}</color> getötet, <color=#ff686b>{2}</color>, <color=#ff686b>{3}</color><color={4}>({5}m)</color>"},
            {"MsgFeedKillNpc", "<color=#00ff00>{0}</color> hat <color=#ff686b>{1}</color> getötet, <color={2}>({3}m)</color>"},
            {"MsgFeedKillAnimalFromPlayer", "<color=#00ff00>{0}</color> hat ein <color=#ff686b>{1}</color> getötet, <color=#ff686b>{2}</color>, <color={3}>({4}m)</color>"},

            {"MsgAtkWounded", "Du hast <color=#ff686b>{0}</color> bis zum Tod verwundet."},
            {"MsgVictimWounded", "<color=#ff686b>{0}</color> hat dich bis zum Tod verwundet."},
            {"MsgFeedWounded", "<color=#00ff00>{0}</color> hat <color=#ff686b>{1}</color> erledigt."},

            {"MsgAtkBurned", "Du hast <color=#ff686b>{0}</color> lebendig verbrannt!"},
            {"MsgVictimBurned", "<color=#ff686b>{0}</color> hat dich lebendig verbrannt!"},
            {"MsgFeedBurned", "<color=#00ff00>{0}</color> hat <color=#ff686b>{1}</color> verbrannt!"},

            {"MsgFeedKillBrad", "<color=#ff686b>{0}</color> wurde von einer <color=orange>Schale</color> getötet."},
            {"MsgFeedKillEnt", "<color=#ff686b>{0}</color> wurde von <color=orange>{1}</color> getötet."},
            {"MsgFeedKillAnimal", "<color=#ff686b>{0}</color> wurde von <color=orange>{1}</color> getötet."},

            {"MsgFeedKillSuicide", "<color=#ff686b>{0}</color> beging <color=orange>Suizid</color>."},
            {"MsgFeedKillRadiation", "<color=#ff686b>{0}</color> starb an <color=orange>Strahlung</color>."},
            {"MsgFeedKillFall", "<color=#ff686b>{0}</color> starb durch einen <color=orange>Fall</color>."},
            {"MsgFeedKillCold", "<color=#ff686b>{0}</color> starb an <color=orange>Kälte</color>."},
            {"MsgFeedKillDrowned", "<color=#ff686b>{0}</color> <color=orange>Ertrunken</color>."},
            {"MsgFeedKillHunger", "<color=#ff686b>{0}</color> <color=orange>Hungergestorben</color>."},
            {"MsgFeedKillShock", "<color=#ff686b>{0}</color> wurde <color=orange>elektrisiert</color>."},
            {"MsgFeedKillSentry", "<color=#ff686b>{0}</color> wurde von einem <color=orange>Außenposten</color> getötet."},
            {"MsgFeedKillCactus", "<color=#ff686b>{0}</color> starb an einem <color=orange>Kaktus</color>."},
            {"MsgFeedKillBallista", "<color=#ff686b>{0}</color> wurde von einer <color=orange>Ballista</color> durchbohrt"},
            {"MsgFeedKillCatapult", "<color=#ff686b>{0}</color> wurde von einem <color=orange>Katapult</color> zerquetscht"},

            {"Enabled", "KillFeed Aktiviert"},
            {"Disabled", "KillFeed Deaktiviert"}
            } , this , "de");
        }

        #endregion

        #region API for other plugins to use the feed

        private void SendKillfeedmessage(string msg)
        {
            _killQueue.OnDeath(null , null , string.Format(_(msg)));
        }

        #endregion

        #region Kill Events

        private void OnKilled(BasePlayer attacker, BasePlayer victim, HitInfo hitInfo, int dist)
        {
            var HitBone = hitInfo.boneArea.ToString();
            if (HitBone == "-1") HitBone = "Body";
            if (IsCar(hitInfo)) dist = 0;

            if (configdata.EnableChatFeed == true)
            {
                if (!_data.DisabledUsers.Contains(attacker.userID))
                    Player.Message(attacker , _("MsgAttacker" , attacker) , null , configdata.IconId , GetClan(victim) + victim.displayName , dist , HitBone);
                if (!_data.DisabledUsers.Contains(victim.userID))
                    Player.Message(victim , _("MsgVictim" , victim) , null , configdata.IconId , GetClan(attacker) + attacker.displayName , dist , GetCustomWeaponName(hitInfo) , HitBone);
            }

            if ((IsZombieHorde(attacker) || attacker.IsNpc) && configdata.EnableNpcFeed)
            {
                var npc = attacker;
                _killQueue.OnDeath(victim, null, string.Format(_("MsgFeedKillNpc"), CustomNpcName(npc), SanitizeName(GetClan(victim) + victim.displayName), GetDistanceColor(dist), dist));
                return;
            }
            if ((victim.IsNpc || IsZombieHorde(victim)) && configdata.EnableNpcFeed)
            {
                var npc = victim;
                _killQueue.OnDeath(attacker, null, string.Format(_("MsgFeedKill"), SanitizeName(GetClan(attacker) + attacker.displayName), CustomNpcName(npc), GetCustomWeaponName(hitInfo), HitBone, GetDistanceColor(dist), dist));
            }
            else _killQueue.OnDeath(victim, attacker, string.Format(_("MsgFeedKill"), SanitizeName(GetClan(attacker) + attacker.displayName), SanitizeName(GetClan(victim) + victim.displayName), GetCustomWeaponName(hitInfo), HitBone, GetDistanceColor(dist), dist));

            if (!configdata.EnableLogging) return;
            var sfkLog = new StringBuilder($"{DateTime.Now}: ({attacker.UserIDString}){attacker.displayName} killed ({victim.UserIDString}){victim.displayName} from {dist}m in {HitBone}");
            LogToFile("SimpleKillFeed", sfkLog.ToString(), this);
        }

        private void OnSuicide(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillSuicide") , SanitizeName(GetClan(victim) + victim.displayName)));

        private void OnKilledByRadiation(BasePlayer victim) => _killQueue.OnDeath(victim, null, string.Format(_("MsgFeedKillRadiation"), SanitizeName(GetClan(victim) + victim.displayName)));

        private void OnKilledByEnt(BaseEntity attacker , BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillEnt") , SanitizeName(GetClan(victim) + victim.displayName) , CustomEntName(attacker)));

        private void OnKilledByBradley(BasePlayer victim) => _killQueue.OnDeath(victim, null, string.Format(_("MsgFeedKillBrad"), SanitizeName(GetClan(victim) + victim.displayName)));

        private void OnKilledByAnimal(BaseEntity attacker, BasePlayer victim) => _killQueue.OnDeath(victim, null, string.Format(_("MsgFeedKillAnimal"), SanitizeName(GetClan(victim) + victim.displayName), CustomAnimalName(attacker)));

        private void DeathByFall(BasePlayer victim) => _killQueue.OnDeath(victim, null, string.Format(_("MsgFeedKillFall"), SanitizeName(GetClan(victim) + victim.displayName)));

        private void OnDeathFromWounds(BasePlayer attacker, BasePlayer victim)
        {
            if (configdata.EnableChatFeed)
            {
                if (!_data.DisabledUsers.Contains(attacker.userID) && attacker.userID.IsSteamId())
                    Player.Message(attacker, _("MsgAtkWounded", attacker), null, configdata.IconId, GetClan(victim) + victim.displayName);
                if (!_data.DisabledUsers.Contains(victim.userID))
                    Player.Message(victim, _("MsgVictimWounded", victim), null, configdata.IconId, GetClan(attacker) + attacker.displayName);
            }
            _killQueue.OnDeath(victim, attacker, string.Format(_("MsgFeedWounded"), SanitizeName(GetClan(attacker) + attacker.displayName), SanitizeName(GetClan(victim) + victim.displayName)));

            if (!configdata.EnableLogging) return;
            var sfkLog = new StringBuilder($"{DateTime.Now}: ({attacker.UserIDString}){attacker.displayName} finished ({victim.UserIDString}){victim.displayName}");
            LogToFile("SimpleKillFeed", sfkLog.ToString(), this);
        }

        private void OnBurned(BasePlayer attacker, BasePlayer victim)
        {
            if (victim.IsNpc)
            {
                if (!configdata.EnableNpcFeed) return;
                var npc = victim;
                _killQueue.OnDeath(victim, attacker, string.Format(_("MsgFeedBurned"), SanitizeName(GetClan(attacker) + attacker.displayName), CustomNpcName(npc)));
            }

            if (attacker.IsNpc)
            {
                if (!configdata.EnableNpcFeed) return;
                var npc = attacker;
                _killQueue.OnDeath(victim, attacker, string.Format(_("MsgFeedBurned"), CustomNpcName(npc), SanitizeName(GetClan(victim) + victim.displayName)));
            }
            if (configdata.EnableChatFeed)
            {
                if (!_data.DisabledUsers.Contains(attacker.userID))
                    Player.Message(attacker, _("MsgAtkBurned", attacker), null, configdata.IconId, GetClan(victim) + victim.displayName);
                if (!_data.DisabledUsers.Contains(victim.userID))
                    Player.Message(victim, _("MsgVictimBurned", victim), null, configdata.IconId, GetClan(attacker) + attacker.displayName);
            }
            _killQueue.OnDeath(victim, attacker, string.Format(_("MsgFeedBurned"), SanitizeName(GetClan(attacker) + attacker.displayName), SanitizeName(GetClan(victim) + victim.displayName)));

            if (!configdata.EnableLogging) return;
            var sfkLog = new StringBuilder($"{DateTime.Now}: ({attacker.UserIDString}){attacker.displayName} burned ({victim.UserIDString}){victim.displayName}");
            LogToFile("SimpleKillFeed", sfkLog.ToString(), this);
        }

        private void OnDrowning(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillDrowned") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnFrozen(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillCold") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnHunger(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillHunger") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnShock(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillShock") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnSentry(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillSentry") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnCactus(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillCactus") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnBallista(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillBallista") , SanitizeName(GetClan(victim) + victim.displayName)));
        private void OnCatapult(BasePlayer victim) => _killQueue.OnDeath(victim , null , string.Format(_("MsgFeedKillCatapult") , SanitizeName(GetClan(victim) + victim.displayName)));

        #endregion

        #region UI

        private class KillEvent : Pool.IPooled
        {
            public int DisplayUntil;
            public string Text;

            public KillEvent Init(string text, int displayUntil)
            {
                Text = text;
                DisplayUntil = displayUntil;
                return this;
            }

            public void EnterPool()
            {
                Text = null;
                DisplayUntil = 0;
            }

            public void LeavePool() { }
        }

        private class KillQueue : MonoBehaviour
        {
            private readonly WaitForSeconds _secondDelay = new WaitForSeconds(1f);
            private readonly Queue<KillEvent> _queue = new Queue<KillEvent>(configdata.MaxFeedMessages);
            private readonly CuiOutlineComponent _outlineStatic = new CuiOutlineComponent { Distance = configdata.OutlineSize , Color = "0 0 0 1" };
            private readonly CuiRectTransformComponent[] _rectTransformStatic = new CuiRectTransformComponent[configdata.MaxFeedMessages];
            private readonly CuiTextComponent[] _textStatic = new CuiTextComponent[configdata.MaxFeedMessages];
            private readonly CuiElementContainer _cui = new CuiElementContainer();
            private bool _needsRedraw;
            private int _currentlyDrawn;
            private SimpleKillFeed _plugin;

            //add the plugin reference
            public void SetPlugin(SimpleKillFeed plugin)
            {
                _plugin = plugin;
            }


            public void OnDeath(BasePlayer victim , BasePlayer attacker , string text)
            {
                Interface.Call("OnKillFeedMessageReceived" , text);
                if (_queue.Count == configdata.MaxFeedMessages)
                    DequeueEvent(_queue.Dequeue());
                PushEvent(Pool.Get<KillEvent>().Init(text , Epoch.Current + configdata.FeedMessageTtlSec));
            }

            private void PushEvent(KillEvent evt)
            {
                _queue.Enqueue(evt);
                _needsRedraw = true;
                DoProccessQueue();
            }

            private void Start()
            {
                for (var i = 0; i < configdata.MaxFeedMessages; i++)
                {
                    _rectTransformStatic[i] = new CuiRectTransformComponent
                    {
                        AnchorMax =
                            $"{configdata.AnchorMax.Split(Convert.ToChar(' '))[0]} {float.Parse(configdata.AnchorMax.Split(Convert.ToChar(' '))[1]) - (configdata.HeightIdent * i)}" ,
                        AnchorMin =
                            $"{configdata.AnchorMin.Split(Convert.ToChar(' '))[0]} {float.Parse(configdata.AnchorMin.Split(Convert.ToChar(' '))[1]) - (configdata.HeightIdent * i)}"
                    };
                    if (configdata.TextAnchor == "right") _textStatic[i] = new CuiTextComponent { Align = TextAnchor.MiddleRight , FontSize = configdata.FontSize , Text = string.Empty };
                    else if (configdata.TextAnchor == "left") _textStatic[i] = new CuiTextComponent { Align = TextAnchor.MiddleLeft , FontSize = configdata.FontSize , Text = string.Empty };
                    else _textStatic[i] = new CuiTextComponent { Align = TextAnchor.MiddleRight , FontSize = configdata.FontSize , Text = string.Empty };
                }
                StartCoroutine(ProccessQueue());
            }

            private void DequeueEvent(KillEvent evt)
            {
                Pool.Free(ref evt);
                _needsRedraw = true;
            }

            private void DoProccessQueue()
            {
                while (_queue.Count > 0 && _queue.Peek().DisplayUntil < Epoch.Current)
                    DequeueEvent(_queue.Dequeue());

                if (!_needsRedraw)
                    return;
                var toBeRemoved = _currentlyDrawn;
                _currentlyDrawn = 0;
                foreach (var killEvent in _queue)
                {
                    var cuiText = _textStatic[_currentlyDrawn];
                    cuiText.Text = killEvent.Text;
                    _cui.Add(new CuiElement
                    {
                        Name = $"kf-{_currentlyDrawn}" ,
                        Parent = "Under" ,
                        Components =
                        {
                            cuiText,
                            _rectTransformStatic[_currentlyDrawn],
                            _outlineStatic
                        }
                    });
                    if (++_currentlyDrawn == configdata.MaxFeedMessages)
                        break;
                }
                _needsRedraw = false;
                SendKillCui(_cui , toBeRemoved);
                _cui.Clear();
            }

            private IEnumerator ProccessQueue()
            {
                while (!Interface.Oxide.IsShuttingDown)
                {
                    DoProccessQueue();
                    yield return _secondDelay;
                }
            }
            //Updated this method with plugin reference
            private void SendKillCui(CuiElementContainer cui , int toBeRemoved)
            {
                var json = cui.ToJson();
                foreach (var plr in BasePlayer.activePlayerList)
                {
                    // Check for Exclude Permission
                    if (_plugin.permission.UserHasPermission(plr.UserIDString , Exclude_Perm))
                    {
                        // User has the permission, remove existing UI, and skip adding new UI
                        for (var i = toBeRemoved; i > 0; i--)
                            CuiHelper.DestroyUi(plr , $"kf-{i - 1}");
                        continue; // Skip the rest of this loop and go to the next player
                    }

                    //If not excluded remove ui and add ui
                    for (var i = toBeRemoved; i > 0; i--)
                        CuiHelper.DestroyUi(plr , $"kf-{i - 1}");

                    if (!_data.DisabledUsers.Contains(plr.userID))
                        CuiHelper.AddUi(plr , json);

                }
            }

            public static void RemoveKillCui(string name)
            {
                foreach (var plr in BasePlayer.activePlayerList)
                    CuiHelper.DestroyUi(plr , name);
            }
        }
        #endregion

        #region Helpers

        private string _(string msg, BasePlayer player = null) => lang.GetMessage(msg, this, player?.UserIDString);

        private string GetCustomWeaponName(HitInfo hitInfo)
        {
            var name = GetWeaponName(hitInfo);
            if (string.IsNullOrEmpty(name))
                return null;

            string translatedName;
            if (configdata.Weapons.TryGetValue(name, out translatedName))
                return translatedName;

            configdata.Weapons.Add(name, name);
            Config.WriteObject(configdata);
            return name;
        }

        private string CustomNpcName(BasePlayer npc)
        {
            var name = npc.ShortPrefabName;
            if (string.IsNullOrEmpty(name))
                return null;
            if (npc.displayName != npc.userID.ToString())
                return npc.displayName;
            string translatedName;
            if (configdata.Npcs.TryGetValue(name, out translatedName))
                return translatedName;

            configdata.Npcs.Add(name, name);
            Config.WriteObject(configdata);
            return name;
        }

        private string CustomEntName(BaseEntity attacker)
        {
            var name = attacker.ShortPrefabName;
            if (string.IsNullOrEmpty(name))
                return null;
            string translatedName;
            if (configdata.Ents.TryGetValue(name, out translatedName))
                return translatedName;

            configdata.Npcs.Add(name, name);
            Config.WriteObject(configdata);
            return name;
        }

        private string CustomAnimalName(BaseEntity attacker)
        {
            var name = attacker.ShortPrefabName;
            if (string.IsNullOrEmpty(name))
                return null;
            string translatedName;
            if (configdata.Animal.TryGetValue(name, out translatedName))
                return translatedName;

            configdata.Npcs.Add(name, name);
            Config.WriteObject(configdata);
            return name;
        }

        private string GetWeaponName(HitInfo hitInfo)
        {
            string weaponName = "??Unknown??"; // Default Weapon Name

            if (hitInfo == null)
            {
                return weaponName; // Handle null HitInfo immediately
            }

            //Check if the projectile is a Ballista
            if (hitInfo.ProjectilePrefab != null && hitInfo.ProjectilePrefab.name != null && hitInfo.ProjectilePrefab.name.StartsWith("ballista" , System.StringComparison.OrdinalIgnoreCase))
            {
                return "Ballista";
            }
            // Handle cases without a Weapon object
            if (hitInfo.Weapon == null)
            {
                //Check if WeaponPrefab exists, prevent null exception
                if (hitInfo.WeaponPrefab == null)
                {
                    return weaponName;
                }

                //Use the WeaponPrefab to classify explosions and other entities

                ulong prefabID = hitInfo.WeaponPrefab.prefabID; //Get the PrefabID
                switch (prefabID) //Switch instead of if
                {
                    case 3898309212: weaponName = "C4"; break;
                    case 3046924118: weaponName = "Rocket"; break;
                    case 1217937936: weaponName = "HV Rocket"; break;
                    case 2742759844: weaponName = "Satchel"; break;
                    case 2144399804: weaponName = "Beancan"; break;
                    case 1859672190: weaponName = "Shell"; break;
                    case 1128089209: weaponName = "Grenade"; break;
                    case 3717106868: weaponName = "Flamethrower"; break;
                    case 3032863244: weaponName = "Cannon Shell"; break;
                    case 1233562048: weaponName = "Grenade Launcher"; break;
                    default: break; //Leave weaponName as the default
                }

                //Use the initiator when Weapon == null
                if (hitInfo.Initiator != null)
                {
                    if (hitInfo.Initiator is GunTrap) weaponName = "GunTrap";
                    else if (hitInfo.Initiator is FlameTurret) weaponName = "FlameTurret";
                    else if (hitInfo.Initiator is AutoTurret) weaponName = "AutoTurret";
                    else if (hitInfo.Initiator is HotAirBalloon) weaponName = "Hot Air Balloon";
                }
                else if (hitInfo.damageTypes != null && hitInfo.damageTypes.GetMajorityDamageType() == DamageType.Heat) weaponName = "Fire";
                else if (hitInfo.damageTypes != null && hitInfo.damageTypes.GetMajorityDamageType() == DamageType.Fun_Water) weaponName = "WaterGun";

                //Vehicle Check
                if (hitInfo.WeaponPrefab != null && hitInfo.WeaponPrefab.name != null && hitInfo.WeaponPrefab.name.StartsWith("car_" , System.StringComparison.OrdinalIgnoreCase)) weaponName = "Car";
                if (hitInfo.WeaponPrefab.prefabID == 1913489021 || hitInfo.WeaponPrefab.prefabID == 3871024251 || hitInfo.WeaponPrefab.prefabID == 362061794 || hitInfo.WeaponPrefab.prefabID == 4081160652 || hitInfo.WeaponPrefab.prefabID == 1034606242) weaponName = "Catapult";

                    return weaponName; //Return weapon name after all checks

            }
            // Weapon object exists, safe to process

            var item = hitInfo.Weapon.GetItem();
            if (item != null)
                weaponName = item.info.displayName.english;

            // MLRS Check
            if (hitInfo.Weapon is MLRSRocket) weaponName = "MLRS";

            return weaponName; //Return the weapon name
        }
        private static bool IsExplosion(HitInfo hit) => (hit.WeaponPrefab != null && (hit.WeaponPrefab.ShortPrefabName.Contains("grenade") || hit.WeaponPrefab.ShortPrefabName.Contains("explosive")))
                                                        || hit.damageTypes.GetMajorityDamageType() == DamageType.Explosion || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Explosion));

        private static bool IsFlame(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Heat || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Heat));

        private static bool IsRadiation(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Radiation || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Radiation));

        private static bool IsCold(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Cold || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Cold));
        private static bool IsHunger(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Hunger || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Hunger));
        private static bool IsThirst(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Thirst || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Thirst));
        private static bool IsDrowning(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Drowned /* || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Drowned))*/;
        private static bool IsPoison(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Poison || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Poison));
        private static bool IsShock(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.ElectricShock || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.ElectricShock));
        private static bool IsFall(HitInfo hit) => hit.damageTypes.GetMajorityDamageType() == DamageType.Fall || (!hit.damageTypes.IsBleedCausing() && hit.damageTypes.Has(DamageType.Fall));
        private static bool IsCar(HitInfo hit) => hit.WeaponPrefab.ToString().Contains("car_");

        private static bool IsTrap(BaseEntity ent) => ent != null && configdata.Ents.ContainsKey(ent.ShortPrefabName);

        private static bool IsAnimal(BaseEntity animal)
        {
            if (animal == null || animal.ShortPrefabName == null)
                return false;
            if (animal is BaseCorpse)
                return false;
            if (animal.ShortPrefabName == "snake.entity" && animal.OwnerID == 0)
                return configdata.Animal.ContainsKey(animal.ShortPrefabName); // Return config data check instead of false

            return configdata.Animal.ContainsKey(animal.ShortPrefabName);
        }


        private static bool IsZombieHorde(BasePlayer player) => player.GetType().Name.Equals("ZombieNPC");

        private static string GetDistanceColor(int dist)
        {
            foreach (var distanceColor in configdata.DistanceColors)
            {
                if (distanceColor.TestDistance(dist))
                    return distanceColor.Color;
            }
            return configdata.DefaultDistanceColor ?? "white";
        }

        private string GetClan(BasePlayer player)
        {
            if (_isClansReborn || _isClansIo ||_isClansMevent || Clans == null || BetterChat) return null;
            var clan = (string)Clans.Call("GetClanOf", player.UserIDString);
            if (clan == null) return null;
            var format = string.Format("[" + clan + "] ");
            return format;
        }

        private static string SanitizeName(string name)
        {
            if (name.Length > configdata.MaxPlayerNameLength)
                name = name.Substring(0, configdata.MaxPlayerNameLength).Trim();
            return name.Replace("\"", "''");
        }
        #endregion
    }
}
