using GTANetworkAPI;
using Newtonsoft.Json;
using ReallifeGamemode.Server.Entities;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Inventory;
using ReallifeGamemode.Server.Inventory.Interfaces;
using ReallifeGamemode.Server.Models;
using ReallifeGamemode.Server.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

/**
* @overview Life of German Reallife - Managers InventoryManager (InventoryManager.cs)
* @author hydrant, VegaZ
* @copyright (c) 2008 - 2018 Life of German
*/

namespace ReallifeGamemode.Server.Managers
{
    public class InventoryManager : Script
    {
        public static List<IItem> itemList;

        public static void LoadItems()
        {
            itemList = new List<IItem>();

            Type[] allTypes = Assembly.GetExecutingAssembly().GetTypes();
            foreach (Type item in allTypes)
            {
                if (item.GetInterfaces().Contains((typeof(IItem))) && !item.IsAbstract)
                {
                    NAPI.Util.ConsoleOutput($"Loading Item {item.Name}");
                    if (Activator.CreateInstance(item) is IItem o)
                    {
                        if (GetItemById(o.Id) != null)
                        {
                            throw new InvalidOperationException($"Double ItemID found: {o.Id} | {o.Name}");
                        }
                        itemList.Add(o);
                    }
                }
            }
        }

        public static IItem GetItemById(int id)
        {
            return itemList.Find(i => i.Id == id);
        }

        public static IItem GetItemByName(string name)
        {
            return itemList.Find(i => i.Name.ToLower() == name.ToLower());
        }

        public static void RemoveUserItem(User user, UserItem item, int amount)
        {
            using (var dbContext = new DatabaseContext())
            {
                var userItem = dbContext.UserItems.FirstOrDefault(i => i.Id == item.Id);

                userItem.Amount -= amount;

                dbContext.SaveChanges();

                if (userItem.Amount == 0)
                {
                    dbContext.Remove(userItem);
                    dbContext.SaveChanges();
                }
            }
        }

        public static void GetUserItems(Client player)
        {
            var user = player.GetUser();
            var inventoryWeight = 0;

            using (var context = new DatabaseContext())
            {
                List<UserItem> userItems = context.UserItems.ToList().FindAll(i => i.UserId == user.Id);
                string[][] items = new string[userItems.Count][];
                foreach (var item in userItems)
                {
                    IItem iItem = GetItemById(item.ItemId);
                    var currentItemWeight = iItem.Gewicht * item.Amount;
                    inventoryWeight += currentItemWeight;

                    items[userItems.IndexOf(item)] = new string[6];
                    items[userItems.IndexOf(item)][0] = iItem.Name;
                    items[userItems.IndexOf(item)][1] = iItem.Description;
                    items[userItems.IndexOf(item)][2] = iItem.Gewicht.ToString();
                    items[userItems.IndexOf(item)][3] = item.Amount.ToString();
                    items[userItems.IndexOf(item)][4] = item.Slot.ToString();
                    items[userItems.IndexOf(item)][5] = item.Id.ToString();
                }
                player.TriggerEvent("showInventory", inventoryWeight, items);
            }
        }

        public static int GetUserInventoryWeight(Client player)
        {
            var user = player.GetUser();
            var inventoryWeight = 0;

            using (var context = new DatabaseContext())
            {
                List<UserItem> userItems = context.UserItems.ToList().FindAll(i => i.UserId == user.Id);
                foreach (var item in userItems)
                {
                    IItem iItem = GetItemById(item.ItemId);
                    var currentItemWeight = iItem.Gewicht * item.Amount;
                    inventoryWeight += currentItemWeight;
                }
            }

            return inventoryWeight;
        }

        public static UserItem UserHasThisItem(Client player, int itemId)
        {
            var user = player.GetUser();
            using (var context = new DatabaseContext())
            {
                UserItem existingItem = context.UserItems.FirstOrDefault(i => i.UserId == user.Id && i.ItemId == itemId);
                return existingItem;
            }
        }

        public static void GetUserItemsAsAdmin(Client player, Entities.User user)
        {
            var inventoryWeight = 0;

            using (var context = new DatabaseContext())
            {
                List<UserItem> userItems = context.UserItems.ToList().FindAll(i => i.UserId == user.Id);
                string[][] items = new string[userItems.Count][];
                foreach (var item in userItems)
                {
                    IItem iItem = GetItemById(item.ItemId);
                    var currentItemWeight = iItem.Gewicht * item.Amount;
                    inventoryWeight += currentItemWeight;

                    items[userItems.IndexOf(item)] = new string[6];
                    items[userItems.IndexOf(item)][0] = iItem.Name;
                    items[userItems.IndexOf(item)][1] = iItem.Description;
                    items[userItems.IndexOf(item)][2] = iItem.Gewicht.ToString();
                    items[userItems.IndexOf(item)][3] = item.Amount.ToString();
                    items[userItems.IndexOf(item)][4] = item.Slot.ToString();
                    items[userItems.IndexOf(item)][5] = item.Id.ToString();
                }
                player.TriggerEvent("showInventoryToAdmin", user.Name, inventoryWeight, items);
            }
        }

        [RemoteEvent("saveInventory")]
        public void SavePlayerInventory(Client player, string itemArray)
        {

            var user = player.GetUser();
            var items = JsonConvert.DeserializeObject<string[][]>(itemArray);

            using (var context = new DatabaseContext())
            {
                for (var i = 0; i < items.Length; i++)
                {
                    UserItem cItem = new UserItem
                    {
                        Amount = int.Parse(items[i][3]),
                        ItemId = GetItemByName(items[i][0]).Id,
                        UserId = user.Id,
                        Slot = int.Parse(items[i][4]),
                    };

                    if (int.Parse(items[i][5]) == -1)
                    {
                        context.UserItems.Add(cItem);
                    }
                    else if (cItem.Slot == -1)
                    {
                        UserItem fItem = context.UserItems.FirstOrDefault(j => j.Id == int.Parse(items[i][5]));
                        context.UserItems.Remove(fItem);
                    }
                    else
                    {
                        UserItem fItem = context.UserItems.FirstOrDefault(j => j.Id == int.Parse(items[i][5]));
                        fItem.Amount = cItem.Amount;
                        fItem.Slot = cItem.Slot;
                    }
                }
                context.SaveChanges();
                player.TriggerEvent("SERVER:INVENTORY_SYNC");
            }
        }

        public static void AddItemToInventory(Client player, UserItem item)
        {
            var user = player.GetUser();
            using (var context = new DatabaseContext())
            {
                UserItem existingItem = context.UserItems.FirstOrDefault(i => i.ItemId == item.ItemId && i.UserId == item.UserId);
                if (existingItem != null)
                {
                    existingItem.Amount += item.Amount;
                }
                else
                {
                    List<UserItem> allItemsByUser = context.UserItems.ToList().FindAll(i => i.UserId == user.Id);
                    var slotArr = Enumerable.Range(1, 20).ToList();
                    allItemsByUser.ForEach(allItem =>
                    {
                        if (slotArr.Contains(allItem.Slot)) slotArr.Remove(allItem.Slot);
                    });

                    int newSlot = slotArr.Min();

                    item.Slot = newSlot;
                    context.UserItems.Add(item);

                    IItem iItem = GetItemById(item.ItemId);

                    string[] newItem = new string[] { iItem.Name, iItem.Description, iItem.Gewicht.ToString(), item.Amount.ToString(), newSlot.ToString(), item.Id.ToString() };

                    player.TriggerEvent("addItem", JsonConvert.SerializeObject(newItem));
                }
                context.SaveChanges();
            }
        }

        [RemoteEvent("removeItemAsAdmin")]
        public void SavePlayerInventory(Client player, string amount, string userItemId, string targetPlayerName)
        {
            using (var context = new DatabaseContext())
            {
                UserItem fItem = context.UserItems.FirstOrDefault(j => j.Id == int.Parse(userItemId));

                Client targetPlayer = ClientService.GetClientByNameOrId(targetPlayerName);

                if (amount == "stack")
                {
                    var itemSlot = fItem.Slot;
                    targetPlayer.TriggerEvent("removeItem", userItemId, fItem.Amount);
                    context.UserItems.Remove(fItem);
                }
                else if (amount == "one")
                {
                    var itemSlot = fItem.Slot;
                    targetPlayer.TriggerEvent("removeItem", userItemId, 1);
                    fItem.Amount--;
                }
                context.SaveChanges();
            }
        }
        [RemoteEvent("itemInteract")]
        public void ItemInteract(Client player, string type, string itemId, int amount)
        {
            using (var context = new DatabaseContext())
            {
                UserItem fItem = context.UserItems.FirstOrDefault(j => j.Id == int.Parse(itemId));
                IItem iItem = GetItemById(fItem.ItemId);

                switch (type)
                {
                    case "use":
                        ChatService.SendMessage(player, "use item: " + iItem.Name);
                        if (iItem == null)
                        {
                            ChatService.SendMessage(player, "Dieses Essen existiert nicht.");
                            return;
                        }

                        if (fItem == null)
                        {
                            ChatService.SendMessage(player, "Du hast dieses Item nicht");
                            return;
                        }

                        if (iItem is IUsableItem usableItemObj)
                        {
                            usableItemObj.Use(fItem);
                            player.TriggerEvent("removeItem", itemId, amount);
                        }
                        else ChatService.SendMessage(player, "not useable");
                        break;
                    case "drop":

                        if (iItem == null)
                        {
                            ChatService.SendMessage(player, "Dieses Item existiert nicht.");
                            return;
                        }

                        if (fItem == null)
                        {
                            ChatService.SendMessage(player, "Du hast dieses Item nicht");
                            return;
                        }

                        if (iItem is IDroppableItem usableItemObj2)
                        {
                            Vector3 dropPosition = ClientExtension.GetPositionFromPlayer(player, 0.6f, 0);
                            dropPosition.Z -= 0.8f;
                            //new Vector3(player.Position.X, player.Position.Y, player.Position.Z - 0.8f);
                            Random r = new Random();
                            GTANetworkAPI.Object grndObject = NAPI.Object.CreateObject(3777723516, dropPosition, new Vector3(0, 0, r.Next(0, 360)), 0);
                            GroundItem grndItem = new GroundItem { ItemId = iItem.Id, Amount = amount, Position = dropPosition };
                            TextLabel grndTxtLbl = NAPI.TextLabel.CreateTextLabel(iItem.Name + " ~s~(~y~" + amount + "~s~)", dropPosition, 5, 0.5f, 4, new Color(255, 255, 255), false, 0);
                            GroundItem.AddGroundItem(grndItem, grndObject, grndTxtLbl);
                            fItem.Amount -= amount;
                            player.TriggerEvent("removeItem", itemId, amount);
                        }
                        break;
                }
                context.SaveChanges();
            }
        }
    }
}