using Microsoft.Extensions.DependencyInjection;
using RackPeek.Domain.Resources.Services.UseCases;
using Spectre.Console;
using Spectre.Console.Cli;

namespace Shared.Rcl.Commands.Services;

public class ServiceSubnetsCommand(
    IServiceProvider serviceProvider
) : AsyncCommand<ServiceSubnetsCommand.Settings> {
    private static string BuildUtilizationBar(double fullness, int width = 30) {
        fullness = Math.Clamp(fullness, 0, 100);
        var filled = (int)(width * (fullness / 100.0));
        var empty = width - filled;

        Color color = fullness switch {
            < 50 => Color.Green,
            < 80 => Color.Yellow,
            _ => Color.Red
        };

        var filledBar = new string('█', filled);
        var emptyBar = new string('░', empty);

        return $"[{color.ToString().ToLower()}]{filledBar}[/]{emptyBar} {fullness:0}%";
    }

    private static uint IpToUInt32(string ip) {
        var parts = ip.Split('.');
        return (uint)(
            (int.Parse(parts[0]) << 24) |
            (int.Parse(parts[1]) << 16) |
            (int.Parse(parts[2]) << 8) |
            int.Parse(parts[3]));
    }


    protected override async Task<int> ExecuteAsync(
        CommandContext context,
        Settings settings,
        CancellationToken cancellationToken) {
        using IServiceScope scope = serviceProvider.CreateScope();
        ServiceSubnetsUseCase useCase = scope.ServiceProvider.GetRequiredService<ServiceSubnetsUseCase>();

        ServiceSubnetsResult result = await useCase.ExecuteAsync(settings.Cidr, settings.Prefix, cancellationToken);

        // Handle invalid CIDR
        if (result.IsInvalidCidr) {
            AnsiConsole.MarkupLine($"[red]Invalid CIDR:[/] {result.InvalidCidrValue}");
            return 1;
        }


        if (settings.Cidr is not null) {
            var services = result.Services
                .OrderBy(s => IpToUInt32(s.Ip))
                .ToList();


            if (services.Count == 0) {
                AnsiConsole.MarkupLine($"[yellow]No services found in {settings.Cidr}[/]");
                return 0;
            }

            Table table = new Table()
                .Border(TableBorder.Rounded)
                .AddColumn("Name")
                .AddColumn("IP")
                .AddColumn("Runs On");

            foreach (ServiceSummary s in services) {
                var runsOn = "";
                if (s.RunsOn?.Count > 0) runsOn = string.Join(", ", s.RunsOn);

                table.AddRow(s.Name, s.Ip, runsOn);
            }

            AnsiConsole.MarkupLine($"[green]Services in {result.FilteredCidr}[/]");
            AnsiConsole.Write(table);
            return 0;
        }


        List<SubnetSummary> subnets = result.Subnets;

        subnets = subnets
            .OrderByDescending(s => {
                var parts = s.Cidr.Split('/');
                var prefix = int.Parse(parts[1]);
                var alloc = Math.Pow(2, 32 - prefix) - 2;
                return alloc <= 0 ? 0 : s.Count / alloc;
            })
            .ToList();

        if (subnets.Count == 0) {
            AnsiConsole.MarkupLine("[yellow]No subnets found.[/]");
            return 0;
        }

        Table subnetTable = new Table()
            .Border(TableBorder.Rounded)
            .AddColumn("Subnet")
            .AddColumn("Services")
            .AddColumn("Utilization");

        foreach (SubnetSummary subnet in subnets) {
            var parts = subnet.Cidr.Split('/');
            var prefix = int.Parse(parts[1]);

            // allocatable addresses
            var alloc = Math.Pow(2, 32 - prefix) - 2;
            double used = subnet.Count;
            var fullness = alloc <= 0 ? 0 : used / alloc * 100;

            var bar = BuildUtilizationBar(fullness);

            subnetTable.AddRow(subnet.Cidr, subnet.Count.ToString(), bar);
        }

        AnsiConsole.Write(subnetTable);

        return 0;
    }

    public class Settings : CommandSettings {
        [CommandOption("--cidr <CIDR>")] public string? Cidr { get; set; }

        [CommandOption("--prefix <PREFIX>")] public int? Prefix { get; set; }
    }
}
