using System;
using System.Collections.Generic;
using System.Linq;
using GTANetworkAPI;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Extensions;
using ReallifeGamemode.Server.Factions.Medic;
using ReallifeGamemode.Server.Inventory;
using ReallifeGamemode.Server.Inventory.Interfaces;
using ReallifeGamemode.Server.Managers;
using ReallifeGamemode.Server.Services;
using ReallifeGamemode.Server.Types;
using ReallifeGamemode.Server.Util;
using ReallifeGamemode.Server.Wanted;
using ReallifeGamemode.Services;

/**
* @overview Life of German Reallife - Events Death (Death.cs)
* @author VegaZ
* @copyright (c) 2008 - 2018 Life of German
*/

namespace ReallifeGamemode.Server.Events
{
  public class Death : Script
  {
    private readonly Dictionary<Player, DateTime> lastPlayerDeathTime = new Dictionary<Player, DateTime>();

    [ServerEvent(Event.PlayerDeath)]
    public void OnPlayerDeath(Player player, Player killer, uint reason)
    {
      if (lastPlayerDeathTime.TryGetValue(player, out DateTime lastDeathTime) && DateTime.Now - lastDeathTime < TimeSpan.FromSeconds(5))
      {
        return;
      }

      lastPlayerDeathTime[player] = DateTime.Now;

      if (!player.IsLoggedIn())
      {
        player.Kick();
        return;
      }

      using var dbContext = new DatabaseContext();

      User user = player.GetUser(dbContext);

      PositionManager.cuffPoints.Remove(player);
      player.TriggerEvent("abortSpawnschutz");

      //TODO: Zum Full Release entfernen
      if (player.HasData("togdeath"))
      {
        ChatService.SendMessage(player, "Du bist durch " + (killer?.Name ?? "Niemanden") + " gestorben: " + reason.ToString());
      }
      int? killerId = null;
      float killerPosX = -1;
      float killerPosY = -1;
      float killerPosZ = -1;
      float killerHeading = -1;

      NAPI.Util.ConsoleOutput($"OnPlayerDeath - Player {player.Name} died - Killer = {(killer != null ? killer.Name : "no killer found")}");

      if (killer.IsLoggedIn())
      {
        NAPI.Util.ConsoleOutput($"OnPlayerDeath - Player {player.Name} died - Killer is logged in");

        var killerUser = killer.GetUser(dbContext);
        if (killerUser != null)
        {
          NAPI.Util.ConsoleOutput($"OnPlayerDeath - Player {player.Name} died - Killer GetUser is not null");
          killerId = killerUser.Id;
          killerPosX = killer.Position.X;
          killerPosY = killer.Position.Y;
          killerPosZ = killer.Position.Z;
          killerHeading = killer.Heading;

          var playerInGangwar = player.HasData("inGangWar");
          var killerInGangwar = killer.HasData("inGangWar");

          NAPI.Util.ConsoleOutput($"OnPlayerDeath - Player {player.Name} died - PlayerInGangwar = {playerInGangwar}, KillerInGangwar = {killerInGangwar}");
          if (playerInGangwar && killerInGangwar)
          {
            NAPI.Util.ConsoleOutput($"OnPlayerDeath - Player {player.Name} died - Both players in gangwar, setting kill");
            Gangwar.Gangwar.GangwarKill(killer, player);
          }

          if (player != killer)
          {
            Autowanted.Check_AutoWanted(killer, player);
            string message = "~y~[HINWEIS]: " + killer.Name + " hat " + player.Name + " getötet (" + Managers.WeaponManager.GetCauseOfDeathByHash(reason) + ")";
            ChatService.BroadcastAdmin(message, AdminLevel.ADMIN, shouldSendMessage: admin =>
            {
              return admin.HasData("togdeath");
            });
          }
        }
      }

      var dead = new Database.Entities.Logs.Death
      {
        VictimId = player.GetUser().Id,
        KillerId = killerId,
        KillerPositionX = killerPosX,
        KillerPositionY = killerPosY,
        KillerPositionZ = killerPosZ,
        KillerHeading = killerHeading,
        VictimPositionX = player.Position.X,
        VictimPositionY = player.Position.Y,
        VictimPositionZ = player.Position.Z,
        VictimHeading = player.Heading,
        CauseOfDeath = reason.ToString()
      };
      dbContext.DeathLogs.Add(dead);

      player.ClearAnimation();
      player.ClearAttachments();
      List<UserItem> fItem = dbContext.UserItems.Where(u => u.UserId == user.Id).ToList();
      foreach (var item in fItem)
      {
        IItem iItem = InventoryManager.GetItemById(item.ItemId);
        if (iItem is IWeaponDealItem obj)
        {
          int amount = item.Amount;

          Vector3 dropPosition = PlayerExtension.GetPositionFromPlayer(player, 0.6f, 0);
          //new Vector3(player.Position.X, player.Position.Y, player.Position.Z - 0.8f);
          Random r = new Random();
          GTANetworkAPI.Object grndObject;
          Vector3 textPos = dropPosition;

          dropPosition.Z -= 1.05f;
          grndObject = NAPI.Object.CreateObject(3666746839, dropPosition, new Vector3(0, 0, r.Next(0, 360)), 255, 0);

          GroundItem grndItem = new GroundItem { ItemId = iItem.Id, Amount = amount, Position = dropPosition };
          TextLabel grndTxtLbl = NAPI.TextLabel.CreateTextLabel(iItem.Name + " ~s~(~y~" + amount + "~s~)", textPos, 5, 0.5f, 4, new Color(255, 255, 255), false, 0);
          GroundItem.AddGroundItem(grndItem, grndObject, grndTxtLbl);

          dbContext.Remove(item);
        }
      }

      bool copNearby = NAPI.Pools.GetAllPlayers().Any(u => u.IsDuty() && u.IsAlive() && u.Position.DistanceToSquared(player.Position) <= 100 * 100);

      if (user.Wanteds > 0 && (!player.HasData("GotInsideOfTurf") || !player.GetData<bool>("GotInsideOfTurf")))
      {
        if (copNearby)
        {
          user.SetJailTime(true, dbContext);
          Jail.Check_PutBehindBars(user, JailInLocations.InCell);
          ChatService.HQMessage(user.Name + " wurde ins Gefängnis eingeliefert.");
        }
        else
        {
          ChatService.HQMessage(user.Name + " wurde soeben ins Krankenhaus eingeliefert.");
        }
      }

      Medic.delHealTask(player);

      if (user.JailTime <= 0)
      {
        user.Dead = true;

        if (user.Wanteds == 0 && !player.HasData("reviveSperre") && player.GetData<bool>("isDead") == false && !player.HasData("inGangWar") && player.GetData<bool>("SAdminduty") == false)
        {
          //MEDIC AUFTRAG
          MedicTask reviveTask = new MedicTask()
          {
            Victim = player.Name,
            Position = player.Position,
            CauseOfDeath = reason.ToString(),
            Caller = null,
            Description = "Gestorben",
            Time = DateTime.Now,
            Type = MedicTaskType.REVIVE,
            MedicName = "none"
          };
          Medic.AddTaskToList(reviveTask);
          ChatService.BroadcastFaction("~y~[MEDIC] ~w~" + player.Name + " ist soeben verstorben.", new List<int>() { 2 });
        }

        player.SetData("isDead", true);

        if (player.GetUser().IsAdmin(AdminLevel.ADMIN) == true)
        {
          player.TriggerEvent("startDeathTimer", true);
        }
        else
        {
          player.TriggerEvent("startDeathTimer", false);
        }

        //TODO PICTURE NOTIFICATION + SOUND für Medics
      }
      Job.JobBase job = Managers.JobManager.GetJob(player.GetUser().JobId ?? -1);
      if (job != null)
      {
        if (job.GetUsersInJob().Contains(player))
        {
          if (player.Vehicle != null)
          {
            if (player.Vehicle.GetServerVehicle() is JobVehicle vehJ)
            {
              player.Vehicle.ResetData("timerJobVehicleRespawn");
              ServerVehicle sVeh = Managers.VehicleManager.GetServerVehicleFromVehicle(player.Vehicle);
              ServerVehicleExtensions.Spawn(sVeh, player.Vehicle);
            }
          }
          if (Managers.JobManager.playerTimersJobVehicleRespawn.ContainsKey(player))
          {
            Managers.JobManager.playerTimersJobVehicleRespawn[player].Stop();
          }
          job.StopJob(player);
          ChatService.SendMessage(player, $"~y~[JOB]~s~ Du hast deinen Job ~o~{job.Name}~s~ beendet.");
          if (player.GetData<bool>("HatRoute") == true)
          {
            CheckPointHandle.DeleteCheckpoints(player);
            player.ResetData("HatRoute");
          }
        }
      }
      dbContext.SaveChanges();
    }

    [RemoteEvent("RespawnPlayerAtHospital")]
    public void RespawnPlayerAtHospital(Player player)
    {
      player.SetData("isDead", false);
      using (var dbContext = new DatabaseContext())
      {
        User u;
        u = player.GetUser(dbContext);
        u.Dead = false;
        dbContext.SaveChanges();
      }
      player.RemoveAllWeapons();
      MedicTask task = Medic.ReviveTasks.FirstOrDefault(t => t.Victim == player.Name);

      if (task != null)
      {
        Player medicPlayer = PlayerService.GetPlayerByNameOrId(task.MedicName);
        Medic.RemoveTaskFromList(task);
      }

      player.SafeTeleport(new Vector3(-495.45, -336.33, 34.5), 0, true);
      
      player.TriggerEvent("enableSpawnschutz");
    }
  }
}
