﻿using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Player Report", "hoppel", "1.0.8")]
    [Description("GUI reporting for players after being killed")]
    public class PlayerReport : RustPlugin
    {
        #region Fields

        [PluginReference]
        private Plugin DiscordMessages;

        private List<string> openUI = new List<string>();
        private HashSet<ulong> cooldowns = new HashSet<ulong>();

        private const string Permname = "playerreport.use";
        private const string Permnameblock = "playerreport.block";

        #endregion Fields

        #region Hooks/Functions

        private void Cooldownhandling(BasePlayer player)
        {
            if (cooldowns.Contains(player.userID))
                return;

            cooldowns.Add(player.userID);
            timer.Once(Cooldown, () => cooldowns.Remove(player.userID));
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            var victim = entity?.ToPlayer();
            var killer = info?.Initiator?.ToPlayer();
            if (victim == null || killer == null || killer == victim)
                return;

            if (!permission.UserHasPermission(victim.UserIDString, Permname) || permission.UserHasPermission(victim.UserIDString, Permnameblock))
                return;

            var killername = killer.displayName.Replace(" ", "_");
            var weaponName = "Unknown";
            if (info.Weapon != null)
            {
                var usedItem = info.Weapon.GetItem();
                if (usedItem != null)
                    weaponName = usedItem.info.displayName.english.Replace(" ", "_");
            }
            var distance = Mathf.Round(info.ProjectileDistance).ToString();
            if (string.IsNullOrEmpty(distance) || cooldowns.Contains(victim.userID))
                return;

            DeathReportUI(victim, killer.UserIDString, distance, killername, weaponName);
            openUI.Add(victim.UserIDString);
        }

        private void OnPlayerRespawned(BasePlayer player)
        {
            if (player == null)
                return;

            if (openUI.Contains(player.UserIDString))
                DestroyUI(player);
        }

        private void Loaded() => storedData = new StoredData();

        private void Unload()
        {
            storedData.Save();
            foreach (var entry in openUI)
            {
                var player = BasePlayer.Find(entry);
                if (player == null)
                    continue;

                DestroyUI(player);
            }
        }

        #endregion Hooks/Functions

        #region Commands

        [ConsoleCommand("ReportHacking")]
        private void cmdReportHacking(ConsoleSystem.Arg arg)
        {
            var killer = arg.Args[0];
            var distance = arg.Args[1];
            var killername = arg.Args[2].Replace("_", " ");
            var weapon = arg.Args[3].Replace("_", " ");
            var player = arg.Player();
            if (player == null)
                return;

            if (!storedData.PlayerInformation.ContainsKey(player.UserIDString))
                storedData.PlayerInformation.Add(player.UserIDString, new ReportInfo { Reported = 0, Reports = 0 });
            if (!storedData.PlayerInformation.ContainsKey(killer))
                storedData.PlayerInformation.Add(killer, new ReportInfo { Reported = 0, Reports = 0 });
            storedData.PlayerInformation[killer].Reported++;
            storedData.PlayerInformation[player.UserIDString].Reports++;
            storedData.Save();
            if (storedData.PlayerInformation[killer].Reported < requiredReports)
            {
                DestroyUI(player);
                return;
            }

            Cooldownhandling(player);
            if (enableTickets)
                player.SendConsoleCommand("ticket create " + msg("Button1Re", null, killer, distance, killername));
            if (enableDiscordMessages)
                DiscordMessage(player.displayName, player.UserIDString, msg("DiscordButton1Field2"), killername, killer, distance, weapon, storedData.PlayerInformation[killer].Reported, storedData.PlayerInformation[killer].Reports);
            if (openUI.Contains(player.UserIDString))
                DestroyUI(player);
        }

        [ConsoleCommand("ReportGroupLimit")]
        private void cmdReportGroupLimit(ConsoleSystem.Arg arg)
        {
            var killer = arg.Args[0];
            var distance = arg.Args[1];
            var killername = arg.Args[2].Replace("_", " ");
            var weapon = arg.Args[3].Replace("_", " ");
            var player = arg.Player();
            if (player == null)
                return;

            if (!storedData.PlayerInformation.ContainsKey(player.UserIDString))
                storedData.PlayerInformation.Add(player.UserIDString, new ReportInfo { Reported = 0, Reports = 0 });
            if (!storedData.PlayerInformation.ContainsKey(killer))
                storedData.PlayerInformation.Add(killer, new ReportInfo { Reported = 0, Reports = 0 });
            storedData.PlayerInformation[killer].Reported++;
            storedData.PlayerInformation[player.UserIDString].Reports++;
            storedData.Save();
            if (storedData.PlayerInformation[killer].Reported < requiredReports)
            {
                DestroyUI(player);
                return;
            }

            Cooldownhandling(player);
            if (enableTickets)
                player.SendConsoleCommand("ticket create " + msg("Button2Re", null, killer, distance, killername));
            if (enableDiscordMessages)
                DiscordMessage(player.displayName, player.UserIDString, msg("DiscordButton2Field2"), killername, killer, distance, weapon, storedData.PlayerInformation[killer].Reported, storedData.PlayerInformation[killer].Reports);
            if (openUI.Contains(player.UserIDString))
                DestroyUI(player);
        }

        [ConsoleCommand("ReportBugAbuse")]
        private void cmdReportBugAbuse(ConsoleSystem.Arg arg)
        {
            var killer = arg.Args[0];
            var distance = arg.Args[1];
            var killername = arg.Args[2].Replace("_", " ");
            var weapon = arg.Args[3].Replace("_", " ");
            var player = arg.Player();
            if (player == null)
                return;

            if (!storedData.PlayerInformation.ContainsKey(player.UserIDString))
                storedData.PlayerInformation.Add(player.UserIDString, new ReportInfo { Reported = 0, Reports = 0 });
            if (!storedData.PlayerInformation.ContainsKey(killer))
                storedData.PlayerInformation.Add(killer, new ReportInfo { Reported = 0, Reports = 0 });
            storedData.PlayerInformation[killer].Reported++;
            storedData.PlayerInformation[player.UserIDString].Reports++;
            storedData.Save();
            if (storedData.PlayerInformation[killer].Reported < requiredReports)
            {
                DestroyUI(player);
                return;
            }

            Cooldownhandling(player);
            if (enableTickets)
                player.SendConsoleCommand("ticket create " + msg("Button3Re", null, killer, distance, killername));
            if (enableDiscordMessages)
                DiscordMessage(player.displayName, player.UserIDString, msg("DiscordButton3Field2"), killername, killer, distance, weapon, storedData.PlayerInformation[killer].Reported, storedData.PlayerInformation[killer].Reports);
            if (openUI.Contains(player.UserIDString))
                DestroyUI(player);
        }

        private void DiscordMessage(string displayName, string userId, string reportFunction, string killerName,
            string killerUserId, string distance, string weapon, int reported, int reports)
        {
            var fields = new List<Fields>();
            fields.Add(new Fields(msg("DiscordButton1FieldName1"), $"[{displayName}](https://steamcommunity.com/profiles/{userId}) ({reports})", true));
            fields.Add(new Fields(msg("DiscordButton1FieldName2"), msg(reportFunction), true));
            fields.Add(new Fields(msg("DiscordButton1FieldName3"), $"[{killerName}](https://steamcommunity.com/profiles/{killerUserId})", true));
            fields.Add(new Fields(msg("DiscordButton1FieldName6"), $"{reported}", true));
            fields.Add(new Fields(msg("DiscordButton1FieldName4"), $"{distance}m", true));
            fields.Add(new Fields(msg("DiscordButton1FieldName5"), $"{weapon}", true));
            var serializedObject = JsonConvert.SerializeObject(fields);
            DiscordMessages?.Call("API_SendFancyMessage", webhookURL, Servername, serializedObject);
            if (Alert)
                DiscordMessages?.Call("API_SendTextMessage", webhookURL, "@here");
        }

        public class Fields
        {
            public string name { get; set; }
            public string value { get; set; }
            public bool inline { get; set; }
            public Fields(string name, string value, bool inline)
            {
                this.name = name;
                this.value = value;
                this.inline = inline;
            }
        }

        #endregion Commands

        #region UI

        private void DeathReportUI(BasePlayer player, string killer, string distance, string killername, string weaponName)
        {
            var container = new CuiElementContainer();
            var ReportUI = container.Add(new CuiPanel
            {
                Image =
                {
                    Color = "0 0 0 0.0",
                },
                RectTransform =
                {
                    AnchorMin = "0.25 0.861",
                    AnchorMax = "0.771 1"
                },
                CursorEnabled = false
            }, "Overlay", "ReportUI");

            if (Button1Enabled)
            {
                container.Add(new CuiButton
                {
                    Button =
                {
                Command = $"global.ReportHacking {killer} {distance} {killername} {weaponName}",
                Color = Button1Color
                },
                    RectTransform =
                {
                AnchorMin = Button1AnchMin,
                AnchorMax = Button1AnchMax
                },
                    Text =
                {
                Text = msg("Button1"),
                    FontSize = 15,
                    Align = TextAnchor.MiddleCenter
                }
                }, ReportUI);
            }

            if (Button2Enabled)
            {
                container.Add(new CuiButton
                {
                    Button =
                {
                Command = $"global.ReportGroupLimit {killer} {distance} {killername} {weaponName}",
                Color = Button2Color
                },
                    RectTransform =
                {
                AnchorMin = Button2AnchMin,
                    AnchorMax = Button2AnchMax
                },
                    Text =
                {
                Text = msg("Button2"),
                    FontSize = 15,
                    Align = TextAnchor.MiddleCenter
                }
                }, ReportUI);
            }

            if (Button3Enabled)
            {
                container.Add(new CuiButton
                {
                    Button =
                {
                Command = $"global.ReportBugAbuse {killer} {distance} {killername} {weaponName}",
                Color = Button3Color
                },
                    RectTransform =
                {
                AnchorMin = Button3AnchMin,
                    AnchorMax = Button3AnchMax
                },
                    Text =
                {
                Text = msg("Button3"),
                    FontSize = 15,
                    Align = TextAnchor.MiddleCenter
                }
                }, ReportUI);
            }

            var Text = container.Add(new CuiPanel
            {
                Image =
                {
                    Color = "0 0 0 0"
                },
                RectTransform =
                {
                    AnchorMin = "0 0.028",
                    AnchorMax = "1 0.290"
                },
                CursorEnabled = false
            }, ReportUI);

            container.Add(new CuiLabel
            {
                Text =
                {
                    Text = msg("InfoMessage"),
                    FontSize = 14,
                    Align = TextAnchor.MiddleCenter,
                    Color = "1 1 1 1"
                },
                RectTransform =
                {
                    AnchorMin = "0 0",
                    AnchorMax = "1 1"
                }
            }, Text);

            CuiHelper.AddUi(player, container);
        }

        private void DestroyUI(BasePlayer player)
        {
            if (player == null)
                return;

            CuiHelper.DestroyUi(player, "ReportUI");
            CuiHelper.DestroyUi(player, "Report_Info");
            openUI.Remove(player.UserIDString);
        }

        #endregion UI

        #region Config

        private int requiredReports = 1;
        private string Servername = "Servername";
        private float Cooldown = 50;
        private string webhookURL = "DISCORD WEBHOOK URL";
        private bool Alert = true;
        private bool enableTickets = true;
        private bool enableDiscordMessages = true;
        private bool Button1Enabled = true;
        private string Button1Color = "0.41 0.5 0.25 1";
        private string Button1AnchMin = "0 0.283";
        private string Button1AnchMax = "0.25 0.75";
        private bool Button2Enabled = true;
        private string Button2Color = "0.12 0.38 0.57 1";
        private string Button2AnchMin = "0.375 0.283";
        private string Button2AnchMax = "0.625 0.75";
        private bool Button3Enabled = true;
        private string Button3Color = "0.57 0.21 0.11 1";
        private string Button3AnchMin = "0.75 0.283";
        private string Button3AnchMax = "1 0.75";

        private new void LoadConfig()
        {
            GetConfig(ref requiredReports, "Settings", "Reports needed to send a Report");
            GetConfig(ref Alert, "Settings", "Enable @here Message in Discord");
            GetConfig(ref Servername, "Settings", "Your servername (for discord messages)");
            GetConfig(ref enableTickets, "Settings", "Enable Ticket Reports");
            GetConfig(ref Cooldown, "Settings", "GUI Cooldown");
            GetConfig(ref webhookURL, "Settings", "Discord Webhook URL");
            GetConfig(ref enableDiscordMessages, "Settings", "Enable DiscordMessages");
            GetConfig(ref Button1Enabled, "Button 1", "Button 1 Enabled");
            GetConfig(ref Button1Color, "Button 1", "Button 1 Color");
            GetConfig(ref Button1AnchMin, "Button 1", "Button 1 AnchorMin");
            GetConfig(ref Button1AnchMax, "Button 1", "Button 1 AnchorMax");
            GetConfig(ref Button2Enabled, "Button 2", "Button 2 Enabled");
            GetConfig(ref Button2Color, "Button 2", "Button 2 Color");
            GetConfig(ref Button2AnchMin, "Button 2", "Button 2 AnchorMin");
            GetConfig(ref Button2AnchMax, "Button 2", "Button 2 AnchorMax");
            GetConfig(ref Button3Enabled, "Button 3", "Button 3 Enabled");
            GetConfig(ref Button3Color, "Button 3", "Button 3 Color");
            GetConfig(ref Button3AnchMin, "Button 3", "Button 3 AnchorMin");
            GetConfig(ref Button3AnchMax, "Button 3", "Button 3 AnchorMax");
            SaveConfig();
        }

        private void Init()
        {
            LoadConfig();
            permission.RegisterPermission(Permname, this);
            permission.RegisterPermission(Permnameblock, this);
        }

        private void GetConfig<T>(ref T variable, params string[] path)
        {
            if (path.Length == 0)
                return;

            if (Config.Get(path) == null)
            {
                Config.Set(path.Concat(new object[] { variable }).ToArray());
            }

            variable = (T)Convert.ChangeType(Config.Get(path), typeof(T));
        }

        protected override void LoadDefaultConfig() => PrintWarning("Generating new configuration file...");

        #endregion Config

        #region Lang

        public string RemoveFormatting(string source)
        {
            return source.Contains(">") ? Regex.Replace(source, "<.*?>", string.Empty) : source;
        }

        public string msg(string key, string id = null, params object[] args)
        {
            string message = id == null ? RemoveFormatting(lang.GetMessage(key, this, id)) : lang.GetMessage(key, this, id);

            return args.Length > 0 ? string.Format(message, args) : message;
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["InfoMessage"] = "Abusing this system will result in a ban!",
                ["Button1"] = "Report: Hacking ",
                ["Button2"] = "Report: Group Limit ",
                ["Button3"] = "Report: Bug Abusing ",
                ["Button1Re"] = "reported {2} ({0}) for hacking distance {1}m",
                ["Button2Re"] = "reported {2} ({0}) for exceeding the Group Limit",
                ["Button3Re"] = "reported {2} ({0}) for Bug Abusing distance {1}m",
                ["DiscordButton1FieldName1"] = "Player",
                ["DiscordButton1FieldName2"] = "Reason",
                ["DiscordButton1FieldName3"] = "Reported",
                ["DiscordButton1FieldName4"] = "Distance",
                ["DiscordButton1FieldName5"] = "Weapon",
                ["DiscordButton1FieldName6"] = "Reported",
                ["DiscordButton1FieldName7"] = "Reporter Reports",
                ["DiscordButton1Field2"] = "Hacking",
                ["DiscordButton2FieldName1"] = "Player",
                ["DiscordButton2FieldName2"] = "Reason",
                ["DiscordButton2FieldName3"] = "Reported",
                ["DiscordButton2FieldName4"] = "Distance",
                ["DiscordButton2FieldName5"] = "Weapon",
                ["DiscordButton2FieldName6"] = "Reported",
                ["DiscordButton2FieldName7"] = "Reporter Reports",
                ["DiscordButton2Field2"] = "Group Limit",
                ["DiscordButton3FieldName1"] = "Player",
                ["DiscordButton3FieldName2"] = "Reason",
                ["DiscordButton3FieldName3"] = "Reported",
                ["DiscordButton3FieldName4"] = "Distance",
                ["DiscordButton3FieldName5"] = "Weapon",
                ["DiscordButton3FieldName6"] = "Reported",
                ["DiscordButton3FieldName7"] = "Reporter Reports",
                ["DiscordButton3Field2"] = "Bug Abuse",
            }, this);
        }

        #endregion Lang

        #region Data

        private StoredData storedData;

        private class StoredData
        {
            public Dictionary<string, ReportInfo> PlayerInformation = new Dictionary<string, ReportInfo>();

            public StoredData()
            {
                Read();
            }

            public void Read() => PlayerInformation = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, ReportInfo>>("PlayerReport");

            public void Save() => Interface.Oxide.DataFileSystem.WriteObject("PlayerReport", PlayerInformation);
        }

        private class ReportInfo
        {
            public int Reports;
            public int Reported;
        }

        #endregion Data
    }
}
