using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using GTANetworkAPI;
using Newtonsoft.Json;
using ReallifeGamemode.Database.Entities;
using ReallifeGamemode.Database.Models;
using ReallifeGamemode.Server.Classes;
using ReallifeGamemode.Server.Common;
using ReallifeGamemode.Server.Core.API;
using ReallifeGamemode.Server.Core.Events;
using ReallifeGamemode.Server.Core.RageMP;
using ReallifeGamemode.Server.DrivingSchool;
using ReallifeGamemode.Server.Managers;
using ReallifeGamemode.Server.Util;
using ReallifeGamemode.Services;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using ReallifeGamemode.Server.newbie;
using System;
using System.IO;

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

namespace ReallifeGamemode.Server
{
  public class Main : Script
  {
    public static readonly Vector3 DEFAULT_SPAWN_POSITION = new Vector3(-1033.93603515625, -2731.572998046875, 13.756634712219238);
    public static readonly float DEFAULT_SPAWN_HEADING = -32.23991012573242f;

    public static readonly CultureInfo SERVER_CULTURE = new CultureInfo("de-DE");

    private Core.Events.EventHandler eventHandler;

    private readonly Mutex logMutex = new Mutex(false, "RAGE_MAIN_LOG_MUTEX");

    public Main()
    {
      AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    }

    [ServerEvent(Event.ResourceStart)]
    public void OnResourceStart()
    {
      try
      {
        if (System.Environment.GetEnvironmentVariable("RAGEMP_UPDATE_DATABASE_ON_STARTUP", System.EnvironmentVariableTarget.User) == "true")
        {
          using var dbContext = new DatabaseContext(true);

          var pendingMigrations = dbContext.Database.GetPendingMigrations();

          if (!pendingMigrations.Any())
          {
            System.Console.WriteLine("No migrations to apply");
          }
          else
          {
            System.Console.WriteLine("Applying {0} migrations", pendingMigrations.Count());
            foreach (var migration in pendingMigrations)
            {
              System.Console.WriteLine("\t{0}", migration);
            }

            try
            {
              dbContext.Database.Migrate();
            }
            catch (System.Exception e)
            {
              System.Console.WriteLine("Error while updating database: {0}", e.ToString());
              System.Console.ReadLine();
              System.Environment.Exit(1);
            }

            System.Console.WriteLine("Migrations successfull");
          }
        }

        var methods = Assembly.GetExecutingAssembly()
                        .GetTypes()
                        .SelectMany(t => t.GetMethods())
                        .Where(m => m.GetCustomAttributes(typeof(CommandAttribute), false).Length > 0)
                        .ToArray();

        var cmdAttributes = methods.Select(c => c.GetCustomAttribute<CommandAttribute>()).ToList();

        var registeredCommands = cmdAttributes.Select(c => c.CommandString).ToList();
        cmdAttributes.Where(c => c.Alias?.Any() ?? false).ToList().ForEach(a =>
        {
          registeredCommands.AddRange(a.Alias.Split(','));
        });

        IAPI apiInstance = new RageAPI();
        eventHandler = new Core.Events.EventHandler(apiInstance);

        new Core.Main(apiInstance, eventHandler, registeredCommands.ToArray());

        NAPI.Server.SetGlobalServerChat(false);

        NAPI.Server.SetCommandErrorMessage("");
        NAPI.Server.SetDefaultSpawnLocation(DEFAULT_SPAWN_POSITION, DEFAULT_SPAWN_HEADING);
        NAPI.Server.SetAutoSpawnOnConnect(false);
        NAPI.Server.SetAutoRespawnAfterDeath(false);
        NAPI.Data.SetWorldData("playerCreatorDimension", 0);

        JsonConvert.DefaultSettings = () =>
        {
          return new JsonSerializerSettings()
          {
            NullValueHandling = NullValueHandling.Ignore
          };
        };

        InventoryManager.LoadItems();
        ShopManager.LoadClotheShops();
        ShopManager.LoadItemShops();
        ShopManager.LoadFriseur();
        TuningManager.LoadTuningGarages();

        TimeManager.StartTimeManager();
        VehicleManager.CheckEnabledMods();

        DatabaseHelper.InitDatabaseFirstTime();

        FactionHelper.CheckFactionBankAccounts();
        BusinessManager.LoadBusinesses();
        //InteriorManager.LoadInteriors();
        DoorManager.LoadDoors();
        ATMManager.InitATMs();
        CityHallManager.LoadCityHall();
        JobManager.LoadJobs();
        //TaxiDriverJob.StartTaxiTimer(); Obselete
        //HouseManager.LoadHouses();
        DrivingSchool.DrivingSchool.Setup();
        PlaneSchool.Setup();
        Gangwar.Gangwar.loadTurfs();
        Bank.bank.Setup();
        Introduction.Setup();
        PositionManager.LoadPositionManager();
        LoadManager.LoadLoadManager();

        TempBlip tempBlip = new TempBlip()
        {
          Color = 1,
          Name = "",
          Transparency = 0,
          ShortRange = true,
          Sprite = 1,
          Scale = 1,
        };

        NAPI.Data.SetWorldData("blipTemplate", tempBlip);

        //WantedEscapeTimer.WantedTimer(); Obselete
        //Jail.JailTimer(); Obselete
        //Economy.PaydayTimer(); Obselete
        // WeaponDealManager.WeaponDealTimer(); Obselete

        ThreadTimers.StartAllTimers();

        Managers.SVarManager.LoadSVars();

        UserBankAccount.BalanceChanged += (account) =>
        {
          using (var dbContext = new DatabaseContext())
          {
            var user = dbContext.Users.Where(u => u.BankAccountId == account.Id).Select(u => u.Name).FirstOrDefault();
            if (user == null) return;
            PlayerService.GetPlayerByNameOrId(user).TriggerEvent("updateMoney", account.Balance);
          }
        };

        User.HandMoneyChanged += (user) =>
        {
          user.Player.TriggerEvent("SERVER:SET_HANDMONEY", user.Handmoney);
        };

        //IPLS
        NAPI.World.RequestIpl("vw_casino_garage");

      }
      catch (Exception e)
      {
        Log(e.ToString());
      }
    }

    private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
      Log(e.ExceptionObject.ToString());
    }

    private void Log(string message)
    {
      string basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
      string log = Path.Combine(basePath, "log.txt");

      logMutex.WaitOne();

      if (!File.Exists(log))
      {
        File.Create(log);
      }

      string msg = $"[{DateTime.Now:s}] - Error: {message}{Environment.NewLine}";

      File.AppendAllText(log, msg);

      logMutex.ReleaseMutex();
    }

    [RemoteEvent("CLIENT:Event")]
    public void OnClientEvent(Player player, string dataStr)
    {
      var data = dataStr.DeserializeJson<List<object>>();
      eventHandler.HandleEvent(new RagePlayer(player), data);
    }
  }
}
