using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/*
https://umod.org/messages/9kgNMQRKRp?page=2#message-12
Trivanus: Hello... I have another question... Could you expand your "Admin Panel" so that when someone becomes an admin, their inventory and clothing/weapons, etc., are automatically removed/disappear, and they then receive separate clothing, a building map, and a hammer. Then, they can do things in admin mode, and when they switch back to player mode, the admin inventory and clothing/weapons, etc., are deleted, and they get back the items they had as a player? There's a plugin like that (https://codefling.com/plugins/admin-toggle), but unfortunately, it's no longer maintained and can't be purchased. Could you add it to your admin panel as a premium feature and offer it for a fee? Or make it available to me for a set price? We offer demolition and other services on my server, and having to empty the inventory and change clothes every time is really annoying. Perhaps it would be a great help to other admins as well... I know there are other plugins that can do this, but we're a community server, and some of them operate in a gray area with Facepunch. They also don't recognize the switch between player and admin roles through the admin panel. It would be a huge help, and I'd be willing to pay for it. The only questions are what you'd like for it and whether updates would be available if needed.
*/

// Kael Discord: add movement arrow
// https://umod.org/community/admin-panel/28638-ability-to-create-custom-buttons
// https://umod.org/community/admin-panel/40834-possible-to-add-night-vision-to-this

namespace Oxide.Plugins
{
    [Info("Admin Panel", "nivex", "1.4.8")]
    [Description("GUI admin panel with command buttons")]
    class AdminPanel : RustPlugin
    {
        [PluginReference]
        private Plugin AdminRadar, Godmode, Vanish, PlayerAdministration;

        private const string permAdminPanel = "adminpanel.allowed";
        private const string permAdminRadar = "adminradar.allowed";
        private const string permGodmode = "godmode.toggle";
        private const string permVanish = "vanish.allow";
        private const string permPlayerAdministration = "playeradministration.access.show";
        private const string permAutoToggle = "adminpanel.autotoggle.admin";

        public Dictionary<ulong, string> playerCUI = new();

        #region Integrations

        public class StoredData
        {
            public Dictionary<string, string> TP = new();
            public StoredData() { }
        }

        public StoredData data = new StoredData();

        #region Player Administration

        private List<string> _playerAdministration = new();

        private bool IsPlayerAdministration(string UserID)
        {
            return PlayerAdministration != null && _playerAdministration.Contains(UserID);
        }

        private void TogglePlayerAdministration(BasePlayer player)
        {            
            if (PlayerAdministration == null || !PlayerAdministration.IsLoaded) return;

            if (IsPlayerAdministration(player.UserIDString))
            {
                player.Command("playeradministration.closeui", player, "playeradministration.closeui", new string[0]);
            }
            else
            {
                player.Command("padmin", player, "padmin", new string[0]);
            }
        }

        private void OnPlayerCommand(BasePlayer player, string command, string[] args)
        {
            if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration))
            {
                if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString))
                {
                    _playerAdministration.Add(player.UserIDString);
                    if (CanToggle(player)) AdminGui(player);
                }
                else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString))
                {
                    _playerAdministration.Remove(player.UserIDString);
                    if (CanToggle(player)) AdminGui(player);
                }
            }
        }

        private void OnServerCommand(ConsoleSystem.Arg arg)
        {
            //Puts(arg.cmd.FullName);

            var player = arg.Player();

            if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration))
            {
                string command = arg.cmd.FullName.Replace("/", string.Empty);
                
                if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString))
                {
                    _playerAdministration.Add(player.UserIDString);
                    if (CanToggle(player)) AdminGui(player);
                }
                else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString))
                {
                    _playerAdministration.Remove(player.UserIDString);
                    if (CanToggle(player)) AdminGui(player);
                }
            }
        }

        #endregion Player Administration

        #region Godmode

        private bool IsGod(string UserID)
        {
            return Godmode != null && Convert.ToBoolean(Godmode?.Call("IsGod", UserID));
        }

        private void ToggleGodmode(BasePlayer player)
        {
            if (Godmode == null || !Godmode.IsLoaded) return;

            if (IsGod(player.UserIDString))
                Godmode.Call("DisableGodmode", player.IPlayer);
            else
                Godmode.Call("EnableGodmode", player.IPlayer);

            if (CanToggle(player)) AdminGui(player);
        }

        private void OnGodmodeToggle(string playerId, bool state)
        {
            var player = RustCore.FindPlayerByIdString(playerId);

            if (player.IsValid() && player.IsConnected && CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        #endregion Godmode

        #region Vanish

        private bool IsInvisible(BasePlayer player)
        {
            return Vanish != null && Convert.ToBoolean(Vanish?.Call("IsInvisible", player));
        }

        private void ToggleVanish(BasePlayer player)
        {
            if (Vanish == null || !Vanish.IsLoaded) return;

            if (!IsInvisible(player))
                Vanish.Call("Disappear", player);
            else
                Vanish.Call("Reappear", player);

            if (CanToggle(player)) AdminGui(player);
        }

        private void OnVanishDisappear(BasePlayer player)
        {
            if (player.IsValid() && player.IsConnected && CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        private void OnVanishReappear(BasePlayer player)
        {
            if (player.IsValid() && player.IsConnected && CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        #endregion Vanish

        #region Admin Radar

        private bool IsRadar(string id)
        {
            return AdminRadar != null && Convert.ToBoolean(AdminRadar?.Call("IsRadar", id));
        }

        private void ToggleRadar(BasePlayer player)
        {
            if (AdminRadar == null || !AdminRadar.IsLoaded) return;

            if (AdminRadar.Version < new Core.VersionNumber(5, 0, 0)) AdminRadar.Call("cmdESP", player, "radar", new string[0]);
            else if (player.IPlayer != null) AdminRadar.Call("RadarCommand", player.IPlayer, "radar", new string[0]);
            if (CanToggle(player)) AdminGui(player);
        }

        private void OnRadarActivated(BasePlayer player)
        {
            if (player.IsValid() && player.IsConnected && CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        private void OnRadarDeactivated(BasePlayer player)
        {
            if (player.IsValid() && player.IsConnected && CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        #endregion Admin Radar

        #endregion Integrations

        private void Init()
        {
            permission.RegisterPermission(permAdminPanel, this);
            permission.RegisterPermission(permAutoToggle, this);
            Unsubscribe(nameof(OnPlayerSleepEnded));
            Unsubscribe(nameof(OnPlayerDeath));
        }

        private void OnUserGroupRemoved(string id, string group)
        {
            if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle))
            {
                return;
            }

            var player = BasePlayer.FindAwakeOrSleeping(id);

            if (player == null || !player.IsConnected)
            {
                return;
            }

            DestroyUI(player);
        }

        private void OnUserGroupAdded(string id, string group)
        {
            if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle))
            {
                return;
            }

            var player = BasePlayer.FindAwakeOrSleeping(id);

            if (player == null || !player.IsConnected || !IsAllowed(player, permAdminPanel))
            {
                return;
            }

            AdminGui(player);
        }

        public bool CanToggle(BasePlayer player) => !config.ToggleMode || playerCUI.ContainsKey(player.userID);

        #region Localization

        private new void LoadDefaultMessages()
        {
            // English
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["AdminTP"] = "Teleport",
                ["Godmode"] = "God",
                ["Radar"] = "Radar",
                ["Vanish"] = "Vanish",
                ["NewTP"] = "NewTP",
                ["PA"] = "Player Administration",
                ["Syntax"] = "Invalid syntax: /{0} {1}",
                ["No Custom Location Set"] = "You do not have a custom location set.",
                ["Removed Custom Location"] = "Removed your custom TP coordinates. You will teleport to the admin location instead.",
                ["Set Custom TP Coordinates"] = "Your TP coordinates set to current position {0}",
                ["Set Admin TP Coordinates"] = "Admin zone coordinates set to current position {0}",
                ["Panel Shown"] = "Admin panel refreshed/shown",
                ["Panel Hidden"] = "Admin panel hidden",
                ["Usage"] = "Usage: /{0} show/hide/settp/removetp",
            }, this);

            // Spanish
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["AdminTP"] = "Teleport",
                ["Godmode"] = "Dios",
                ["Radar"] = "Radar",
                ["Vanish"] = "Desaparecer",
                ["NewTP"] = "NewTP",
            }, this, "es");

            // French
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["AdminTP"] = "Teleport",
                ["Godmode"] = "Dieu",
                ["Radar"] = "Radar",
                ["Vanish"] = "Invisible",
                ["NewTP"] = "NewTP",
            }, this, "fr");
        }

        #endregion Localization

        #region Hooks

        private void OnPlayerSleepEnded(BasePlayer player)
        {
            if (CanToggle(player) && IsAllowed(player, permAdminPanel))
            {
                AdminGui(player);
            }
        }

        private void OnPlayerDeath(BasePlayer player)
        {
            DestroyUI(player);
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            if (plugin.Name == "AdminRadar" || plugin.Name == "Godmode" || plugin.Name == "Vanish" || plugin.Name == "PlayerAdministration")
            {
                RefreshAllUI();
            }
        }

        private void OnPluginUnloaded(Plugin plugin)
        {
            if (plugin.Name == "AdminRadar" || plugin.Name == "Godmode" || plugin.Name == "Vanish" || plugin.Name == "PlayerAdministration")
            {
                RefreshAllUI();
            }
        }

        private void OnServerInitialized()
        {
            try
            {
                data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
            }
            catch
            {

            }

            if (data == null)
            {
                data = new StoredData();
                SaveData();
            }

            Subscribe(nameof(OnPlayerDeath));

            if (!config.ToggleMode)
            {
                Subscribe(nameof(OnPlayerSleepEnded));
                RefreshAllUI();
            }
        }

        private void SaveData()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, data, true);
        }

        private void RefreshAllUI()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                if (IsAllowed(player, permAdminPanel) && CanToggle(player))
                {
                    AdminGui(player);
                }
            }
        }

        #endregion Hooks

        #region Command Structure

        [ConsoleCommand("adminpanel")]
        private void ccmdAdminPanel(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null || !IsAllowed(player, permAdminPanel) || !arg.HasArgs()) return;
            
            switch (arg.GetString(0).ToLower())
            {
                case "action":
                    {
                        if (arg.HasArgs(2))
                        {
                            switch (arg.GetString(1).ToLower())
                            {
                                case "vanish":
                                    ToggleVanish(player);
                                    break;
                                case "radar":
                                    ToggleRadar(player);
                                    break;
                                case "god":
                                    ToggleGodmode(player);
                                    break;
                                case "pa":
                                    TogglePlayerAdministration(player);
                                    break;
                                case "admintp":
                                    if (data.TP.ContainsKey(player.UserIDString))
                                    {
                                        var pos = data.TP[player.UserIDString].ToVector3();
                                        player.Teleport(pos);
                                    }
                                    else
                                    {
                                        player.Teleport(config.adminZoneCords);
                                    }
                                    break;
                                case "newtp":
                                    if (config.newtp.enabled)
                                    {
                                        string[] argu = new string[1];
                                        argu[0] = "settp";
                                        ccmdAdminPanel(player, null, argu);
                                    }
                                    break;
                                default:
                                    arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp"));
                                    break;
                            }
                        }
                        else
                        {
                            arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp"));
                        }

                        break;
                    }
                case "toggle":
                    {
                        if (IsAllowed(player, permAdminPanel))
                        {
                            if (playerCUI.ContainsKey(player.userID))
                            {
                                DestroyUI(player);
                            }
                            else
                            {
                                AdminGui(player);
                            }
                        }
                        break;
                    }
                default:
                    {
                        arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action/toggle"));
                        break;
                    }
            }
        }

        [ChatCommand("adminpanel")]
        private void ccmdAdminPanel(BasePlayer player, string command, string[] args) // TODO: Make universal command
        {
            if (!IsAllowed(player, permAdminPanel))
            {
                SendReply(player, $"Unknown command: {command}");
                return;
            }

            if (args.Length == 0)
            {
                Message(player, "Usage", command);
                return;
            }

            switch (args[0].ToLower())
            {
                case "hide":
                    DestroyUI(player);
                    Message(player, "Panel Hidden");
                    break;

                case "show":
                    AdminGui(player);                    
                    Message(player, "Panel Shown");
                    break;

                case "settp":
                    Vector3 coord = player.transform.position;
                    if (args.Any(arg => arg.ToLower() == "all"))
                    {
                        config.adminZoneCords = coord;
                        SaveConfig();
                        Message(player, "Set Admin TP Coordinates", coord);
                    }
                    else
                    {
                        data.TP[player.UserIDString] = coord.ToString();
                        SaveData();
                        Message(player, "Set Custom TP Coordinates", coord);
                    }
                    break;

                case "removetp":
                    if (data.TP.Remove(player.UserIDString))
                    {
                        Message(player, "Removed Custom Location");
                        SaveData();
                    }
                    else Message(player, "No Custom Location Set");
                    break;

                default:
                    Message(player, "Syntax", command, args[0]);
                    break;
            }
        }

        #endregion Command Structure

        #region GUI Panel

        private void AdminGui(BasePlayer player)
        {
            NextTick(() =>
            {
                // Destroy existing UI
                DestroyUI(player);

                var BTNColorVanish = config.btnInactColor;
                var BTNColorGod = config.btnInactColor;
                var BTNColorRadar = config.btnInactColor;
                var BTNColorNewTP = config.newtp.color;
                var BTNColorPA = config.btnInactColor;

                if (AdminRadar != null && IsRadar(player.UserIDString)) BTNColorRadar = config.btnActColor;
                if (Godmode != null && IsGod(player.UserIDString)) BTNColorGod = config.btnActColor;
                if (Vanish != null && IsInvisible(player)) BTNColorVanish = config.btnActColor;
                if (_playerAdministration.Contains(player.UserIDString)) BTNColorPA = config.btnActColor;

                var GUIElement = new CuiElementContainer();

                var GUIPanel = GUIElement.Add(new CuiPanel
                {
                    Image =
                    {
                        Color = "1 1 1 0"
                    },
                    RectTransform =
                    {
                        AnchorMin = config.PanelPosMin,
                        AnchorMax = config.PanelPosMax
                    },
                    CursorEnabled = false
                }, "Hud", Name);

                if (AdminRadar != null && permission.UserHasPermission(player.UserIDString, permAdminRadar))
                {
                    GUIElement.Add(new CuiButton
                    {
                        Button =
                        {
                            Command = "adminpanel action radar",
                            Color = BTNColorRadar
                        },
                        Text =
                        {
                            Text = _("Radar", player.UserIDString),
                            FontSize = config.fontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.062 0.21",
                            AnchorMax = "0.51 0.37"
                        }
                    }, GUIPanel);
                }

                if (PlayerAdministration != null && permission.UserHasPermission(player.UserIDString, permPlayerAdministration))
                {
                    GUIElement.Add(new CuiButton
                    {
                        Button =
                        {
                            Command = "adminpanel action pa",
                            Color = BTNColorPA
                        },
                        Text =
                        {
                            Text = _("PA", player.UserIDString),
                            FontSize = config.fontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.062 0.39",
                            AnchorMax = "0.51 0.555"
                        }
                    }, GUIPanel);
                }

                GUIElement.Add(new CuiButton
                {
                    Button =
                    {
                        Command = "adminpanel action admintp",
                        Color = "1.28 0 1.28 0.3"
                    },
                    Text =
                    {
                        Text = _("AdminTP", player.UserIDString),
                        FontSize = config.fontSize,
                        Align = TextAnchor.MiddleCenter,
                        Color = "1 1 1 1"
                    },
                    RectTransform =
                    {
                        AnchorMin = "0.52 0.21",
                        AnchorMax = "0.95 0.37"
                    }
                }, GUIPanel);

                if (config.newtp.enabled)
                {
                    GUIElement.Add(new CuiButton
                    {
                        Button =
                        {
                            Command = "adminpanel action newtp",
                            Color = BTNColorNewTP
                        },
                        Text =
                        {
                            Text = _("newTP", player.UserIDString),
                            FontSize = config.fontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.52 0.39",
                            AnchorMax = "0.95 0.47"
                        }
                    }, GUIPanel);
                }

                if (Godmode != null && permission.UserHasPermission(player.UserIDString, permGodmode))
                {
                    GUIElement.Add(new CuiButton
                    {
                        Button =
                        {
                            Command = "adminpanel action god",
                            Color = BTNColorGod
                        },
                        Text =
                        {
                            Text = _("Godmode", player.UserIDString),
                            FontSize = config.fontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.52 0.02",
                            AnchorMax = "0.95 0.19"
                        }
                    }, GUIPanel);
                }

                if (Vanish != null && permission.UserHasPermission(player.UserIDString, permVanish))
                {
                    GUIElement.Add(new CuiButton
                    {
                        Button =
                        {
                            Command = "adminpanel action vanish",
                            Color = BTNColorVanish
                        },
                        Text =
                        {
                            Text = _("Vanish", player.UserIDString),
                            FontSize = config.fontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.062 0.02",
                            AnchorMax = "0.51 0.19"
                        }
                    }, GUIPanel);
                }

                CuiHelper.AddUi(player, GUIElement);
                playerCUI.Add(player.userID, GUIPanel);
            });
        }

        private void Unload()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyUI(player);
            }
        }

        private void DestroyUI(BasePlayer player)
        {
            if (playerCUI.Remove(player.userID, out var cuiElement))
            {
                CuiHelper.DestroyUi(player, cuiElement);
            }
        }

        #endregion GUI Panel

        #region Helpers

        private bool IsAllowed(BasePlayer player, string perm) => player != null && permission.UserHasPermission(player.UserIDString, perm);

        private string _(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);

        private void Message(BasePlayer player, string key, params object[] args) => Player.Message(player, _(key, player.UserIDString, args));

        #endregion Helpers

        #region Configuration

        private Configuration config;

        public class ButtonState
        {
            [JsonProperty(PropertyName = "Button Enabled")]
            public bool enabled { get; set; }

            [JsonProperty(PropertyName = "Button Color")]
            public string color { get; set; }
        }

        public class Configuration
        {
            [JsonProperty(PropertyName = "newtp")]
            public ButtonState newtp { get; set; } = new ButtonState
            {
                enabled = false,
                color = "1.0 0.65 0.85 0.3"
            };

            [JsonProperty(PropertyName = "Admin Zone Coordinates")]
            public Vector3 adminZoneCords { get; set; }

            [JsonProperty(PropertyName = "Button Active Color")]
            public string btnActColor { get; set; } = "0 2.55 0 0.3";

            [JsonProperty(PropertyName = "Button Inactive Color")]
            public string btnInactColor { get; set; } = "2.55 0 0 0.3";

            [JsonProperty(PropertyName = "Font Size")]
            public int fontSize { get; set; } = 8;

            [JsonProperty(PropertyName = "Panel Pos Max")]
            public string PanelPosMax { get; set; } = "0.991 0.87";

            [JsonProperty(PropertyName = "Panel Pos Min")]
            public string PanelPosMin { get; set; } = "0.9 0.7";

            [JsonProperty(PropertyName = "Toggle Mode")]
            public bool ToggleMode { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            canSaveConfig = false;
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
                canSaveConfig = true;
                SaveConfig();
            }
            catch (Exception ex)
            {
                Puts(ex.ToString());
                LoadDefaultConfig();
            }
        }

        private bool canSaveConfig = true;

        protected override void SaveConfig()
        {
            if (canSaveConfig)
            {
                Config.WriteObject(config);
            }
        }

        protected override void LoadDefaultConfig() => config = new Configuration();

        #endregion
    }
}
