using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using GTANetworkAPI;
using Microsoft.EntityFrameworkCore;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Factions.Medic;
using ReallifeGamemode.Server.Managers;
using ReallifeGamemode.Server.Services;
using ReallifeGamemode.Server.Types;
using ReallifeGamemode.Services;

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

namespace ReallifeGamemode.Server.Commands
{
  internal class FactionCommands : Script
  {
    #region Chat Commands

    [Command("f", "~m~Benutzung: ~s~/f [Nachricht]", GreedyArg = true)]
    public void CmdFactionF(Player player, string message)
    {
      Faction f = player.GetUser()?.Faction;
      if (f == null || f.StateOwned)
      {
        ChatService.NotAuthorized(player);
        return;
      }

      message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

      string broadcastMessage = "!{02FCFF}** " + player.GetUser().GetFactionRank().RankName + " " + player.Name + ": " + message + " **";
      ChatService.BroadcastFaction(broadcastMessage, f);
    }

    [Command("ga", "~m~Benutzung: ~s~/ga [Nachricht]", GreedyArg = true)]
    public void CmdFactionGA(Player player, string message)
    {
      User user = player.GetUser();
      Faction f = user?.Faction;
      if ((f == null || f.StateOwned) && !user.IsAdmin(AdminLevel.ADMIN))
      {
        ChatService.NotAuthorized(player);
        return;
      }
      if (f.Name == "Ballas" || f.Name == "Grove" || user.IsAdmin(AdminLevel.ADMIN))
      {
        message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

        string broadcastMessage = "!{FF0000}** " + player.GetUser().GetFactionRank().RankName + " " + player.Name + ": " + message + " **";
        using (var context = new DatabaseContext())
        {
          ChatService.BroadcastFaction(broadcastMessage, context.Factions.ToList().FindAll(c => c.GangOwned), true);
        }
      }
      else
      {
        return;
      }
    }

    [Command("r", "~m~Benutzung: ~s~/r [Nachricht]", GreedyArg = true)]
    public void CmdFactionR(Player player, string message)
    {
      Faction f = player.GetUser()?.Faction;
      if (f == null || !f.StateOwned)
      {
        ChatService.NotAuthorized(player);
        return;
      }

      message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

      string broadcastMessage = "!{33AA33}** " + player.GetUser().GetFactionRank().RankName + " " + player.Name + ": " + message + ", over **";
      ChatService.BroadcastFaction(broadcastMessage, f);
    }

    [Command("d", "~m~Benutzung: ~s~/d [Nachricht]", GreedyArg = true)]
    public void CmdFactionD(Player player, string message)
    {
      Faction f = player.GetUser()?.Faction;
      if (f == null || !f.StateOwned)
      {
        ChatService.NotAuthorized(player);
        return;
      }

      message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

      string broadcastMessage = "!{CC3333}** " + player.GetUser().GetFactionRank().RankName + " " + player.Name + ": " + message + ", over **";
      using (var context = new DatabaseContext())
      {
        ChatService.BroadcastFaction(broadcastMessage, context.Factions.ToList().FindAll(c => c.StateOwned));
      }
    }

    #endregion Chat Commands


    #region NewsReporter commands

    [Command("news", "~m~Benutzung: ~s~/news [Nachricht]", GreedyArg = true)]
    public void CmdFactionNR(Player player, string message)
    {
      Faction f = player.GetUser()?.Faction;
      if (f == null)
      {
        ChatService.NotAuthorized(player);
        return;
      }
      if (player.GetUser().Faction.Id == 9 )
      {
        message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

        string broadcastMessage = "!{ff9531}** News Reporter" + " " + player.Name + ": " + message + " **";
          ChatService.Broadcast(broadcastMessage);
      }
      else
      {
        return;
      }
    }





    #endregion NewsReporter commands
    #region Leader Commands

    [Command("giverank", "~m~Benutzung: ~s~/giverank [Name] [Rang]", GreedyArg = true)]
    public void CmdFactionGiverank(Player player, string name, string rank)
    {
      if (player.GetUser()?.FactionId == null || player.GetUser().FactionLeader == false)
      {
        ChatService.NotAuthorized(player);
        return;
      }

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

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

      using (var dbContext = new DatabaseContext())
      {
        FactionRank fr = dbContext.FactionRanks.FirstOrDefault(r => r.Order == int.Parse(rank) && r.FactionId == player.GetUser(dbContext).FactionId);
        if (fr == null)
        {
          ChatService.ErrorMessage(player, "Dieser Rang existiert nicht");
          return;
        }

        target.GetUser(dbContext).FactionRankId = fr.Id;

        ChatService.SendMessage(player, "!{02FCFF}Du hast " + target.Name + " den Rang '" + fr.RankName + "' (" + fr.Order + ") gegeben.");
        ChatService.SendMessage(target, "!{02FCFF}Du hast von " + player.Name + " den Rang '" + fr.RankName + "' (" + fr.Order + ")' erhalten.");

        dbContext.SaveChanges();
      }
    }

    [Command("lc", "~m~Benutzung: ~s~/lc [Nachricht]", GreedyArg = true)]
    public void CmdFactionLc(Player player, string message)
    {
      User user = player.GetUser();
      if ((user?.FactionId == null || user.FactionLeader == false) && !user.IsAdmin(AdminLevel.ADMIN))
      {
        ChatService.NotAuthorized(player);
        return;
      }

      message = Regex.Replace(message, "(~[a-zA-Z]~)|(!{(.*)})", "");

      string broadcastMsg = "~y~[" + player.GetUser().Faction.Name + "] " + player.Name + ": " + message;

      NAPI.Pools.GetAllPlayers().ForEach(p =>
      {
        User pUser = p.GetUser();
        if (pUser?.FactionLeader ?? false || pUser.IsAdmin(AdminLevel.ADMIN)) ChatService.SendMessage(p, broadcastMsg);
      });
    }

    [Command("setweaponrank", "~m~Benutzung: ~s~/setweaponrank [Waffen Name] [Rank]")]
    public void CmdFactionWeaponRank(Player player, string weaponModel, int rank)
    {
      if (player.GetUser()?.FactionId == null || player.GetUser().FactionLeader == false)
      {
        ChatService.NotAuthorized(player);
        return;
      }
      if (rank > 12 || rank < 1)
      {
        ChatService.ErrorMessage(player, "Gebe einen gültigen Rang ein");
        return;
      }

      User user = player.GetUser();
      WeaponPoint nearestWeapon = PositionManager.WeaponPoints.Find(w => w.Position.DistanceTo(player.Position) <= 1.5 && w.FactionId == user.FactionId);
      if (nearestWeapon == null)
      {
        ChatService.ErrorMessage(player, "Du bist nicht in der nähe vom Waffenspind");
        return;
      }

      using (var context = new DatabaseContext())
      {
        FactionWeapon fw2 = context.FactionWeapons.FirstOrDefault(w => w.FactionId == user.FactionId && w.WeaponModel == weaponModel);

        if (fw2 != null)
        {
          fw2.Rank = rank;
          context.SaveChanges();
          ChatService.SendMessage(player, "Du hast die " + weaponModel + " als Rang " + rank + " Waffe eingestellt.");
          return;
        }
        ChatService.ErrorMessage(player, "Diese Waffe ist nicht im Waffenlager");
        return;
      }
    }

    #endregion Leader Commands

    #region Sanitäter Commands

    [Command("revive", "~m~Benutzung: ~s~/revive")]
    public void CmdFactionMedicRevive(Player player)
    {
      if (player.GetUser()?.FactionId == null || player.GetUser().FactionId != 2)
      {
        ChatService.NotAuthorized(player);
        return;
      }

      if (!player.IsDuty())
      {
        ChatService.ErrorMessage(player, "Du bist nicht im Dienst");
        return;
      }

      if (player.IsInVehicle)
      {
        ChatService.ErrorMessage(player, "Du kannst in einem Auto nicht wiederbeleben.");
        return;
      }

      using var dbContext = new DatabaseContext();
      var nearPlayers = NAPI.Player.GetPlayersInRadiusOfPlayer(2, player);
      var deadPlayer = nearPlayers.Where(i => i.GetData<bool>("isDead") == true).FirstOrDefault();
      if (player == deadPlayer)
      {
        ChatService.ErrorMessage(player, "Du kannst dich nicht selbst wiederbeleben.");
        return;
      }

      if (deadPlayer == null)
      {
        ChatService.ErrorMessage(player, "Es wurde kein toter Spieler gefunden");
        return;
      }

      var deadPlayerUser = deadPlayer.GetUser(dbContext);

      if (deadPlayerUser.Handmoney >= 100)
      {
        deadPlayerUser.Handmoney -= 100;
      }
      else
      {
        int bankMoney = 100 - deadPlayerUser.Handmoney;
        deadPlayerUser.Handmoney = 0;
        deadPlayerUser.BankAccount.Balance -= bankMoney;
      }

      dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == 2).First().BankAccount.Balance += Medic.ReviveIncome;
      
      dbContext.SaveChanges();

      player.PlayAnimation("amb@medic@standing@kneel@enter", "enter", 0);

      deadPlayer.TriggerEvent("onPlayerRevived");
      player.TriggerEvent("destroyMedicTaskCheckpoint");
      deadPlayer.SendNotification($"Du wurdest von ~y~{player.Name}~s~ für ~g~{100.ToMoneyString()} ~s~wiederbelebt.");
      player.SendNotification($"Du hast ~y~{deadPlayer.Name}~s~ wiederbelebt und ~g~{Medic.ReviveIncome}$ ~s~für die Fraktion verdient.");
      deadPlayer.SetData("isDead", false);
      using (var dbContext1 = new DatabaseContext())
      {
        User u;
        u = deadPlayer.GetUser(dbContext1);
        u.Dead = false;
        dbContext.SaveChanges();
      }
      NAPI.Player.SpawnPlayer(deadPlayer, deadPlayer.Position);
      deadPlayer.Health = 50;

      MedicTask task = Medic.ReviveTasks.FirstOrDefault(t => t.Victim == deadPlayer.Name);
      Medic.RemoveTaskFromList(task);
    }

    [Command("heal", "~m~Benutzung: ~s~/heal [Spieler] (Preis)")] //TODO Eventuell noch mit Geldbetrag wie bei SA:MP
    public void CmdFactionMedicHealive(Player player, string receiver, int price = 100)
    {
      Player target = PlayerService.GetPlayerByNameOrId(receiver);
      if (player.GetUser()?.FactionId != 2)
      {
        ChatService.NotAuthorized(player);
        return;
      }

      if(player.Position.DistanceTo(target.Position) > 3)
      {
        ChatService.ErrorMessage(player, "Der Patient ist zu weit entfernt");
        return;
      }

      if (price < 10 || price > 100)
      {
        ChatService.ErrorMessage(player, "Der Preis muss zwischen 10$ und 100$ liegen");
        return;
      }

      if (!player.IsDuty())
      {
        ChatService.ErrorMessage(player, "Du bist nicht im Dienst");
        return;
      }

      if (player.IsInVehicle)
      {
        ChatService.ErrorMessage(player, "Du kannst in einem Auto nicht heilen");
        return;
      }
      if (player == target)
      {
        ChatService.ErrorMessage(player, "Du kannst dich nicht selbst heilen");
        return;
      }

      if (target.Health == 100)
      {
        ChatService.ErrorMessage(player, "Der Spieler ist bereits voll geheilt");
        return;
      }

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

      if (targetUser.Handmoney >= price)
      {
        targetUser.Handmoney -= price;
      }
      else
      {
        int bankMoney = price - targetUser.Handmoney;
        targetUser.Handmoney = 0;
        targetUser.BankAccount.Balance -= bankMoney;
      }

      dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == 2).First().BankAccount.Balance += price;

      dbContext.SaveChanges();

      target.Health = 100;
      target.SendNotification($"Du wurdest von ~g~{player.Name} ~s~ für ~g~{price.ToMoneyString()} geheilt.", false);
      player.SendNotification($"Du hast ~g~{target.Name} ~s~ für {price.ToMoneyString()} geheilt.", false);
      player.TriggerEvent("delHealTask");
    }

    #endregion Sanitäter Commands

    #region Staatsfraktionen (LSPD / FBI) Commands

    [Command("wanted", "~m~Benutzung: ~s~/wa(nted) [Spieler] [Anzahl] [Grund]", Alias = "wa", GreedyArg = true)]
    public void CmdFactionWanted(Player player, string nameOrId, int amount, string reason)
    {
      User user = player.GetUser();
      if (user == null || (user.FactionId != 1 && user.FactionId != 3))
      {
        ChatService.NotAuthorized(player);
        return;
      }

      if (!player.IsDuty())
      {
        ChatService.ErrorMessage(player, "Du bist nicht im Dienst");
        return;
      }

      if (amount <= 0)
      {
        ChatService.ErrorMessage(player, "Es muss eine positive Wantedanzahl vergeben werden");
        return;
      }

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

      User targetCop = target.GetUser();
      if (targetCop.FactionId == 1 || targetCop.FactionId == 3)
      {
        ChatService.ErrorMessage(player, "Du kannst dieser Person keine Wanteds geben");
        return;
      }
      User targetUser = target.GetUser();
      targetUser.GiveWanteds(player, amount, reason);
    }

    [Command("clear", "~m~Benutzung: ~s~/clear [Spieler] [Grund]", GreedyArg = true)]
    public void CmdFactionClear(Player player, string nameOrId, string reason)
    {
      User user = player.GetUser();
      if (user == null || (user.FactionId != 1 && user.FactionId != 3))
      {
        ChatService.NotAuthorized(player);
        return;
      }

      if (!player.IsDuty())
      {
        ChatService.ErrorMessage(player, "Du bist nicht im Dienst");
        return;
      }

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

      using (var dbContext = new DatabaseContext())
      {
        User targetUser = target.GetUser(dbContext);
        if (targetUser.Wanteds == 0)
        {
          ChatService.ErrorMessage(player, "Der Spieler hat keine Wanteds");
          return;
        }
        ChatService.SendMessage(target, "!{#8181E9}Deine Akte wurde von " + player.Name + " gelöscht. Grund: " + reason);
        ChatService.BroadcastFaction("!{#8181E9}HQ: Die Akte von " + target.Name + " wurde von " + player.Name + " gelöscht. Grund: " + reason + ".", new List<int>() { 1, 3 });
        target.SetSharedData("blipColor", 0);
        switch (targetUser.FactionId)
        {
          case null:
            player.SetSharedData("blipColor", 0);
            player.SetSharedData("nameTagColor", 0);
            break;

          case 0:
            player.SetSharedData("blipColor", 0);
            break;

          case 8:
            target.SetSharedData("blipColor", 83);
            break;

          case 7:
            target.SetSharedData("blipColor", 52);
            break;

          case 4:
            target.SetSharedData("blipColor", 5);
            break;
          case 9:
            target.SetSharedData("blipColor", 25);
            break;
        }
        target.TriggerEvent("jailTime", 0);
        if (targetUser.FactionId != null) player.SetSharedData("nameTagColor", targetUser.FactionId);
        targetUser.Wanteds = 0;
        dbContext.SaveChanges();
      }
    }

    #endregion Staatsfraktionen (LSPD / FBI) Commands
    #region Weazel News
    [Command("eventchat", "~m~Benutzung: ~s~/eventchat [Typ = ~g~on~s~, ~g~off~s~]", GreedyArg = true)]
    public void CmdFactionNREventchat(Player player, string onoff)
    {

      switch (onoff.ToLower())
      {
        case "on":
          NAPI.Pools.GetAllPlayers().ForEach(p =>
          {
            p.SetData("eventAllowed", true);
          });
          player.SendNotification("Der Eventchat ist nun an.");
          break;

        case "off":
          NAPI.Pools.GetAllPlayers().ForEach(p =>
          {
            p.ResetData("eventAllowed");
          });
          player.SendNotification("Der Eventchat ist nun aus.");
          break;
        
      }
      

    }


    #endregion Weazel News
  }

}
