using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("IndustrialRecycler", "Marte6", "1.0.0")]
    [Description("IndustrialRecycler")]
    class IndustrialRecycler : RustPlugin
    {
        private Configuration _config;

        private const string RecyclerPrefab = "assets/bundled/prefabs/static/recycler_static.prefab";
        private const string StorageInPrefab = "assets/content/vehicles/boats/rowboat/subents/rowboat_storage.prefab";
        private const string StorageOutPrefab = "assets/prefabs/deployable/hot air balloon/subents/hab_storage.prefab";
        private const string AdaptorPrefab = "assets/prefabs/deployable/playerioents/industrialadaptors/storageadaptor.deployed.prefab";
        private const string SmallGeneratorPrefab = "electric.generator.small";
        private const ulong RecyclerSkinID = 3341430953;

        private void OnServerInitialized()
        {
            var recyclers = UnityEngine.Object.FindObjectsOfType<Recycler>();
            foreach (var recycler in recyclers)
            {
                if (recycler.OwnerID != 0)
                {
                    recycler.gameObject.AddComponent<RecyclerComponent>();
                }
            }
        }

        private void Init()
        {
            RegisterCommands();
        }

        private void Unload()
        {
            var recyclerComponents = UnityEngine.Object.FindObjectsOfType<RecyclerComponent>();
            foreach (var recycler in recyclerComponents)
            {
                UnityEngine.Object.Destroy(recycler);
            }
        }

        private object OnEntityKill(Recycler entity)
        {
            Puts($"OnEntityKill works!: {entity}");
            return null;
        }

        private void OnEntityBuilt(Planner plan, GameObject obj)
        {
            var player = plan?.GetOwnerPlayer();
            var entity = obj.GetComponent<BaseEntity>();

            if (player == null || entity == null || entity.ShortPrefabName != "generator.small" || entity.skinID != RecyclerSkinID)
                return;

            if (!DeployRecycler(player, entity))
            {
                player.ChatMessage("Must be placed on Construction");
                timer.Once(1, () => GiveRecyclerItem(player));
            }

            NextTick(() => entity?.Kill());
        }

        private void OnItemAddedToContainer(ItemContainer container, Item item)
        {
            if (container == null || container.entityOwner == null)
                return;

            var recycler = container.entityOwner as Recycler;
            if (recycler != null && recycler.OwnerID != 0UL)
            {
                if (item.position < 6)
                {
                    recycler.StartRecycling();
                }
                else
                {
                    var recyclerComponent = recycler.GetComponentInParent<RecyclerComponent>();
                    if (!item.MoveToContainer(recyclerComponent.OutputContainer.inventory))
                    {
                        recycler.StopRecycling();
                    }
                }
            }
        }

        private void RegisterCommands()
        {
            foreach (var command in _config.Commands)
            {
                cmd.AddChatCommand(command, this, nameof(CmdTest));
            }
        }

        private void CmdTest(BasePlayer player, string command, string[] args)
        {
            GiveRecyclerItem(player);
        }

        private bool DeployRecycler(BasePlayer player, BaseEntity entity)
        {
            var buildingBlock = GetBuildingBlock(entity);
            if (buildingBlock == null)
                return false;

            var recycler = SpawnEntity(RecyclerPrefab, buildingBlock, player);
            var storageIn = SpawnEntity(StorageInPrefab, recycler, player, new Vector3(-1.0f, 0.72f, 0.31f), new Vector3(90, 90, 180));
            var adaptorIn = SpawnEntity(AdaptorPrefab, storageIn, player, new Vector3(0.00f, -0.15f, -0.25f), new Vector3(180, 90, 270));

            var storageOut = SpawnEntity(StorageOutPrefab, recycler, player, new Vector3(-0.9f, 0.72f, -0.09f), new Vector3(180, 90, 180));
            var adaptorOut = SpawnEntity(AdaptorPrefab, storageOut, player, new Vector3(0.00f, 0.26f, -0.05f), new Vector3(0, 90, 0));

            recycler.gameObject.AddComponent<RecyclerComponent>();
            return true;
        }

        private BaseEntity SpawnEntity(string prefab, BaseEntity parent, BasePlayer player, Vector3? position = null, Vector3? rotation = null)
        {
            var entity = GameManager.server.CreateEntity(prefab);
            entity.OwnerID = player.userID;
            entity.SetParent(parent, true, true);
            entity.transform.localPosition = position ?? Vector3.zero;
            entity.transform.localRotation = Quaternion.Euler(rotation ?? Vector3.zero);
            entity.Spawn();
            entity.SendNetworkUpdateImmediate();
            return entity;
        }

        private BuildingBlock GetBuildingBlock(BaseEntity entity)
        {
            if (Physics.Raycast(entity.transform.position + Vector3.up * 0.1f, Vector3.down, out RaycastHit hit, 4f, LayerMask.GetMask("Construction")))
            {
                return hit.GetEntity() as BuildingBlock;
            }

            return null;
        }

        private void GiveRecyclerItem(BasePlayer player)
        {
            var item = ItemManager.CreateByName(SmallGeneratorPrefab, 1, RecyclerSkinID);
            item.name = "Recycler";
            player.inventory.GiveItem(item);
        }

        private class Configuration
        {
            [JsonProperty("Commands")]
            public List<string> Commands { get; set; }

            [JsonProperty("Version")]
            public VersionNumber Version { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            try
            {
                _config = Config.ReadObject<Configuration>() ?? throw new Exception();

                if (_config.Version < Version)
                {
                    UpdateConfigValues();
                }

                SaveConfig();
            }
            catch
            {
                PrintError("There is an error in your configuration file. Default values will be used.");
                LoadDefaultConfig();
            }
        }

        protected override void LoadDefaultConfig()
        {
            _config = CreateDefaultConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(_config);
        }

        private void UpdateConfigValues()
        {
            PrintWarning("Configuration change detected! Updating settings.");
            _config.Version = Version;
            PrintWarning("Configuration update completed!");
        }

        private Configuration CreateDefaultConfig()
        {
            return new Configuration
            {
                Commands = new List<string> { "teste", "testee", "testeeee" },
            };
        }

        private class RecyclerComponent : MonoBehaviour
        {
            public Recycler Recycler { get; private set; }
            public StorageContainer InputContainer { get; private set; }
            public StorageContainer OutputContainer { get; private set; }

            private void Awake()
            {
                Recycler = GetComponent<Recycler>();
                InputContainer = Recycler.children.OfType<StorageContainer>().FirstOrDefault(c => c.name == StorageInPrefab);
                OutputContainer = Recycler.children.OfType<StorageContainer>().FirstOrDefault(c => c.name == StorageOutPrefab);

                if (InputContainer == null || OutputContainer == null)
                {
                    Destroy(this);
                }

                InvokeRepeating(nameof(MoveItems), 0f, 10f);
            }

            private void MoveItems()
            {
                int targetSlot = 0;
                var inputItems = InputContainer.inventory.itemList.ToList();
                foreach (var item in inputItems)
                {
                    if (targetSlot >= 6)
                        break;

                    if (Recycler.inventory.GetSlot(targetSlot) == null)
                    {
                        item.MoveToContainer(Recycler.inventory, targetSlot);
                        targetSlot++;
                    }
                }
            }

            private void OnDestroy()
            {
                CancelInvoke(nameof(MoveItems));
            }
        }
    }
}