using System.Collections.Generic;
using System.Linq;
using GTANetworkAPI;
using Newtonsoft.Json;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Job;
using ReallifeGamemode.Server.Services;
using ReallifeGamemode.Server.Types;
using ReallifeGamemode.Server.Util;
using ReallifeGamemode.Services;
using System.Buffers;
using Microsoft.EntityFrameworkCore;
using ReallifeGamemode.Server.Events;
using System;
using ReallifeGamemode.Server.Core.API;
using ReallifeGamemode.Server.Factions.Medic;

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

namespace ReallifeGamemode.Server.Managers
{
  public class InteractionManager : Script
  {
    #region Eigeninteraktionen Taste-M

    [RemoteEvent("CLIENT:InteractionMenu_AcceptInvite")]
    public void InteractionMenuAcceptInvite(Player player, string type)
    {
      if (type != "Fraktion" && type != "Gruppe") return;

      if (type == "Fraktion") // Fraktions Invite annehmen
      {
        if (!player.HasData("accept_faction_invite"))
        {
          ChatService.ErrorMessage(player, "Du hast keine Einladung in eine Fraktion erhalten");
          return;
        }

        Player leader = NAPI.Player.GetPlayerFromName(player.GetData<string>("accept_faction_invite"));
        player.ResetData("accept_faction_invite");

        if (leader == null)
        {
          ChatService.ErrorMessage(player, "Die Einladung ist abgelaufen");
          return;
        }

        using (var dbContext = new DatabaseContext(true))
        {
          User u = leader.GetUser(dbContext);
          User own = player.GetUser(dbContext);

          FactionHelper.ResetPlayer(player, own, dbContext);

          own.FactionId = u.FactionId;
          own.FactionLeader = false;
          own.FactionRank = dbContext
              .FactionRanks
              .OrderBy(x => x.Order)
              .Where(r => r.FactionId == u.FactionId)
              .FirstOrDefault();

          ChatService.SendMessage(leader, "!{02FCFF}" + player.Name + " hat die Einladung angenommen.");
          ChatService.SendMessage(player, "!{02FCFF}Du hast die Einladung angenommen.");

          own.SetBlipAndNametagColor();
          dbContext.SaveChanges();
        }
      }
      else if (type == "Gruppe") // Gruppen Einladung annehmen
      {
        if (!player.HasData("accept_group_invite"))
        {
          ChatService.ErrorMessage(player, "Du hast keine Einladung in eine Gruppe erhalten");
          return;
        }

        Player leader = NAPI.Player.GetPlayerFromName(player.GetData<string>("accept_group_invite"));
        player.ResetData("accept_group_invite");

        if (leader == null)
        {
          ChatService.ErrorMessage(player, "Die Einladung ist abgelaufen");
          return;
        }

        using (var dbContext = new DatabaseContext())
        {
          User u = leader.GetUser(dbContext);
          User own = player.GetUser(dbContext);

          own.Group = u.Group;
          own.GroupRank = GroupRank.MEMBER;

          ChatService.SendMessage(leader, "!{02FCFF}" + player.Name + " hat die Einladung angenommen.");
          ChatService.SendMessage(player, "!{02FCFF}Du hast die Einladung angenommen.");

          dbContext.SaveChanges();
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_EndShow")]
    public void InteractionMenuEndShow(Player client)
    {
      client.ResetData("ShowActive");
    }

    [RemoteEvent("CLIENT:InteractionMenu_Show")]
    public void InteractionMenuShow(Player player, string type, string nameOrId)
    {
      if (type != "License")
        return;

      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);
      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }

      User targetUser = target.GetUser();
      User playerUser = player.GetUser();

      if (type == "License")
      {
        if (!playerUser.DriverLicenseBike && !playerUser.DriverLicenseVehicle && !playerUser.FlyingLicensePlane && !playerUser.WeaponLicense)
        {
          player.SendNotification("~r~Sie besitzen keine Scheine!");
          return;
        }
        if (player.Position.DistanceTo(target.Position) > 5)
          return;

        if (target.HasData("ShowActive"))
        {
          player.SendNotification("Spieler ist beschäftigt.");
          return;
        }
        target.SetData("ShowActive", true);
        List<bool> licenses = new List<bool>();
        licenses.Add(playerUser.DriverLicenseVehicle);
        licenses.Add(playerUser.DriverLicenseBike);
        licenses.Add(playerUser.FlyingLicensePlane);
        licenses.Add(playerUser.WeaponLicense);
        target.TriggerEvent("ShowLicenses", player.Name, licenses.ToArray());
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_Invite")]
    public void InteractionMenuInviteFaction(Player player, string type, string nameOrId)
    {
      if (type != "Fraktion" && type != "Gruppe") return;

      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);
      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }

      User targetUser = target.GetUser();
      User playerUser = player.GetUser();

      if (type == "Fraktion")
      {
        if (playerUser?.FactionId == null || playerUser?.FactionLeader == false)
        {
          ChatService.NotAuthorized(player);
          return;
        }

        if (targetUser?.FactionId != null)
        {
          ChatService.ErrorMessage(player, "Dieser Spieler ist schon in einer Fraktion");
          return;
        }
        if (targetUser?.Wanteds > 0)
        {
          player.SendNotification("~r~Du kannst diesen Spieler nicht inviten da er aktuell gesucht wird. (Wanted)", false);
          return;
        }

        target.SetData("accept_faction_invite", player.Name);

        ChatService.SendMessage(player, "!{02FCFF}Du hast dem Spieler " + target.Name + " eine Einladung in deine Fraktion gesendet.");
        ChatService.SendMessage(target, "!{02FCFF}Du hast von " + player.Name + " eine Einladung in die Fraktion \"" + playerUser.Faction.Name + "\" erhalten.");
        ChatService.SendMessage(target, "!{02FCFF}Benutze das Interaktionsmenü, um die Einladung anzunehmen");
      }
      else if (type == "Gruppe")
      {
        if (playerUser?.Group == null || playerUser.GroupRank < GroupRank.MANAGER)
        {
          ChatService.NotAuthorized(player);
          return;
        }

        if (targetUser?.Group != null)
        {
          ChatService.ErrorMessage(player, "Dieser Spieler ist schon in einer Gruppe");
          return;
        }

        target.SetData("accept_group_invite", player.Name);

        ChatService.SendMessage(player, "!{02FCFF}Du hast dem Spieler " + target.Name + " eine Einladung in deine Gruppe gesendet.");
        ChatService.SendMessage(target, "!{02FCFF}Du hast von " + player.Name + " eine Einladung in die Gruppe \"" + playerUser.Group.Name + "\" erhalten.");
        ChatService.SendMessage(target, "!{02FCFF}Benutze das Interaktionsmenü, um die Einladung anzunehmen");
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_Uninvite")]
    public void InteractionMenuInviteGroup(Player player, string type, string name)
    {
      if (type != "Fraktion" && type != "Gruppe") return;

      using var dbContext = new DatabaseContext();
      User targetUser = dbContext.Users.FirstOrDefault(u => u.Name == name);

      if (targetUser == null)
      {
        ChatService.PlayerNotFound(player);
        return;
      }

      Player targetPlayer = PlayerService.GetPlayerByNameOrId(name);
      User playerUser = player.GetUser(dbContext);

      if (type == "Fraktion")
      {
        if (playerUser?.FactionId == null || playerUser.FactionLeader == false)
        {
          ChatService.NotAuthorized(player);
          return;
        }

        if (targetUser?.FactionId != playerUser?.FactionId)
        {
          ChatService.ErrorMessage(player, "Dieser Spieler ist nicht in deiner Fraktion");
          return;
        }

        if (player.Name == targetUser.Name)
        {
          ChatService.ErrorMessage(player, "Du kannst dich nicht selber uninviten");
          return;
        }

        if (targetUser?.FactionLeader ?? false)
        {
          ChatService.ErrorMessage(player, "Du kannst keinen Leader uninviten");
          return;
        }

        if (targetPlayer != null)
        {
          targetUser.SetData("duty", false);
          targetPlayer.TriggerEvent("toggleDutyMode", false);
          UpdateCharacterCloth.LoadCharacterDefaults(targetPlayer);
          ChatService.SendMessage(targetPlayer, "!{02FCFF}Du wurdest von " + player.Name + " aus der Fraktion geworfen.");
          FactionHelper.ResetPlayer(targetPlayer, targetUser, dbContext);
        }
        Medic.UpdateDutyMedics();
        targetUser.FactionRankId = null;
        targetUser.FactionId = null;
        targetUser.SetBlipAndNametagColor();

        ChatService.SendMessage(player, "!{02FCFF}Du hast " + targetUser.Name + " aus der Fraktion geworfen.");

        dbContext.SaveChanges();
      }
      else if (type == "Gruppe")
      {
        if (playerUser?.Group == null || playerUser.GroupRank < GroupRank.MANAGER)
        {
          ChatService.NotAuthorized(player);
          return;
        }

        if (targetUser?.Group.Id != playerUser?.Group.Id)
        {
          ChatService.ErrorMessage(player, "Dieser Spieler ist nicht in deiner Gruppe");
          return;
        }

        if (player.Handle == targetPlayer.Handle)
        {
          ChatService.ErrorMessage(player, "Du kannst dich nicht selber uninviten");
          return;
        }

        if (targetUser?.GroupRank >= GroupRank.MANAGER)
        {
          ChatService.ErrorMessage(player, "Du kannst keinen Leader uninviten");
          return;
        }

        targetUser.Group = null;
        targetUser.GroupRank = GroupRank.NONE;

        ChatService.SendMessage(player, "!{02FCFF}Du hast " + targetPlayer.Name + " aus der Gruppe geworfen.");
        ChatService.SendMessage(targetPlayer, "!{02FCFF}Du wurdest von " + player.Name + " aus der Gruppe geworfen.");

        dbContext.SaveChanges();
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_MemberList")]
    public void InteractionMenuGroupMemberList(Player player, string type, string name)
    {

    }

    [RemoteEvent("CLIENT:InteractionMenu_MakeLeader")]
    public void InteractionMenuGroupMakeLeader(Player player, string type, string name)
    {
      Player target = PlayerService.GetPlayerByNameOrId(name);
      User targetUser = target.GetUser();
      User playerUser = player.GetUser();

      if (type == "Gruppe")
      {
        using (var dbContext = new DatabaseContext())
        {
          if (playerUser.GroupRank != GroupRank.OWNER)
          {
            ChatService.ErrorMessage(player, "Nur Besitzer können Manager hinzufügen");
            return;
          }
          if (targetUser?.Group.Id != playerUser?.Group.Id)
          {
            ChatService.ErrorMessage(player, "Dieser Spieler ist nicht in deiner Gruppe");
            return;
          }

          if (player.Name == name)
          {
            ChatService.ErrorMessage(player, "Du bist schon Besitzer");
            return;
          }

          ChatService.BroadcastGroup("~y~" + targetUser.Name + " ~s~ist jetzt neuer Gruppen-Manager", playerUser.Group);
          ChatService.SendMessage(targetUser.Player, "~y~Du bist jetzt Manager deiner Gruppe");
          targetUser.GroupRank = GroupRank.MANAGER;

          dbContext.SaveChanges();
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_LeaveGroup")]
    public void InteractionMenuGroupLeageGroup(Player player, string type)
    {

      if (type == "Gruppe")
      {
        using (var dbContext = new DatabaseContext())
        {
          User playerUser = player.GetUser(dbContext);

          if (playerUser?.Group == null)
          {
            ChatService.NotAuthorized(player);
            return;
          }

          if (dbContext.Users.Count(c => c.Group == playerUser.Group) == 1)
          {
            InteractionMenuGroupDeleteGroup(player, "Gruppe");
            return;
          }

          var group = player.GetUser().Group;

          if (playerUser.GroupRank == GroupRank.OWNER)
          {
            var memberList = dbContext.Users.Where(u => u.Group == playerUser.Group && u.GroupRank < GroupRank.OWNER).ToList();
            var newOwner = memberList.OrderByDescending(c => c.GroupRank).First();

            newOwner.GroupRank = GroupRank.OWNER;
            ChatService.BroadcastGroup("~y~" + newOwner.Name + "~s~ wurde zum neuen Besitzer ernannt", playerUser.Group);
          }

          playerUser.GroupRank = GroupRank.NONE;
          playerUser.Group = null;
          ChatService.BroadcastGroup("~y~" + player.Name + "~s~ hat die Gruppe verlassen", group);
          dbContext.SaveChanges();
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_DeleteGroup")]
    public void InteractionMenuGroupDeleteGroup(Player player, string type)
    {
      User playerUser = player.GetUser();
      if (type == "Gruppe")
      {
        using (var dbContext = new DatabaseContext())
        {
          if (playerUser?.Group == null || playerUser.GroupRank < GroupRank.OWNER)
          {
            ChatService.NotAuthorized(player);
            return;
          }

          var deletedGroup = dbContext.Groups.FirstOrDefault(g => g.Id == playerUser.Group.Id);

          foreach (var user in dbContext.Users.Where(u => u.Group == playerUser.Group).ToList())
          {
            user.Group = null;
            user.GroupRank = GroupRank.NONE;
            ChatService.SendMessage(PlayerService.GetPlayerByNameOrId(user.Name), "Die Gruppe ~y~" + deletedGroup.Name + "~s~ wurde aufgelöst");
          }
          dbContext.Remove(deletedGroup);
          dbContext.SaveChanges();
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_CallService_Taxi")]
    public void CallServiceTaxi(Player player, string street, string zone)
    {
      var taxiJob = JobManager.GetJob<TaxiDriverJob>();

      if (taxiJob == null)
      {
        player.SendChatMessage("Aktuell kann kein Taxi gerufen werden.");
        return;
      }

      if (taxiJob.TaxiContracts.Where(t => t.Name == player.Name).Count() != 0)
      {
        ChatService.ErrorMessage(player, "Du kannst nur einmal ein Taxi rufen");
        return;
      }

      taxiJob.TaxiContracts.Add(new TaxiContract()
      {
        Name = player.Name,
        Position = player.Position
      });

      string msg = $"!{{02FCFF}}{player.Name} hat in der Straße {street} in {zone} ein Taxi gerufen.";
      ChatService.BroadcastJob(msg, taxiJob);
      ChatService.SendMessage(player, "!{02FCFF}Du hast erfolgreich ein Taxi zu deiner aktuellen Position gerufen.");
    }

    [RemoteEvent("CLIENT:InteractionMenu_PlayerVehicleInteraction")]
    public void PlayerVehicleInteraction(Player player, int id, string selection)
    {
      using var dbContext = new DatabaseContext();
      var user = player.GetUser(dbContext);

      var userVehicle = dbContext.UserVehicles.Where(v => v.Id == id && v.UserId == user.Id).FirstOrDefault();
      if (userVehicle == null)
      {
        ChatService.ErrorMessage(player, "Das Fahrzeug wurde nicht gefunden");
      }

      GTANetworkAPI.Vehicle veh = VehicleManager.GetVehicleFromServerVehicle(userVehicle);

      if (selection == "Finden")
      {
        player.TriggerEvent("SERVER:Util_setWaypoint", veh.Position.X, veh.Position.Y);
      }
      else if (selection == "Verkaufen")
      {
        var vehPrice = userVehicle.Price;
        var backPrice = (int)(vehPrice * 0.4);
        player.SetData<bool>("SellVehicleDecision", true);
        player.SetData<int>("VehicleToSell", id);
        ChatService.SendMessage(player, "~s~Möchtes du das Fahrzeug ~y~" + veh.DisplayName + " ~s~wirklich für ~g~$" + backPrice + " ~s~verkaufen? Drücke ~g~J~s~ zum Bestätigen oder ~r~N ~s~zum Abbrechen");
      }
    }

    public static void SellServerVehicle(Player player, int id)
    {
      using var dbContext = new DatabaseContext();
      var user = player.GetUser(dbContext);
      ServerVehicle serverVehicle = dbContext.ServerVehicles.FirstOrDefault(v => v.Id == id);
        
      if(serverVehicle is UserVehicle)
      {
        UserVehicle userVehicle = dbContext.UserVehicles.Where(v => v.Id == id && v.UserId == user.Id).FirstOrDefault();
        var vehPrice = userVehicle.Price;
        var backPrice = (int)(vehPrice * 0.4);
        GTANetworkAPI.Vehicle veh = VehicleManager.GetVehicleFromServerVehicle(userVehicle);
        user.BankAccount.Balance += backPrice;

        ChatService.SendMessage(player, $"~b~[INFO]~s~ Du hast durch den Autoverkauf ~g~{backPrice.ToMoneyString()}~s~ erhalten.");

        VehicleManager.DeleteVehicle(veh);
        dbContext.UserVehicles.Remove(userVehicle);
        player.SendNotification("~g~Du hast dein Fahrzeug verkauft");
      }
      else if(serverVehicle is FactionVehicle)
      {
        FactionVehicle factionVehicle = dbContext.FactionVehicles.FirstOrDefault(v => v.Id == id);
        var backPrice = (int)(factionVehicle.BuyPrice * 1.5 * 0.4);
        user.Faction.BankAccount.Balance += backPrice;

        ChatService.SendMessage(player, $"~b~[INFO]~s~ Die Fraktionskasse hat ~g~{backPrice.ToMoneyString()}~s~ erhalten.");

        VehicleManager.DeleteVehicle(VehicleManager.GetVehicleFromServerVehicle(factionVehicle));

        dbContext.FactionVehicles.Remove(factionVehicle);
        player.SendNotification("~g~Du hast dein Fraktionsfahrzeug verkauft");
      }   
      dbContext.SaveChanges();
    }

    [RemoteEvent("CLIENT:InteractionMenu_FactionVehicleInteraction")]
    public void FactionVehicleInteraction(Player player, int id, string selection)
    {
      using var dbContext = new DatabaseContext();
      var user = player.GetUser(dbContext);

      if (user.FactionId == null)
      {
        return;
      }

      var factionVehicle = dbContext.FactionVehicles.ToList().Where(v => v.Id == id && v.GetOwners().Contains(user.FactionId.Value)).FirstOrDefault();
      if (factionVehicle == null)
      {
        ChatService.ErrorMessage(player, "Das Fahrzeug wurde nicht gefunden");
      }

      GTANetworkAPI.Vehicle veh = VehicleManager.GetVehicleFromServerVehicle(factionVehicle);

      if (selection == "Finden")
      {
        player.TriggerEvent("SERVER:Util_setWaypoint", veh.Position.X, veh.Position.Y);
      }
      else if (selection == "Verkaufen")
      {
        if (!user.FactionLeader)
        {
          return;
        }

        if (user.Faction.StateOwned)
        {
          ChatService.ErrorMessage(player, "Der Fuhrpark der Staatsfraktionen kann über die Leaderverwaltung geändert werden");
          return;
        }

        var factionVehicleCount = dbContext.FactionVehicles.ToList().Where(v => v.GetOwners().Contains(user.FactionId.Value)).Count();
        if (factionVehicleCount <= 6)
        {
          ChatService.ErrorMessage(player, "Die Fraktion braucht mindestens sechs Fahrzeuge");
          return;
        }

        var price = dbContext.ShopVehicles.Where(v => v.Model == factionVehicle.Model && v.Active).Select(s => new { s.Price }).FirstOrDefault();
        if (price == null)
        {
          ChatService.ErrorMessage(player, "Das Fahrzeug konnte nicht verkauft werden. Bitte melde dich bei der Leaderverwaltung");
          return;
        }
      

        var backPrice = (int)(price.Price * 1.5 * 0.4);

        player.SetData<bool>("SellVehicleDecision", true);
        player.SetData<int>("VehicleToSell", id);
        ChatService.SendMessage(player, "~s~Möchtes du das Fahrzeug ~y~" + veh.DisplayName + " ~s~wirklich für ~g~$" + backPrice + " ~s~verkaufen? Drücke ~g~J~s~ zum Bestätigen oder ~r~N ~s~zum Abbrechen");       
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_LSPD_Ticket3")]
    public void InteractionMenu_LSPD_Ticket3(Player player, string jsonNameOrId, string stringAmount, string jsonReason)
    {
      string nameOrId = (string)JsonConvert.DeserializeObject(jsonNameOrId);
      int amount = Int32.Parse(stringAmount);
      string reason = (string)JsonConvert.DeserializeObject(jsonReason);
      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);
      string playername = NAPI.Player.GetPlayerName(player);
      string targetname = NAPI.Player.GetPlayerName(target);

      List<Player> nearPlayers = NAPI.Player.GetPlayersInRadiusOfPlayer(10, player);

      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }
      else if (target == player)
      {
        player.SendNotification("~r~Du kannst dir selbst kein Ticket geben!");
      }
      else if (!nearPlayers.Contains(target))
      {
        player.SendNotification("~r~Der Spieler ist nicht in deiner Nähe!");
      }
      else
      {
        if (amount > 0 && amount <= 10000)
        {
          player.SendNotification($"~w~Du hast ~y~{targetname} ~w~ein Ticket in Höhe von ~b~${amount} ~w~ausgestellt. Grund: ~g~{reason}~w~.");
          target.SendNotification($"~b~{player.GetUser().FactionRank.RankName} {playername} ~w~hat dir ein Ticket in Höhe von ~y~${amount} ~w~ausgestellt.", true);
          target.SendNotification($"~w~Grund: ~g~{reason}~w~. ~x~Das Ticket kann im Interaktionsmenü angenommen werden.", true);
          target.SetData("ticket_boolean", true);
          target.SetData<int>("ticket_amount", amount);
          target.SetData<Player>("ticket_player", player);
        }
        else
        {
          player.SendNotification("~r~Du kannst nur zwischen $1 und $10.000 angeben!");
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_LSPD_Points3")]
    public void InteractionMenu_LSPD_Points3(Player player, string jsonNameOrId, string jsoAmount, string jsonReason)
    {
      string nameOrId = (string)JsonConvert.DeserializeObject(jsonNameOrId);
      string reason = (string)JsonConvert.DeserializeObject(jsonReason);
      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);
      string playername = NAPI.Player.GetPlayerName(player);
      string targetname = NAPI.Player.GetPlayerName(target);

      if (!int.TryParse((string)JsonConvert.DeserializeObject(jsoAmount), out int amount))
      {
        player.SendNotification("~r~Es muss eine gültige Zahl für die Punkte angegeben werden!");
        return;
      }

      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }

      if (target == player)
      {
        player.SendNotification("~r~Du kannst dir selbst keine Punkte setzen!");
        return;
      }

      using var dbContext = new DatabaseContext();
      User targetUser = target.GetUser(dbContext);

      if (targetUser == null)
      {
        ChatService.PlayerNotFound(player);
        return;
      }

      if (targetUser.DriverLicenseVehicle == false && targetUser.DriverLicenseBike == false)
      {
        player.SendNotification("~r~Spieler hat weder Auto- noch Motorradführerschein!");
        return;
      }

      if (amount < -13 || amount > 13)
      {
        player.SendNotification("~r~Du kannst nur zwischen 1 und 12 Punkte verteilen!");
        return;
      }

      targetUser.Points += amount;

      player.SendNotification($"~w~Du hast ~y~{targetname} ~b~{amount} ~w~Strafpunkte gegeben. Grund: ~g~{reason}~w~.");
      target.SendNotification($"~b~{player.GetUser().FactionRank.RankName} {playername} ~w~hat dir ~y~{amount} ~w~Strafpunkte gegeben (Grund: ~r~{reason}~w~).", true);
      ChatService.HQMessage(targetname + " hat von " + playername + " " + amount + " Strafpunkt(e) erhalten (Grund: " + reason + "). Gesamt: " + targetUser.Points);

      if (targetUser.Points < 0)
      {
        targetUser.Points = 0;
      }
      else if (targetUser.Points >= 12)
      {
        target.SendNotification($"~x~Du hast nun ~r~12/12 ~x~Strafpunkte. ~w~Dir wurde dein ~r~Auto- und dein Motorradführerschein ~w~entzogen.", true);
        ChatService.HQMessage(targetname + " besitzt nun keinen Führerschein mehr.");

        targetUser.Points = 0;
        targetUser.DriverLicenseVehicle = false;
        targetUser.DriverLicenseBike = false;
      }
      else
      {
        target.SendNotification($"~x~Du hast nun ~y~{targetUser.Points}/12 ~x~Strafpunkt(e).", true);
      }

      dbContext.SaveChanges();
    }

    [RemoteEvent("CLIENT:InteractionMenu_Ticket")]
    public void InteractionMenuTicket(Player target, string type, int ticket_amount)
    {
      Player player = NAPI.Data.GetEntityData(target, "ticket_player");
      string playername = NAPI.Player.GetPlayerName(player);
      string targetname = NAPI.Player.GetPlayerName(target);
      /*if (type != "Fraktion" && type != "Gruppe") return;

      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);
      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }*/

      //User targetUser = target.GetUser();
      //User playerUser = player.GetUser();

      if (type == "Angenommen")
      {
        target.SendNotification($"~g~Du hast das Ticket über ~b~{ticket_amount}$ ~g~angenommen und bezahlt.", true);
        player.SendNotification($"~y~{targetname} ~g~hat das Ticket über ~b~{ticket_amount}$ ~g~angenommen und bezahlt.", true);
        using (var dbContext = new DatabaseContext())
        {
          User user = player.GetUser(dbContext);
          User targetUser = target.GetUser(dbContext);
          targetUser.BankAccount.Balance -= ticket_amount;

          FactionBankAccount lspdBankAccount = dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == 1).First().BankAccount;
          FactionBankAccount fibBankAccount = dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == 3).First().BankAccount;

          if (user.FactionId == 1)
          {
            lspdBankAccount.Balance += (int)(ticket_amount / 100 * 60);
            fibBankAccount.Balance += (int)(ticket_amount / 100 * 40);
          }
          else if (user.FactionId == 3)
          {
            fibBankAccount.Balance += (int)(ticket_amount / 100 * 60);
            lspdBankAccount.Balance += (int)(ticket_amount / 100 * 40);
          }
          dbContext.SaveChanges();
        }
        target.ResetData("ticket_boolean");
        target.ResetData("ticket_amount");
        target.ResetData("ticket_player");
      }
      else if (type == "Abgelehnt")
      {
        target.SendNotification($"~r~Du hast das Ticket über ~b~{ticket_amount}$ ~r~abgelehnt.", true);
        player.SendNotification($"~y~{targetname} ~r~hat das Ticket über ~b~{ticket_amount}$ ~r~abgelehnt.", true);
        target.ResetData("ticket_boolean");
        target.ResetData("ticket_amount");
        target.ResetData("ticket_player");
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_Pay")]
    public void InteractionMenu_Pay(Player player, string jsonNameOrId, string stringAmount)
    {
      if (player.GetUser().PlayedMinutes < 180)
      {
        player.SendNotification("~r~Du kannst Geld erst ab 3 Spielstunden vergeben");
        return;
      }

      string nameOrId = (string)JsonConvert.DeserializeObject(jsonNameOrId);
      int amount;
      try
      {
        amount = Int32.Parse(stringAmount);
      }
      catch
      {
        player.SendNotification("~r~" + stringAmount + " ist kein gültiger Betrag.");
        return;
      }
      Player target = PlayerService.GetPlayerByNameOrId(nameOrId);

      List<Player> nearbyPlayers = NAPI.Player.GetPlayersInRadiusOfPlayer(3, player);

      if (target == null || !target.IsLoggedIn())
      {
        ChatService.PlayerNotFound(player);
        return;
      }
      else if (target == player)
      {
        player.SendNotification("~r~Du kannst dir selber kein Geld geben!");
      }
      else if (!nearbyPlayers.Contains(target))
      {
        player.SendNotification("~r~Dieser Spieler befindet sich nicht in deiner Nähe!");
      }
      else if (player.GetUser().Handmoney < amount)
      {
        player.SendNotification("~r~Du hast nicht genug Geld.");
        return;
      }
      else
      {
        string playername = NAPI.Player.GetPlayerName(player);
        string targetname = NAPI.Player.GetPlayerName(target);

        if (amount > 0 && amount <= 5000)
        {
          player.SendNotification($"~w~Du hast ~y~{targetname} ~w~ ${amount} gegeben.");
          target.SendNotification($"~w~ Du hast von ~y~{playername} ~w~ ${amount} erhalten.", true);

          using (var dbContext = new DatabaseContext())
          {
            target.GetUser(dbContext).Handmoney += amount;
            player.GetUser(dbContext).Handmoney -= amount;
            dbContext.SaveChanges();
          }
        }
        else
        {
          player.SendNotification("~r~ Du kannst nur zwischen $1 und $5.000 geben!");
        }
      }
    }

    [RemoteEvent("CLIENT:wantedlist")]
    public void wantedlist(Player player)
    {
      if (!player.IsLoggedIn()) return;
      List<Player> players = NAPI.Pools.GetAllPlayers();

      var listPlayers = players
        .Select(p => new
        {
          Id = p.Handle.Value,
          p.Name,
          wanted = p.GetUser()?.Wanteds,
        }).Where(u => u.wanted > 0).ToList();

      if (listPlayers.Count() > 0)
        player.TriggerEvent("showWantedlist", JsonConvert.SerializeObject(listPlayers));
      else
        player.SendNotification("~r~[Fehler] ~w~Es gibt derzeitig keine Verbrecher!");


    }

    [RemoteEvent("CLIENT:InteractionMenu_FindHouse")]
    public void InteractionMenu_House(Player player)
    {
      using (var dbContext = new DatabaseContext())
      {
        User user = player.GetUser(dbContext);
        if (user.House == null)
        {
          player.SendNotification("~r~[Fehler] ~w~Du besitzt kein Haus!");
          return;
        }
        else
        {
          player.TriggerEvent("SERVER:Util_setWaypoint", user.House.Position.X, user.House.Position.Y);
        }
      }
    }

    [RemoteEvent("CLIENT:InteractionMenu_CallService_Sani")]
    public void CallServiceSani(Player player)
    {
      if (player.GetUser().FactionId == 2 && player.IsDuty())
      {
        player.SendNotification("~r~[Fehler] ~w~Du kannst keinen Sanitäter rufen.");
        return;
      }

      //MEDIC AUFTRAG
      MedicTask healTask = new MedicTask()
      {
        Victim = player.Name,
        Position = player.Position,
        Caller = null,
        Description = "Healen",
        Time = DateTime.Now,
        Type = MedicTaskType.HEAL,
        MedicName = "none"
      };
      if (player.GetData<bool>("healauftrag") == true)
      {
        player.SendNotification("~r~[Fehler] ~w~Du hast bereits ein Sanitäter gerufen.");
      }
      else
      {
        Medic.AddTaskToList(healTask);
        player.SetData("healauftrag", true);
        player.SendNotification("~w~Du hast einen Sanitäter um Hilfe gerufen");
        ChatService.BroadcastFaction("~y~[MEDIC] ~w~" + player.Name + " hat soeben einen Serviceauftrag beantragt.", new List<int>() { 2 });
      }
    }


    #endregion Eigeninteraktionen PFEILTASTE-RUNTER

    #region Spielerinteraktionen PFEILTASTE-LINKS

    [RemoteEvent("openTradeInventory")]
    public void OpenTradeInventory(Player player, string targetPlayer)
    {
      InventoryManager.GetUserItems(player);
      player.TriggerEvent("openTradeMenu", targetPlayer);
    }

    [RemoteEvent("sendTradeItemsToPartner")]
    public void SendTradeItemsToPartner(Player player, string tradeItemArray, int tradePrize, string tradePartnerName)
    {
      var tradeItems = JsonConvert.DeserializeObject<string[][]>(tradeItemArray);
      Player tradePartner = PlayerService.GetPlayerByNameOrId(tradePartnerName);
      InventoryManager.GetUserItems(player);
      tradePartner.TriggerEvent("showTradeRequest", player.Name, tradeItems, tradePrize);
      player.TriggerEvent("startTradeRequestTimer");
    }

    [RemoteEvent("tradeDecision")]
    public void TradeDecision(Player player, string tradeSelection, string tradeItemArray, string tradeRequesterName, int tradePrize)
    {
      var tradeItems = JsonConvert.DeserializeObject<string[][]>(tradeItemArray);
      Player tradeRequester = PlayerService.GetPlayerByNameOrId(tradeRequesterName);
      if (tradeSelection == "accept")
      {
        if (player.GetUser().Handmoney < tradePrize)
        {
          tradeRequester.TriggerEvent("unlockTradeItems");
          tradeRequester.SendNotification("~y~" + player.Name + " ~r~hat nicht genügend Bargeld! ~s~Handel abgebrochen.");
          player.SendNotification("~r~Du hast nicht genügend Bargeld! ~s~Handel abgebrochen.");
        }
        else
        {
          InventoryManager.GetUserItems(player);
          tradeRequester.TriggerEvent("clearTradeItems");
          player.TriggerEvent("showTradeItems", tradeItemArray);

          using (var dbContext = new DatabaseContext())
          {
            var playerUserId = player.GetUser().Id;
            var tradeRUserId = tradeRequester.GetUser().Id;
            var playerUser = dbContext.Users.FirstOrDefault(u => u.Id == playerUserId);
            var tradeRequesterUser = dbContext.Users.FirstOrDefault(u => u.Id == tradeRUserId);

            playerUser.Handmoney -= tradePrize;
            player.TriggerEvent("SERVER:WORLD_INTERACTION:UPDATE_HANDMONEY", playerUser.Handmoney);

            tradeRequesterUser.Handmoney += tradePrize;
            tradeRequester.TriggerEvent("SERVER:WORLD_INTERACTION:UPDATE_HANDMONEY", tradeRequesterUser.Handmoney);

            dbContext.SaveChanges();
          }
        }
      }
      else
      {
        tradeRequester.TriggerEvent("unlockTradeItems");
      }
    }

    #endregion Spielerinteraktionen PFEILTASTE-LINKS
  }
}
