@page "/admin/servers/manager"

@using MoonCore.Abstractions
@using MoonCore.Helpers
@using MoonCoreUI.Services
@using Moonlight.Features.Servers.Api.Resources
@using Moonlight.Features.Servers.Entities
@using Moonlight.Features.Servers.Services
@using Moonlight.Features.Servers.UI.Components
@using BlazorTable
@using Microsoft.EntityFrameworkCore
@using Moonlight.Features.Servers.Api.Packets
@using Moonlight.Features.Servers.Helpers
@using Moonlight.Features.Servers.Models.Enums

@inject Repository<ServerNode> NodeRepository
@inject Repository<Server> ServerRepository
@inject ServerService ServerService
@inject ToastService ToastService
@inject AlertService AlertService

@attribute [RequirePermission(5000)]

@implements IDisposable

<AdminServersNavigation Index="3"/>

<LazyLoader Load="Load">
    @if (Nodes.Length == 0)
    {
        <IconAlert Title="No nodes found" Color="primary" Icon="bx-search-alt">
            No nodes found to scan for servers. Please add a node to start using the manager
        </IconAlert>
    }
    else
    {
        if (Servers.Length == 0)
        {
            <IconAlert Title="No servers found" Color="primary" Icon="bx-search-alt">
                No servers found to scan for. Please add a server to start using the manager
            </IconAlert>
        }
        else
        {
            <div class="card card-body px-5 py-3 mb-5">
                <div class="d-flex justify-content-between align-items-center">
                    @if (IsRefreshing)
                    {
                        <div class="btn btn-secondary">
                            <div class="spinner-border spinner-border-sm" role="status">
                                <span class="visually-hidden">Loading...</span>
                            </div>
                            <span class="ms-3">Refreshing</span>
                        </div>
                    }
                    else
                    {
                        <div></div>
                    }
                    <div>
                        <div class="dropdown">
                            <button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown">
                                Perform action
                            </button>
                            <ul class="dropdown-menu">
                                <li>
                                    <WButton OnClick="() => PerformActionOnSelection(PowerAction.Stop)" CssClasses="dropdown-item bg-hover-secondary">
                                        <div class="d-flex align-items-center"><i class="bx bx-sm bx-power-off me-3 text-warning"></i>Stop</div>
                                    </WButton>
                                </li>
                                <li>
                                    <WButton OnClick="() => PerformActionOnSelection(PowerAction.Kill)" CssClasses="dropdown-item bg-hover-secondary">
                                        <div class="d-flex align-items-center"><i class="bx bx-sm bx-bomb me-3 text-danger"></i>Kill</div>
                                    </WButton>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>

            if (Items.Length == 0)
            {
                <IconAlert Title="No servers found on the nodes" Color="primary" Icon="bx-search-alt">
                    No running servers found
                </IconAlert>
            }
            else
            {
                <div class="card card-body p-5">
                    <div class="table-responsive">
                        <Table @ref="Table"
                               TableItem="MetaItem"
                               Items="Items"
                               TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
                               TableHeadClass="fw-bold text-muted"
                               PageSize="PageSize">
                            <Column TableItem="MetaItem" Field="@(x => x.Server.Id)" Filterable="true" Title="Id"/>
                            <Column TableItem="MetaItem" Field="@(x => x.Server.Name)" Filterable="true" Title="Name">
                                <Template>
                                    <a target="_blank" href="/server/@(context.Server.Id)">@context.Server.Name</a>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.State)" Filterable="true" Sortable="true">
                                <Template>
                                    <span class="text-@(ServerUtilsHelper.GetColorFromState(context.State))">
                                        @context.State
                                    </span>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Stats.CpuUsage)" Sortable="true">
                                <Template>
                                    <span>@Math.Round(context.Stats.CpuUsage, 2)%</span>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Stats.MemoryUsage)" Sortable="true">
                                <Template>
                                    <span>@Formatter.FormatSize(context.Stats.MemoryUsage)</span>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Stats.NetRead)" Sortable="true">
                                <Template>
                                    <span>@Formatter.FormatSize(context.Stats.NetRead)</span>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Stats.NetWrite)" Sortable="true">
                                <Template>
                                    <span>@Formatter.FormatSize(context.Stats.NetWrite)</span>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Server.Owner.Username)" Title="Owner" Filterable="true">
                                <Template>
                                    <a target="_blank" href="/admin/users/view/@(context.Server.Owner.Id)">@context.Server.Owner.Username</a>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Server.Node.Name)" Title="Node" Filterable="true">
                                <Template>
                                    <a target="_blank" href="/admin/servers/nodes/view/@(context.Server.Node.Id)">@context.Server.Node.Name</a>
                                </Template>
                            </Column>
                            <Column TableItem="MetaItem" Field="@(x => x.Server.Image.Name)" Title="Image" Filterable="true">
                                <Template>
                                    <a target="_blank" href="/admin/servers/images/view/@(context.Server.Image.Id)">@context.Server.Image.Name</a>
                                </Template>
                            </Column>
                            <Pager AlwaysShow="true" ShowTotalCount="true"></Pager>
                        </Table>
                    </div>
                </div>
            }
        }
    }
</LazyLoader>

@code
{
    private ServerNode[] Nodes;
    private List<ServerNode> OfflineNodes = new();
    private Server[] Servers;

    private Table<MetaItem>? Table;
    private MetaItem[] Items;
    private bool IsRefreshing;
    private int PageSize = 50;

    private Timer? UpdateTimer;

    private async Task Load(LazyLoader lazyLoader)
    {
        await lazyLoader.SetText("Loading nodes");

        Nodes = NodeRepository
            .Get()
            .ToArray();

        if (Nodes.Length == 0)
            return;

        await lazyLoader.SetText("Loading servers");

        Servers = ServerRepository
            .Get()
            .Include(x => x.Owner)
            .Include(x => x.Image)
            .Include(x => x.Node)
            .ToArray();

        if (Servers.Length == 0)
            return;

        await lazyLoader.SetText("Loading stats");
        await Refresh();

        UpdateTimer = new Timer(async _ => await Refresh(), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5));
    }

    private async Task Refresh()
    {
        var nodesToCheck = Nodes
            .Where(x => OfflineNodes.All(y => y.Id != x.Id))
            .ToArray();

        var items = new List<ServerListItem>();

        foreach (var node in nodesToCheck)
        {
            try
            {
                items.AddRange(await ServerService.GetServersList(node));
            }
            catch (Exception e)
            {
                Logger.Warn($"An error occured while fetching server list from node {node.Id}");
                Logger.Warn(e);

                OfflineNodes.Add(node);

                await ToastService.Danger($"Unable to reach node '{node.Name}', It will be ignored now to avoid timeout delays");
            }
        }

        Items = items
            .Where(x => Servers.Any(y => y.Id == x.Id))
            .Select(x =>
            {
                return new MetaItem()
                {
                    Server = Servers.First(y => y.Id == x.Id),
                    State = x.State,
                    Stats = x.Stats
                };
            }).ToArray();

        IsRefreshing = false;
        await InvokeAsync(StateHasChanged);
    }

    private async Task PerformActionOnSelection(PowerAction action)
    {
        if(Table == null)
            return;
        
        // This resize is required in order to get all items with the table filters
        await Table.SetPageSizeAsync(Items.Length);

        var items = Table.FilteredItems.ToArray();

        await Table.SetPageSizeAsync(PageSize);

        // Confirm
        if(!await AlertService.YesNo($"Do you really want to perform the action '{action}' for {items.Length} servers?"))
            return;
        
        await ToastService.CreateProgress("multiPowerAction", "Preparing");

        // Perform
        int i = 0;
        foreach (var item in items)
        {
            try
            {
                await ToastService.ModifyProgress("multiPowerAction", $"Sending power action [{i + 1} / {items.Length}]");
                await ServerService.Console.SendAction(item.Server, action);
                
                i++;
            }
            catch (Exception e)
            {
                Logger.Warn($"An error occured while performing power action on server {item.Server.Id}");
                Logger.Warn(e);

                await ToastService.Danger($"Unable to perform power action for server '{item.Server.Name}'");
            }
        }

        await ToastService.RemoveProgress("multiPowerAction");
        await ToastService.Success($"Successfully performed the action for {i} servers");
    }

    public void Dispose()
    {
        UpdateTimer?.Dispose();
    }

    struct MetaItem
    {
        public Server Server { get; set; }
        public ServerState State { get; set; }
        public ServerStats Stats { get; set; }
    }
}