using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using GTANetworkAPI;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using ReallifeGamemode.Database;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Business;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Services;
using ReallifeGamemode.Server.Util;

namespace ReallifeGamemode.Server.Managers
{
  internal class BusinessManager : Script
  {
    public static List<BusinessBase> Businesses { get; private set; }

    public static void LoadBusinesses()
    {
      Businesses = new List<BusinessBase>();

      IEnumerable<Type> allTypes = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsClass && !type.IsAbstract && type.IsSubclassOf(typeof(BusinessBase)));
      foreach (Type item in allTypes)
      {
        NAPI.Util.ConsoleOutput($"Loading Business {item.Name}");
        if (Activator.CreateInstance(item) is BusinessBase o)
        {
          if (GetBusiness(o.Id) != null)
          {
            throw new InvalidOperationException($"Double Business ID found: {o.Id} | {o.Name}");
          }
          Businesses.Add(o);
          o.Setup();
          o.Load();
          o.Update();
        }
      }
    }

    public static T GetBusiness<T>() where T : BusinessBase
    {
      return (T)Businesses.Find(b => b.GetType() == typeof(T));
    }

    public static BusinessBase GetBusiness(int? id)
    {
      return Businesses.Find(b => b.Id == id);
    }

    public static BusinessBase GetNearBusiness(Player player)
    {
      return Businesses.Where(b => b.Position.DistanceTo(player.Position) < 5f).FirstOrDefault();
    }

    [RemoteEvent("Business_DepositMoney")]
    public void BusinessDepositMoney(Player player, int amount)
    {
      using (var dbContext = new DatabaseContext())
      {
        User user = player.GetUser(dbContext);
        if (user == null)
        {
          return;
        }

        BusinessBase playerBusiness = GetBusiness(user.BusinessId);

        TransactionResult result = BankManager.TransferMoney(user, playerBusiness, amount, "Überweisung", dbContext);

        if (result == TransactionResult.NEGATIVE_MONEY_SENT)
        {
          player.SendNotification("~r~Es können nur positive Beträge überwiesen werden");
          return;
        }
        else if (result == TransactionResult.SENDER_NOT_ENOUGH_MONEY)
        {
          player.SendNotification("~r~Du hast nicht genug Geld");
          return;
        }
        else if (result == TransactionResult.SUCCESS)
        {
          playerBusiness.SendBusinessDataToPlayer(player);
          playerBusiness.Update();
          player.SendNotification("~g~Du hast erfolgreich ~s~" + amount.ToMoneyString() + " ~g~ überwiesen");
          return;
        }
      }
    }

    [RemoteEvent("Business_WithdrawMoney")]
    public void BusinessWithdrawMoney(Player player, int amount)
    {
      using (var dbContext = new DatabaseContext())
      {
        User user = player.GetUser(dbContext);
        if (user == null)
        {
          return;
        }

        BusinessBase playerBusiness = GetBusiness(user.BusinessId);

        TransactionResult result = BankManager.TransferMoney(playerBusiness, user, amount, "Überweisung", dbContext);

        if (result == TransactionResult.NEGATIVE_MONEY_SENT)
        {
          player.SendNotification("~r~Es können nur positive Beträge überwiesen werden");
          return;
        }
        else if (result == TransactionResult.SENDER_NOT_ENOUGH_MONEY)
        {
          player.SendNotification("~r~Es ist nicht genug Geld auf der Businesskasse vorhanden");
          return;
        }
        else if (result == TransactionResult.SUCCESS)
        {
          playerBusiness.SendBusinessDataToPlayer(player);
          playerBusiness.Update();
          player.SendNotification("~g~Du hast erfolgreich ~s~" + amount.ToMoneyString() + " ~g~ überwiesen");
          return;
        }
      }
    }

    [ServerEvent(Event.PlayerEnterVehicle)]
    public void CarDealerBusiness_PlayerEnterVehicle(Player player, Vehicle veh, int seat)
    {
      ServerVehicle sVeh = veh.GetServerVehicle();
      if (sVeh == null) return;
      if (!(sVeh is ShopVehicle)) return;
      ShopVehicle shopVehicle = (ShopVehicle)sVeh;

      List<string> availableTargets = new List<string>()
            {
                "Spieler"
            };

      if (player.GetUser().FactionLeader && !player.GetUser().Faction.StateOwned) availableTargets.Add("Fraktion");
      //if (player.GetUser().GroupRank == GroupRank.OWNER) availableTargets.Add("Gruppe");

      player.TriggerEvent("ShopVehicle_OpenMenu", GetBusiness(shopVehicle.BusinessId).Name, shopVehicle.Price, availableTargets.ToArray());
    }

    [RemoteEvent("VehShop_BuyVehicle")]
    public void CarDealerBusiness_BuyVehicle(Player player, string target)
    {
      using (var dbContext = new DatabaseContext())
      {
        ServerVehicle sVeh = player.Vehicle?.GetServerVehicle();
        if (sVeh == null) return;
        if (!(sVeh is ShopVehicle)) return;
        ShopVehicle shopVehicle = (ShopVehicle)sVeh;
        int price = shopVehicle.Price;
        CarDealerBusinessBase business = GetBusiness(shopVehicle.BusinessId) as CarDealerBusinessBase;

        Vector3 spawnPos = business.CarSpawnPositon.Around(3);

        player.TriggerEvent("SERVER:Util_setWaypoint", spawnPos.X, spawnPos.Y);

        User u = player.GetUser(dbContext);

        ServerVehicle newVeh = null;

        if (target == "Spieler")
        {
          TransactionResult result = BankManager.TransferMoney(u, business, price, "Auto gekauft", dbContext);
          if (result == TransactionResult.SENDER_NOT_ENOUGH_MONEY)
          {
            player.SendNotification("~r~Du hast nicht genug Geld: " + price.ToMoneyString());
            return;
          }

          newVeh = new UserVehicle
          {
            Heading = business.CarSpawnHeading,
            PositionX = spawnPos.X,
            PositionY = spawnPos.Y,
            PositionZ = spawnPos.Z,
            Locked = false,
            UserId = player.GetUser().Id,
            Model = shopVehicle.Model,
            PrimaryColor = 111,
            SecondaryColor = 111,
            Active = true,
            Price = price,
          };
        }
        else if (target == "Fraktion")
        {
          var faction = dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == u.FactionId).First();
          TransactionResult result = BankManager.TransferMoney(faction, business, price * 3, "Auto gekauft", dbContext);
          if (result == TransactionResult.SENDER_NOT_ENOUGH_MONEY)
          {
            player.SendNotification("~r~Die Fraktion hat nicht genug Geld: " + (price * 3).ToMoneyString());
            return;
          }

          newVeh = new FactionVehicle
          {
            Heading = business.CarSpawnHeading,
            PositionX = spawnPos.X,
            PositionY = spawnPos.Y,
            PositionZ = spawnPos.Z,
            Locked = false,
            Owners = JsonConvert.SerializeObject(new int[] { player.GetUser(dbContext).FactionId.Value }),
            Model = shopVehicle.Model,
            PrimaryColor = 111,
            SecondaryColor = 111,
            Active = true,
            BuyPrice = price
          };
        }
        //else if (target == "Gruppe")
        //{
        //  newVeh = new GroupVehicle
        //  {
        //    Heading = business.CarSpawnHeading,
        //    PositionX = spawnPos.X,
        //    PositionY = spawnPos.Y,
        //    PositionZ = spawnPos.Z,
        //    Locked = false,
        //    GroupId = player.GetUser().Group.Id,
        //    Model = shopVehicle.Model,
        //    PrimaryColor = 111,
        //    SecondaryColor = 111,
        //    Active = true,
        //  };
        //}
        dbContext.ServerVehicles.Add(newVeh);
        dbContext.SaveChanges();

        newVeh.Spawn();
      }
    }

    [RemoteEvent("CLIENT:Business_BuyBusiness")]
    public void BusinessEventBuyBusiness(Player player)
    {
      BusinessBase business = GetNearBusiness(player);
      if (business == null) return;

      if (business.GetOwner() != null)
      {
        ChatService.ErrorMessage(player, "Dieses Business hat einen Besitzer");
        return;
      }

      using (var dbContext = new DatabaseContext())
      {
        int price = business.GetData().Price;
        User user = player.GetUser(dbContext);

        if (user.BusinessId != null)
        {
          ChatService.ErrorMessage(player, "Du kannst nicht mehrere Businesse besitzen");
        }

        IBankAccount bankAccount = user.BankAccount;

        if (price > bankAccount.Balance)
        {
          ChatService.ErrorMessage(player, "Du hast nicht genug Geld");
          return;
        }

        user.BusinessId = business.Id;
        bankAccount.Balance -= price;

        player.SendChatMessage("Business gekauft");

        dbContext.SaveChanges();

        business.Update();
      }
    }

    [RemoteEvent("CLIENT:Business_SellBusiness")]
    public void BusinessEventSellBusiness(Player player)
    {
      BusinessBase business = GetNearBusiness(player);
      if (business == null) return;

      using (var dbContext = new DatabaseContext())
      {
        User user = player.GetUser(dbContext);

        if (user.Id != business.Id)
        {
          ChatService.ErrorMessage(player, "Dieses Business gehört nicht dir");
          return;
        }

        user.BusinessId = null;
        dbContext.SaveChanges();

        player.SendChatMessage("Business verkauft");

        business.Update();
      }
    }
  }
}
