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

namespace ReallifeGamemode.Server.Gangwar
{
  public class Turf
  {
    public int TurfID { get; set; }
    public string TurfName { get; set; }
    public int Color { get; set; }
    public string Owner { get; set; }
    public string Attacker { get; set; }
    public int Att_Score { get; set; }
    public int Def_Score { get; set; }
    public string status { get; set; }
    public int value { get; set; }
    public int maxValue { get; set; }
    public bool surplus { get; set; }
    public List<Player> playerInside { get; set; }
    public Timer timer { get; set; }
    public Player[] playerInGangwar { get; set; }
    public int timerCount;

    public Turf(int TurfID, string TurfName, int color, string Owner, int value, int maxValue, bool surplus)
    {
      this.TurfID = TurfID;
      this.TurfName = TurfName;
      this.Color = color;
      this.Owner = Owner;
      this.value = value;
      this.maxValue = maxValue;
      this.surplus = surplus;
      this.Attacker = null;
      this.Att_Score = 1;
      this.Def_Score = 0;
      this.status = "normal";
      this.timer = null;
      this.playerInside = new List<Player>();
      this.timerCount = 0;
    }

    public int getId()
    {
      return this.TurfID;
    }

    public int getValue()
    {
      return this.value;
    }

    public void setValue(int value)
    {
      this.value = value;
    }

    public void addValue(int addedValue)
    {
      this.value += addedValue;
    }

    public int getMaxValue()
    {
      return maxValue;
    }

    public bool getSurplus()
    {
      return surplus;
    }

    public void setSurplus(bool surplus)
    {
      this.surplus = surplus;
    }

    public string getName()
    {
      return this.TurfName;
    }

    public int getColor()
    {
      return this.Color;
    }

    public string getOwner()
    {
      return this.Owner;
    }

    public string getAttacker()
    {
      return this.Attacker;
    }

    private void TurfTick()
    {
      this.timer = new System.Timers.Timer(1000);

      this.timer.Elapsed += Tick;
      this.timer.AutoReset = true;
      this.timer.Enabled = true;
      this.timerCount = 0;
    }

    private void ReloadGangTurfs()
    {
      NAPI.Task.Run(() =>
      {
        Gangwar.loadTurfs();
        Gangwar.loadTurfs_ToAllPlayers();
      }, delayTime: 2000);
    }

    private void Tick(object sender, System.Timers.ElapsedEventArgs e)
    {
      if (this.status == "attack")
      {
        update();
      }
    }

    private void update()
    {
      NAPI.Task.Run(() =>
      {
        foreach (Player gangwarPlayer in this.playerInGangwar)
        {
          if (!gangwarPlayer.IsLoggedIn())
          {
            continue;
          }

          gangwarPlayer.TriggerEvent("GangwarScore", this.Attacker, this.Owner, this.Att_Score, this.Def_Score, Gangwar.GANGWAR_TOTAL_TIME - timerCount);
        }
        /*if (this.Att_Score >= 200)
        {
          this.takeOver(this.Attacker);
        }
        else if (this.Def_Score >= 200)
        {
          this.takeOver(this.Owner);
        }*/
        timerCount += 1;
        if (timerCount >= Gangwar.GANGWAR_TOTAL_TIME)
        {
          if (this.Att_Score > this.Def_Score)
          {
            foreach (Player gangwarPlayer in this.playerInGangwar)
            {
              if (!gangwarPlayer.IsLoggedIn())
              {
                continue;
              }

              gangwarPlayer.TriggerEvent("GangwarScore", this.Attacker, this.Owner, 0, 0, 0);
            }
            this.takeOver(this.Attacker);
            this.Def_Score = 0;
            return;
          }
          else if (this.Att_Score < this.Def_Score)
          {
            foreach (Player gangwarPlayer in this.playerInGangwar)
            {
              gangwarPlayer.TriggerEvent("GangwarScore", this.Attacker, this.Owner, 0, 0, 0);
            }
            this.takeOver(this.Owner);
            this.Att_Score = 0;
            return;
          }
          else if (this.Def_Score == this.Att_Score)
          {
            foreach (Player gangwarPlayer in this.playerInGangwar)
            {
              if (!gangwarPlayer.IsLoggedIn())
              {
                continue;
              }

              gangwarPlayer.TriggerEvent("GangwarScore", this.Attacker, this.Owner, 0, 0, 0);
            }
            this.takeOver(this.Owner);
            this.Def_Score = 0;
            return;
          }
        }
      });
    }


    public void enter(Player client)
    {
      User user = client.GetUser();
      if(user == null || user.FactionId == null)
      {
        return;
      }

      if (this.status == "attack")
      {
        if (user.Faction.Name != getOwner() && user.Faction.Name != getAttacker())
          return;
        Player gPlayer = playerInGangwar.Where(c => c != null && !c.Handle.IsNull && c.IsLoggedIn() && c.Handle.Value == client.Handle.Value).FirstOrDefault();
        if (gPlayer == null)
        {
          using (var dbContext = new DatabaseContext())
          {
            ChatService.BroadcastFaction("~y~[GANGWAR] ~r~" + client.Name + "~w~ ist nicht im Gangwar beteiligt!", dbContext.Factions.Where(f => f.Name == getOwner()).FirstOrDefault());
            ChatService.BroadcastFaction("~y~[GANGWAR] ~r~" + client.Name + "~w~ ist nicht im Gangwar beteiligt!", dbContext.Factions.Where(f => f.Name == getAttacker()).FirstOrDefault());
          }
          return;
        }
      }


      if (playerInside.Find(c => c == client) == null)
      {
        playerInside.Add(client);
        client.SetData("GotInsideOfTurf", true);
      }
    }

    public void leave(Player client)
    {
      if (playerInside.Find(c => c == client) != null)
      {
        this.playerInside = this.playerInside.Where(c => c != client).ToList();
        if (this.status != "attack")
          client.ResetData("GotInsideOfTurf");
      }
    }

    public void takeOver(string FactionName)
    {
      this.timer.Stop();
      this.timer = null;
      using (var dbContext = new DatabaseContext())
      {
        Player[] owners = NAPI.Pools.GetAllPlayers().Where(c => c.IsLoggedIn() && c.GetUser().Faction?.Name == this.Owner).ToArray();
        Player[] attackers = NAPI.Pools.GetAllPlayers().Where(c => c.IsLoggedIn() && c.GetUser().Faction?.Name == this.Attacker).ToArray();

        Faction ownerFaction = dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Name == getOwner()).FirstOrDefault();
        Faction attackerFaction = dbContext.Factions.Include(f => f.BankAccount).Where(f => f.Name == getAttacker()).FirstOrDefault();

        if (ownerFaction == null || attackerFaction == null)
        {
          return;
        }

        string takeOverMessage = string.Empty;

        if (getOwner() == FactionName)
        {
          takeOverMessage = $"~y~[GANGWAR] ~w~Die {getOwner()} konnten ihr Gebiet ~g~{getName()} ~w~ erfolgreich gegen die {getAttacker()} verteidigen.";
          //ChatService.BroadcastFaction("~y~[GANGWAR] ~w~Deine Fraktion hat erfolgreich das Gebiet  ~g~" + getName() + "~w~ verteidigt.", ownerFaction);
          //ChatService.BroadcastFaction("~y~[GANGWAR] ~w~Deine Fraktion hat den Angrif auf das Gebiet ~r~" + getName() + "~w~ verloren.", attackerFaction);

          foreach (var o in owners)
          {
            o.TriggerEvent("CLIENT:win");
          }
          foreach (var a in attackers)
          {
            a.TriggerEvent("CLIENT:loose");
          }
          ownerFaction.BankAccount.Balance += 15000;
        }
        else if (getOwner() != FactionName)
        {
          takeOverMessage = $"~y~[GANGWAR] ~w~Die {getAttacker()} konnten das Gebiet ~g~{getName()} ~w~ der {getOwner()} erfolgreich erobern.";
          //ChatService.BroadcastFaction("~y~[GANGWAR] ~w~Deine Fraktion konnte das Gebiet ~r~" + getName() + "~w~ nicht verteidigen.", ownerFaction);
          //ChatService.BroadcastFaction("~y~[GANGWAR] ~w~Deine Fraktion konnte erfolgreich das Gebiet ~g~" + getName() + "~w~ erobern.", attackerFaction);
          foreach (var o in owners)
          {
            if (o != null)
              o.TriggerEvent("CLIENT:loose");
          }
          foreach (var a in attackers)
          {
            if (a != null)
              a.TriggerEvent("CLIENT:win");
          }
          this.Owner = FactionName;
          attackerFaction.BankAccount.Balance += 10000;
          Turfs turf = dbContext.Turfs.Where(t => t.Id == getId()).FirstOrDefault();
          turf.Owner = this.Owner;
          turf.FactionId = attackerFaction.Id;
        }
        ChatService.Broadcast(takeOverMessage);
        dbContext.SaveChanges();
      }
      this.Attacker = null;
      foreach (var c in playerInGangwar)
      {
        if (!c.IsLoggedIn())
        {
          continue;
        }

        c.TriggerEvent("CLIENT:setAttackBlip", false, TurfID);
        c.ResetData("inGangWar");
        c.ResetData("GotInsideOfTurf");
        c.TriggerEvent("resetKillcounter");
      }
      this.playerInGangwar = null;
      this.status = "conquered";

      NAPI.ClientEvent.TriggerClientEventForAll("CLIENT:Turf_Conquered", JsonConvert.SerializeObject(this.TurfID), JsonConvert.SerializeObject(this.status), JsonConvert.SerializeObject(this.Owner));
      Gangwar.loadTurfs();
      Gangwar.loadTurfs_ToAllPlayers();
      ReloadGangTurfs();
    }

    public void attack(string attacker)
    {
      Player[] usersInGangwar;
      using (var context = new DatabaseContext())
      {
        var onlinePlayers = NAPI.Pools.GetAllPlayers().Where(c => c.IsLoggedIn()).Select(c => c.Name);

        List<Player> ownersInGangwar = context.Users.Include(u => u.Faction).Where(u => onlinePlayers.Contains(u.Name) && u.Faction.Name == getOwner()).Select(u => u.Player).ToList();
        List<Player> attackersInGangwar = context.Users.Include(u => u.Faction).Where(u => onlinePlayers.Contains(u.Name) && u.Faction.Name == attacker).Select(u => u.Player).ToList();

        if (ownersInGangwar.Count < 0 && attackersInGangwar.Count < 0)
        {
          List<Player> leaders = context.Users.Include(u => u.Faction).Where(u => onlinePlayers.Contains(u.Name) && u.Faction.Name == attacker && u.FactionLeader).Select(u => u.Player).ToList();
          foreach (var l in leaders)
          {
            ChatService.ErrorMessage(l, "Du kannst keinen Gangwar starten, weil in der gegnerischen Fraktion zu wenig Spieler online sind");
          }

          return;
        }
        List<Player> inGangwar = new List<Player>(ownersInGangwar.Concat(attackersInGangwar));

        usersInGangwar = inGangwar.ToArray();
      }
      if (this.status == "normal")
      {
        if (this.timer != null)
        {
          this.timer.Stop();
          this.timer = null;
        }
        this.Attacker = attacker;
        this.status = "attack";

        List<Player> clientsInGangwar = new List<Player>();

        foreach (var u in usersInGangwar)
        {
          u.TriggerEvent("CLIENT:setAttackBlip", true, TurfID);
          u.TriggerEvent("GangwarScore", this.Attacker, this.Owner, this.Att_Score, this.Def_Score);
          u.SetData("inGangWar", getId());
          clientsInGangwar.Add(u);
        }

        ChatService.Broadcast("~y~[GANGWAR]~w~ Die " + getAttacker() + " haben das Gebiet ~y~" + TurfName + "~w~ der " + getOwner() + " angegriffen.");

        playerInGangwar = clientsInGangwar.ToArray();
        NAPI.ClientEvent.TriggerClientEventForAll("CLIENT:Turf_Update", JsonConvert.SerializeObject(this.TurfID), JsonConvert.SerializeObject(this.status), JsonConvert.SerializeObject(this.Owner), JsonConvert.SerializeObject(this.Attacker));
        this.TurfTick();
      }
    }

    public void setKill(string FactionName)
    {
      if (getOwner() == FactionName)
      {
        NAPI.Util.ConsoleOutput($"GangwarKill - Adding Point for Faction {getAttacker()}"); 
        Att_Score += 1;
      }
      else if (getAttacker() == FactionName)
      {
        NAPI.Util.ConsoleOutput($"GangwarKill - Adding Point for Faction {getOwner()}");
        Def_Score += 1;
      }
    }
  }
}
