using IGP.Calculator.Cli.Services;
using Model.Entity;
using Model.Entity.Data;
using Services.Immortal;
using Services.Website;

var faction = DataType.FACTION_QRath;
var immortal = DataType.IMMORTAL_Orzum;
var attackTime = 1500;
var entityNames = new List<string>();

for (var i = 0; i < args.Length; i++)
    switch (args[i].ToLower())
    {
        case "--faction" when i + 1 < args.Length:
            var f = args[++i].ToLower();
            faction = f switch
            {
                "qrath" => DataType.FACTION_QRath,
                "aru" => DataType.FACTION_Aru,
                _ => throw new Exception($"Unknown faction '{args[i]}'. Use QRath or Aru.")
            };
            immortal = f switch
            {
                "qrath" => DataType.IMMORTAL_Orzum,
                "aru" => DataType.IMMORTAL_Mala,
                _ => immortal
            };
            break;
        case "--immortal" when i + 1 < args.Length:
            var im = args[++i];
            immortal = im switch
            {
                nameof(DataType.IMMORTAL_Orzum) => DataType.IMMORTAL_Orzum,
                nameof(DataType.IMMORTAL_Ajari) => DataType.IMMORTAL_Ajari,
                nameof(DataType.IMMORTAL_Atzlan) => DataType.IMMORTAL_Atzlan,
                nameof(DataType.IMMORTAL_Mala) => DataType.IMMORTAL_Mala,
                nameof(DataType.IMMORTAL_Xol) => DataType.IMMORTAL_Xol,
                _ => throw new Exception($"Unknown immortal '{im}'.")
            };
            break;
        case "--attack-time" or "-a" when i + 1 < args.Length:
            attackTime = int.Parse(args[++i]);
            break;
        default:
            entityNames.Add(args[i]);
            break;
    }

var toastService = new ToastService();
var storageService = new NullStorageService();
var timingService = new TimingService(storageService);
timingService.SetAttackTime(attackTime);
var buildOrderService = new BuildOrderService(toastService, timingService);
var economyService = new EconomyService();

buildOrderService.Reset(faction);
economyService.Calculate(buildOrderService, timingService, 0);

Console.WriteLine($"Faction: {(faction == DataType.FACTION_QRath ? "Q'Rath" : "Aru")}");
Console.WriteLine($"Immortal: {immortal.Replace("IMMORTAL_", "")}");
Console.WriteLine($"Attack Time: {attackTime}s");
Console.WriteLine(new string('-', 50));

foreach (var name in entityNames)
{
    if (name.StartsWith("wait ", StringComparison.OrdinalIgnoreCase))
    {
        var seconds = int.Parse(name[5..]);
        buildOrderService.AddWait(seconds);
        economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
        Console.WriteLine($"  Wait {seconds}s -> now at interval {buildOrderService.GetLastRequestInterval()}");
        continue;
    }

    if (name.StartsWith("waitto ", StringComparison.OrdinalIgnoreCase))
    {
        var interval = int.Parse(name[7..]);
        buildOrderService.AddWaitTo(interval);
        economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
        Console.WriteLine($"  Wait to {interval}s -> now at interval {buildOrderService.GetLastRequestInterval()}");
        continue;
    }

    var entity = FindEntity(name, faction, immortal);
    if (entity == null)
    {
        Console.WriteLine($"  ERROR: '{name}' not found for this faction/immortal.");
        continue;
    }

    var beforeInterval = buildOrderService.GetLastRequestInterval();
    var added = buildOrderService.Add(entity, economyService);
    if (added)
    {
        economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
        var startedAt = buildOrderService.GetLastRequestInterval();
        var production = entity.Production();
        var completedAt = production != null ? startedAt + production.BuildTime : startedAt;
        var cost = production != null
            ? $" [{production.Alloy}a/{production.Ether}e/{production.Pyre}p, {production.BuildTime}s]"
            : "";
        Console.WriteLine($"  {entity.GetName(),-25} start={startedAt,4}s  done={completedAt,4}s{cost}");
    }
    else
    {
        Console.WriteLine($"  ERROR: Could not add '{name}'.");
        var toasts = toastService.GetToasts();
        if (toasts.Count > 0)
        {
            var lastToast = toasts[0];
            Console.WriteLine($"    Reason: {lastToast.Title} - {lastToast.Message}");
        }
    }
}

Console.WriteLine(new string('-', 50));

var lastInterval = buildOrderService.GetLastRequestInterval();
var finalEconomy = economyService.GetEconomy(timingService.GetAttackTime());
var lastEconomy = economyService.GetEconomy(lastInterval);

Console.WriteLine($"Army Attacking At:    {timingService.GetAttackTime()}s");
Console.WriteLine("");
Console.WriteLine($"At attack time ({timingService.GetAttackTime()}s):");
Console.WriteLine($"  Alloy: {finalEconomy.Alloy,10:F1}");
Console.WriteLine($"  Ether: {finalEconomy.Ether,10:F1}");
Console.WriteLine($"  Pyre:  {finalEconomy.Pyre,10:F1}");
Console.WriteLine("");
Console.WriteLine($"At last build action ({lastInterval}s):");
Console.WriteLine($"  Alloy: {lastEconomy.Alloy,10:F1}");
Console.WriteLine($"  Ether: {lastEconomy.Ether,10:F1}");
Console.WriteLine($"  Pyre:  {lastEconomy.Pyre,10:F1}");

static EntityModel? FindEntity(string name, string faction, string immortal)
{
    var candidates = EntityModel.GetList()
        .Where(e => e.Info()?.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true)
        .Where(e => e.Faction()?.Faction == faction)
        .ToList();

    if (candidates.Count == 0)
        candidates = EntityModel.GetList()
            .Where(e => e.Info()?.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true)
            .Where(e => e.Faction() == null || e.Faction()!.Faction == DataType.FACTION_Neutral)
            .ToList();

    if (candidates.Count == 0) return null;
    if (candidates.Count == 1) return candidates[0];

    var vanguardMatch = candidates.FirstOrDefault(e => e.VanguardAdded()?.ImmortalId == immortal);
    if (vanguardMatch != null) return vanguardMatch;

    return candidates.FirstOrDefault(e => e.VanguardAdded() == null);
}