using Newtonsoft.Json;
using Oxide.Core;
using Rust;
using System.Collections.Generic;
using System.IO;

namespace Oxide.Plugins
{
    [Info("Water Limits", "Razor/Wulf/lukespragg", "3.2.0")]
    [Description("Hurts or kills players that are in water under conditions")]
    public class WaterLimits : CovalencePlugin
    {
        #region Configuration

        private Configuration config;

        public class Configuration
        {
            [JsonProperty(PropertyName = "Hurt player on contact (true/false)")]
            public bool HurtOnContact { get; set; } = false;

            [JsonProperty(PropertyName = "Hurt player on disconnect (true/false)")]
            public bool HurtOnDisconnect { get; set; } = true;

            [JsonProperty(PropertyName = "Hurt player over time (true/false)")]
            public bool HurtOverTime { get; set; } = true;

            [JsonProperty(PropertyName = "Kill player on contact (true/false)")]
            public bool KillOnContact { get; set; } = false;

            [JsonProperty(PropertyName = "Kill player on disconnect (true/false)")]
            public bool KillOnDisconnect { get; set; } = false;

            [JsonProperty(PropertyName = "Damage player amount (1 - 500)")]
            public int DamageAmount { get; set; } = 1;

            [JsonProperty(PropertyName = "Damage player every (seconds)")]
            public int DamageEvery { get; set; } = 10;

            [JsonProperty(PropertyName = "Max Water Depth alowed to sleep")]
            public double maxDepth { get; set; } = 0.6;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null)
                {
                    LoadDefaultConfig();
                }
            }
            catch
            {
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            string configPath = $"{Interface.Oxide.ConfigDirectory}{Path.DirectorySeparatorChar}{Name}.json";
            LogWarning($"Could not load a valid configuration file, creating a new configuration file at {configPath}");
            config = new Configuration();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        #endregion Configuration

        #region Initialization

        private readonly Dictionary<ulong, Timer> timers = new Dictionary<ulong, Timer>();

        private const string permExclude = "waterlimits.exclude";

        private void Init()
        {
            permission.RegisterPermission(permExclude, this);

            if (!config.HurtOnContact && !config.KillOnContact)
            {
                Unsubscribe(nameof(OnRunPlayerMetabolism));
            }
        }

        #endregion Initialization

        #region Water Checking

        private bool IsInWater(BasePlayer player)
        {
            double depth = (double)WaterLevel.GetOverallWaterDepth(player.transform.position, true, false);
#if DEBUG
            LogWarning($"{player.displayName} is {depth} underwater");
#endif
            return depth >= (double)config.maxDepth;
        }

        private void WaterCheck(BasePlayer player)
        {
            if (permission.UserHasPermission(player.UserIDString, permExclude) || !IsInWater(player))
            {
                return;
            }

#if DEBUG
            LogWarning($"{player.displayName} is in water: {IsInWater(player)}");
            LogWarning($"{player.displayName} is {System.Math.Ceiling(player.metabolism.wetness.value)}% wet");
#endif

            if (config.KillOnContact || config.KillOnDisconnect)
            {
                player.Hurt(1000f, DamageType.Drowned, null, false);
            }
            else if (config.HurtOnContact || config.HurtOnDisconnect)
            {
                if (config.HurtOverTime)
                {
                    if (!timers.ContainsKey(player.userID))
                    {
                        timers[player.userID] = timer.Every(config.DamageEvery, () =>
                        {
                            if (player.IsDead() && timers.ContainsKey(player.userID))
                            {
                                timers[player.userID].Destroy();
                            }
                            else
                            {
                                player.Hurt(config.DamageAmount, DamageType.Drowned, null, false);
                            }
                        });
                    }
                }
                else
                {
                    player.Hurt(config.DamageAmount, DamageType.Drowned, null, false);
                }
            }
        }

        #endregion Water Checking

        #region Player Handling

        private void OnServerInitialized()
        {
            foreach (BasePlayer sleeper in BasePlayer.sleepingPlayerList)
            {
                if (sleeper.userID.IsSteamId())
                    WaterCheck(sleeper);
            }
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            if (timers.ContainsKey(player.userID))
            {
                timers[player.userID].Destroy();
            }
        }

        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            WaterCheck(player);
        }

        private void OnRunPlayerMetabolism(PlayerMetabolism metabolism, BasePlayer player, float delta)
        {
            if (player != null)
            {
                WaterCheck(player);
            }
        }

        private void OnPlayerSleepEnded(BasePlayer player)
        {
            if (timers.ContainsKey(player.userID))
            {
                timers[player.userID].Destroy();
            }
        }

        #endregion Player Handling
    }
}
