using System;
using System.Linq;
using GTANetworkAPI;
using Microsoft.Extensions.Logging;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Log;
using ReallifeGamemode.Server.Managers;

namespace ReallifeGamemode.Server.Events
{
  public class Weapon : Script
  {
    private ILogger logger = LogManager.GetLogger<Weapon>();

    private const int SLOT_PRIMARY = 2;
    private const int SLOT_SECONDARY = 1;
    private const int SLOT_MEELE = 3;
    private const int SLOT_SPECIAL = 4;


    [RemoteEvent("saveWeaponSelection")]
    public void SaveWeaponSelection(Player client, string primaryModel, string secondaryModel, string meleeModel, string specialModel, string armor)
    {
      using var dbContext = new DatabaseContext();
      User user = client.GetUser(dbContext);

      WeaponPoint nearestWeapon = PositionManager.WeaponPoints.Find(w => w.Position.DistanceTo(client.Position) <= 1.5 && w.FactionId == user.FactionId);

      if (nearestWeapon == null)
      {
        client.SendChatMessage("~y~Du bist nicht mehr an deinem Waffenschrank!");
        return;
      }

      if (!string.IsNullOrEmpty(primaryModel))
      {
        HandleSlot(user, client, 2, primaryModel, dbContext);
      }

      if (!string.IsNullOrEmpty(secondaryModel))
      {
        HandleSlot(user, client, 1, secondaryModel, dbContext);
      }

      if (!string.IsNullOrEmpty(meleeModel))
      {
        HandleSlot(user, client, 3, meleeModel, dbContext);
      }

      if (!string.IsNullOrEmpty(specialModel))
      {
        HandleSlot(user, client, 4, specialModel, dbContext);
      }

      dbContext.SaveChanges();

      if (armor == "Schutzweste")
      {
        logger.LogInformation("Player {0} took armor from weaponrack of faction {1}", user.Name, user.FactionId);
        client.SafeSetArmor(100);
      }
    }

    public void HandleSlot(User user, Player player, int slot, string selection, DatabaseContext context)
    {
      string weapon = GetRealWeaponModelName(selection);

      uint hashKey = NAPI.Util.GetHashKey(weapon);

      if (!Enum.GetValues(typeof(WeaponHash)).Cast<WeaponHash>().Contains((WeaponHash)hashKey))
      {
        logger.LogWarning("Player {0} tried to take invalid weapon {1} from weaponrack of faction {2}", user.Name, selection, user.FactionId);
        return;
      }

      var slotWeapons = context.FactionWeapons.Where(w => w.FactionId == user.FactionId && w.SlotID == slot).ToList();

      foreach (var slotWeaponHash in slotWeapons.Select(w => NAPI.Util.GetHashKey(GetRealWeaponModelName(w.WeaponModel))))
      {
        player.RemoveWeapon((WeaponHash)slotWeaponHash);
      }

      var chosenWeapon = slotWeapons.Where(w => w.WeaponModel == selection).FirstOrDefault();
      if (chosenWeapon == null)
      {
        logger.LogWarning("Player {0} tried to take not-existing weapon {1} from weaponrack of faction {2}", user.Name, selection, user.FactionId);
        return;
      }

      int ammo = GetSlotAmmoAmount(slot);
      player.GiveWeapon((WeaponHash)hashKey, ammo);
      player.SetWeaponAmmo((WeaponHash)hashKey, ammo);

      chosenWeapon.Ammount -= 1;
      logger.LogInformation("Player {0} took weapon {1} from weaponrack of faction {2}", user.Name, selection, user.FactionId);
    }

    private int GetSlotAmmoAmount(int slot) => slot switch
    {
      SLOT_PRIMARY => 500,
      SLOT_SECONDARY => 250,
      SLOT_MEELE => 1,
      SLOT_SPECIAL => 20,
      _ => 1
    };

    private string GetRealWeaponModelName(string weapon)
    {
      string ret = "weapon_" + weapon.ToLower();

      if (!ret.Contains("_mk2") && ret.Contains("mk2"))
      {
        ret = ret.Replace("mk2", "_mk2");
      }

      return ret;
    }

    [RemoteEvent("CLIENT:Ammunation_BuyWeapon")]
    public void AmmunationBuyWeapoon(Player player, string weaponmodel, int ammo, int price)
    {
      using (var dbContext = new DatabaseContext())
      {
        User user = player.GetUser(dbContext);

        if (user.Handmoney < price)
        {
          player.SendNotification("Du hast nicht genügend Geld bei dir");
          return;
        }
        logger.LogInformation("Player {0} bought a {1} in ammunation", user.Name, weaponmodel);
        user.Handmoney -= price;
        dbContext.SaveChanges();
        player.GiveWeapon(NAPI.Util.WeaponNameToModel(weaponmodel), ammo);
        //client.TriggerEvent("SERVER:SET_HANDMONEY", user.Handmoney);
      }
    }
  }
}
