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

/**
 * @overview Life of German Reallife - Server Factions Medic Medic.cs
 * @author VegaZ
 * @copyright (c) 2008 - 2018 Life of German
 */

namespace ReallifeGamemode.Server.Factions.Medic
{
  public class Medic : Script
  {
    public static List<MedicTask> ReviveTasks = new List<MedicTask>();
    public static List<MedicTask> HealTasks = new List<MedicTask>();
    public static List<MedicTask> FireTasks = new List<MedicTask>();
    public static List<HealDecision> HealDecisions = new List<HealDecision>();
    public static int ReviveIncome = 100;
    public static int dutyMedics = 0;
    public static int reviveTaskTime = 120;

    public class HealDecision
    {
      public Player dMedic;
      public Player dTarget;
      public int dPrice;
    }
    
    public static void AddTaskToList(MedicTask task)
    {
      if (task == null)
      {
        return;
      }

      switch (task.Type)
      {
        case MedicTaskType.REVIVE:
          ReviveTasks.Add(task);
          break;

        case MedicTaskType.HEAL:
          HealTasks.Add(task);
          break;

        case MedicTaskType.FIRE:
          FireTasks.Add(task);
          break;
      }
    }

    public static void RemoveTaskFromList(MedicTask task)
    {
      if (task == null)
      {
        return;
      }

      switch (task.Type)
      {
        case MedicTaskType.REVIVE:
          ReviveTasks.Remove(task);
          break;

        case MedicTaskType.HEAL:
          HealTasks.Remove(task);
          break;

        case MedicTaskType.FIRE:
          FireTasks.Remove(task);
          break;
      }
    }

    public static void UpdateReviveSperre()
    {
      var allRevivedPlayers = NAPI.Pools.GetAllPlayers().Where(c => c.HasData("reviveSperre"));
      foreach(var player in allRevivedPlayers)
      {
        var timeLeft = player.GetData<int>("reviveSperre");

        if (timeLeft > 0)
        {
          player.SetData("reviveSperre", timeLeft - 1);
        }
        else
        {
          player.ResetData("reviveSperre");
        }
      }
    }

    public static void CheckHealDecisionDistance()
    {
      foreach(var healAuftrag in HealDecisions.ToList())
      {
        if (healAuftrag.dMedic.Position.DistanceTo(healAuftrag.dTarget.Position) > 5)
        {
          healAuftrag.dMedic.SendNotification("~r~Die Heal-Anfrage wurde abgebrochen");
          healAuftrag.dTarget.SendNotification("~r~Die Heal-Anfrage wurde abgebrochen");
          healAuftrag.dTarget.ResetData("healDecision");
          var activeDecision = HealDecisions.FirstOrDefault(d => d.dTarget == healAuftrag.dTarget);
          HealDecisions.Remove(healAuftrag);
        }
      }
    }

    public static void MakeHealDecision(Player target, bool decision)
    {
      var activeDecision = HealDecisions.FirstOrDefault(d => d.dTarget == target);
      if(decision == false)
      {
        activeDecision.dMedic.SendNotification(activeDecision.dTarget.Name + " hat den Heal ~r~abgelehnt",false);
        activeDecision.dTarget.SendNotification("Du hast den Heal ~r~abgelehnt", false);
      }
      else
      {
        activeDecision.dMedic.SendNotification(activeDecision.dTarget.Name + " hat den Heal ~g~akzeptiert", false);
        activeDecision.dTarget.SendNotification("Du hast den Heal ~g~akzeptiert", false);
        using var dbContext = new DatabaseContext();
        {
          User targetUser = target.GetUser(dbContext);

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

          dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Id == 2).First().BankAccount.Balance += activeDecision.dPrice / 2;
          activeDecision.dMedic.GetUser(dbContext).Wage += activeDecision.dPrice / 2;

          dbContext.SaveChanges();
        }
        activeDecision.dTarget.SafeSetHealth(100);
        activeDecision.dTarget.SendNotification($"Du wurdest von ~g~{activeDecision.dMedic.Name} ~s~ für ~g~{activeDecision.dPrice.ToMoneyString()} geheilt", false);
        activeDecision.dMedic.SendNotification($"Du hast ~g~{activeDecision.dTarget.Name} ~s~ für {activeDecision.dPrice.ToMoneyString()} geheilt", false);
        delHealTask(activeDecision.dTarget); 
      }
      HealDecisions.Remove(activeDecision);
    }

    public static void UpdateTaskTimeLeft()
    {
      if (ReviveTasks.Count != 0)
      {
        DateTime actualTime = DateTime.Now;
        foreach (var task in ReviveTasks)
        {
          if (task.MedicName != "none")
          {
            var taskTimeLeft = Math.Abs((int)(((DateTimeOffset)actualTime).ToUnixTimeSeconds() - ((DateTimeOffset)task.Time).ToUnixTimeSeconds() - reviveTaskTime));
            task.TimeLeft = taskTimeLeft;
            var medic = PlayerService.GetPlayerByNameOrId(task.MedicName);
            if(medic != null) medic.TriggerEvent("setTaskTimeLeft", taskTimeLeft);
          }
        }
      }
    }

    public static void UpdateHealTaskCheckpoint()
    {
      if (HealTasks.Count != 0)
      {
        foreach(var task in HealTasks)
        {
          if(task.MedicName != "none")
          {
            var medic = PlayerService.GetPlayerByNameOrId(task.MedicName);
            var victim = PlayerService.GetPlayerByNameOrId(task.Victim);

            if(medic == null || victim == null)
            {
              continue;
            }

            task.Position = victim.Position;
            medic.TriggerEvent("updateHealCheckpoint", victim.Position.X, victim.Position.Y, victim.Position.Z);
          }
        }
      }
    }

    public static void RemoveOldHealTask()
    {
      if (HealTasks.Count != 0)
      {
        foreach (var task in HealTasks.ToList())
        {
          var taskActive = DateTime.Now - task.Time;
          if (taskActive > TimeSpan.FromMinutes(10) && task.MedicName == "none")
          {
            delHealTask(PlayerService.GetPlayerByNameOrId(task.Victim));
          }
        }
      }
    }

    [RemoteEvent("loadMedicTasks")]
    public void LoadMedicTasks(Player player, int type)
    {
      switch (type)
      {
        case 0:
          player.TriggerEvent("showMedicTasks", 0, JsonConvert.SerializeObject(ReviveTasks));
          break;

        case 1:
          player.TriggerEvent("showMedicTasks", 1, JsonConvert.SerializeObject(HealTasks));
          break;

        case 2:
          player.TriggerEvent("showMedicTasks", 2, JsonConvert.SerializeObject(FireTasks));
          break;
      }
    }

    [RemoteEvent("updateMedicTask")]
    public void UpdateMedicTasks(Player player, int type, string victim, bool isRemoved)
    {
      Player target = PlayerService.GetPlayerByNameOrId(victim);
      switch (type)
      {
        case 0:
          var reviveTask = ReviveTasks.FirstOrDefault(t => t.Victim == victim);
          if (isRemoved)
          {
            reviveTask.MedicName = "none";
          }
          else 
          {
            reviveTask.MedicName = player.Name;
          }
          
          ChatService.BroadcastFaction("~y~[MEDIC] ~w~" + player.Name + " hat den Reviveauftrag von " + victim + " angenommen.", new List<int>() { 2 });
          target.SendNotification("~w~Sanitäter~g~ " + player.Name + "~w~ eilt zur Rettung.");
          break;

        case 1:
          var healTask = HealTasks.FirstOrDefault(t => t.Victim == victim);
          if (isRemoved)
          {
            healTask.MedicName = "none";
          }
          else
          {
            healTask.MedicName = player.Name;
            ChatService.BroadcastFaction("~y~[MEDIC] ~w~" + player.Name + " hat den Healauftrag von " + victim + " angenommen.", new List<int>() { 2 });
            target.SendNotification("~w~Sanitäter~g~ " + player.Name + "~w~ hat deinen Auftrag angenommen.");
          }         
          break;

        case 2:
          //FireTasks[index].MedicName = player.Name;
          break;
      }
    }

    [RemoteEvent("delHealTask")]
    public static void delHealTask(Player player)
    {
      MedicTask task = HealTasks.FirstOrDefault(t => t.Victim == player.Name);
      if (task != null)
      {       
        player.SetData("healauftrag", false);
        var medicPlayer = PlayerService.GetPlayerByNameOrId(task.MedicName);
        if (medicPlayer != null)
        {
          medicPlayer.TriggerEvent("destroyMedicTaskCheckpoint");
        }
        RemoveTaskFromList(task);
      }
    }

    [RemoteEvent("delHealTaskAsMedic")]
    public static void delHealTaskAsMedic(Player player, string victimName)
    {
      MedicTask task = HealTasks.FirstOrDefault(t => t.Victim == victimName);
      RemoveTaskFromList(task);
      player.SetData("healauftrag", false);

    }

    public static void delReviveTask(Player target)
    {
      MedicTask task = ReviveTasks.FirstOrDefault(t => t.Victim == target.Name);
      if (task != null)
      {
        var medicPlayer = PlayerService.GetPlayerByNameOrId(task.MedicName);
        if (task.MedicName != "none")
        {
          medicPlayer.TriggerEvent("destroyMedicTaskCheckpoint");
        }
        RemoveTaskFromList(task);
      }
    }
    [RemoteEvent("GetDutyMedics")]
    public static void UpdateDutyMedics()
    {

      dutyMedics = NAPI.Pools.GetAllPlayers().Where(c => c.IsDuty() == true && c.GetUser().FactionId == 2).ToList().Count;
      NAPI.ClientEvent.TriggerClientEventForAll("updateDutyMedics", dutyMedics);
    }
  }
}
