@page "/servers"
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Database.Entities
@using Moonlight.App.Helpers
@using Moonlight.App.Models.Misc
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Sessions
@using Newtonsoft.Json

@inject Repository<Server> ServerRepository
@inject Repository<User> UserRepository
@inject SmartTranslateService SmartTranslateService
@inject IServiceScopeFactory ServiceScopeFactory
@inject IJSRuntime JsRuntime
@inject IdentityService IdentityService

<LazyLoader @ref="LazyLoader" Load="Load">
    <div class="mx-auto">
        <div class="card card-body">
            <div class="d-flex justify-content-between">
                <span class="badge badge-primary badge-lg px-5 me-4">Beta</span>
                @if (EditMode)
                {
                    <div>
                        <WButton Text="@(SmartTranslateService.Translate("New group"))"
                                 WorkingText=""
                                 CssClasses="btn-primary me-3"
                                 OnClick="AddGroup">
                        </WButton>
                        <WButton Text="@(SmartTranslateService.Translate("Finish editing layout"))"
                                 CssClasses="btn-secondary"
                                 OnClick="async () => await SetEditMode(false)">
                        </WButton>
                    </div>
                }
                else
                {
                    <WButton Text="@(SmartTranslateService.Translate("Edit layout"))"
                             CssClasses="btn-secondary"
                             OnClick="async () => await SetEditMode(true)">
                    </WButton>
                }
            </div>
        </div>
        @foreach (var group in ServerGroups)
        {
            <div class="accordion my-3" id="serverListGroup@(group.GetHashCode())">
                <div class="accordion-item">
                    <h2 class="accordion-header" id="serverListGroup-header@(group.GetHashCode())">
                        <button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverListGroup-body@(group.GetHashCode())" aria-expanded="false" aria-controls="serverListGroup-body@(group.GetHashCode())">
                            <div class="d-flex gap-2 justify-content-between">
                                <div>
                                    @if (EditMode)
                                    {
                                        <input @bind="group.Name" class="form-control"/>
                                    }
                                    else
                                    {
                                        if (string.IsNullOrEmpty(group.Name))
                                        {
                                            <TL>Unsorted servers</TL>
                                        }
                                        else
                                        {
                                            <span>@(group.Name)</span>
                                        }
                                    }
                                </div>
                                <div>
                                    @if (EditMode)
                                    {
                                        <WButton Text="@(SmartTranslateService.Translate("Remove group"))"
                                                 WorkingText=""
                                                 CssClasses="btn-danger"
                                                 OnClick="async () => await RemoveGroup(group)">
                                        </WButton>
                                    }
                                </div>
                            </div>
                        </button>
                    </h2>
                    <div id="serverListGroup-body@(group.GetHashCode())" class="accordion-collapse collapse @(string.IsNullOrEmpty(group.Name) ? "show" : "")" aria-labelledby="serverListGroup-header@(group.GetHashCode())" data-bs-parent="#serverListGroup">
                        <div class="accordion-body">
                            <div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
                                @foreach (var id in group.Servers)
                                {
                                    var server = AllServers.FirstOrDefault(x => x.Id.ToString() == id);

                                    if (server != null)
                                    {
                                        <div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
                                            @if (EditMode)
                                            {
                                                <div class="card bg-secondary">
                                                    <div class="card-header">
                                                        <div class="card-title">
                                                            <span class="card-label">@(server.Name)</span>
                                                        </div>
                                                        <div class="card-toolbar">
                                                            <a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle">
                                                                <i class="bx bx-md bx-move"></i>
                                                            </a>
                                                        </div>
                                                    </div>
                                                    <div class="card-body">
                                                        <TL>Hidden in edit mode</TL>
                                                    </div>
                                                </div>
                                            }
                                            else
                                            {
                                                <a class="invisible-a" href="/server/@(server.Uuid)">
                                                    <div class="card bg-secondary">
                                                        <div class="card-header">
                                                            <div class="card-title">
                                                                <span class="card-label">@(server.Name)</span>
                                                            </div>
                                                        </div>
                                                        <div class="card-body">
                                                            <span class="card-text fs-6">
                                                                @(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
                                                            </span>
                                                            <div class="card-text my-1 fs-6 fw-bold @(IdentityService.User.StreamerMode ? "blur" : "")">
                                                                @(server.Node.Fqdn):@(server.MainAllocation.Port)
                                                            </div>
                                                            <div class="card-text fs-6">
                                                                @if (StatusCache.ContainsKey(server))
                                                                {
                                                                    var status = StatusCache[server];

                                                                    switch (status)
                                                                    {
                                                                        case "offline":
                                                                            <span class="text-danger">
                                                                                <TL>Offline</TL>
                                                                            </span>
                                                                            break;
                                                                        case "stopping":
                                                                            <span class="text-warning">
                                                                                <TL>Stopping</TL>
                                                                            </span>
                                                                            break;
                                                                        case "starting":
                                                                            <span class="text-warning">
                                                                                <TL>Starting</TL>
                                                                            </span>
                                                                            break;
                                                                        case "running":
                                                                            <span class="text-success">
                                                                                <TL>Running</TL>
                                                                            </span>
                                                                            break;
                                                                        case "failed":
                                                                            <span class="text-gray-400">
                                                                                <TL>Failed</TL>
                                                                            </span>
                                                                            break;
                                                                        default:
                                                                            <span class="text-danger">
                                                                                <TL>Offline</TL>
                                                                            </span>
                                                                            break;
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    <span class="text-gray-400">
                                                                        <TL>Loading</TL>
                                                                    </span>
                                                                }
                                                            </div>
                                                        </div>
                                                    </div>
                                                </a>
                                            }
                                        </div>
                                    }
                                }
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        }
    </div>
</LazyLoader>

@code
{
    

    private Server[] AllServers;
    private LazyLoader LazyLoader;
    private readonly Dictionary<Server, string> StatusCache = new();

    private List<ServerGroup> ServerGroups = new();
    private bool EditMode = false;

    private async Task Load(LazyLoader arg)
    {
        AllServers = ServerRepository
            .Get()
            .Include(x => x.Owner)
            .Include(x => x.MainAllocation)
            .Include(x => x.Node)
            .Include(x => x.Image)
            .Where(x => x.Owner.Id == IdentityService.User.Id)
            .OrderBy(x => x.Name)
            .ToArray();


        if (string.IsNullOrEmpty(IdentityService.User.ServerListLayoutJson))
        {
            ServerGroups.Add(new()
            {
                Name = "",
                Servers = AllServers.Select(x => x.Id.ToString()).ToList()
            });
        }
        else
        {
            ServerGroups = (JsonConvert.DeserializeObject<ServerGroup[]>(
                IdentityService.User.ServerListLayoutJson) ?? Array.Empty<ServerGroup>()).ToList();

            await CheckServerGroups();
        }

        foreach (var server in AllServers)
        {
            Task.Run(async () =>
            {
                try
                {
                    using var scope = ServiceScopeFactory.CreateScope();
                    var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();

                    AddStatus(server, (await serverService.GetDetails(server)).State);
                }
                catch (Exception e)
                {
                    AddStatus(server, "failed");
                }
            });
        }
    }

    private async Task AddGroup()
    {
        ServerGroups.Insert(0, new()
        {
            Name = "New group"
        });

        await InvokeAsync(StateHasChanged);
        await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
    }

    private async Task RemoveGroup(ServerGroup group)
    {
        ServerGroups.Remove(group);
        
        await CheckServerGroups();
        await InvokeAsync(StateHasChanged);

        await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
    }

    private async Task SetEditMode(bool toggle)
    {
        EditMode = toggle;

        if (EditMode)
        {
            await CheckServerGroups();
            await InvokeAsync(StateHasChanged);

            await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
        }
        else
        {
            var json = JsonConvert.SerializeObject(await GetGroupsFromClient());

            await CheckServerGroups();
            
            IdentityService.User.ServerListLayoutJson = json;
            UserRepository.Update(IdentityService.User);

            await LazyLoader.Reload();
        }
    }

    private async Task<ServerGroup[]> GetGroupsFromClient()
    {
        var serverGroups = await JsRuntime.InvokeAsync<ServerGroup[]>("moonlight.serverList.getData");

        // Check user data to prevent users from doing stupid stuff
        foreach (var serverGroup in serverGroups)
        {
            if (serverGroup.Name.Length > 30)
            {
                Logger.Verbose("Server list group lenght too long");
                return Array.Empty<ServerGroup>();
            }
        }

        return serverGroups;
    }

    private Task CheckServerGroups()
    {
        var result = new List<ServerGroup>();

        // Reconstruct the data with checking for invalid server ids
        foreach (var group in ServerGroups)
        {
            var checkedGroup = new ServerGroup()
            {
                Name = group.Name
            };

            foreach (var server in group.Servers)
            {
                var s = AllServers.FirstOrDefault(x => x.Id.ToString() == server);

                if (s != null) // This is a check for invalid server ids
                {
                    checkedGroup.Servers.Add(s.Id.ToString());
                }
            }
            
            result.Add(checkedGroup);
        }
        
        var presentInGroup = new List<Server>();
        
        // Copy all servers to preset in group if they are in the users servers
        foreach (var group in result) 
        {
            foreach (var id in group.Servers)
            {
                var s = AllServers.First(x => x.Id.ToString() == id);
                presentInGroup.Add(s);
            }
        }

        var serversMissing = new List<Server>();

        // Make a list of missing servers
        foreach (var server in AllServers)
        {
            if (presentInGroup.All(x => x.Id != server.Id))
                serversMissing.Add(server);
        }

        // Add all missing servers into the default group
        if (serversMissing.Any())
        {
            var defaultGroup = result.FirstOrDefault(x => x.Name == "");

            if (defaultGroup == null) // If group does not exist, create it
            {
                defaultGroup = new ServerGroup()
                {
                    Name = ""
                };

                result.Add(defaultGroup);
            }

            foreach (var server in serversMissing)
                defaultGroup.Servers.Add(server.Id.ToString());
        }

        ServerGroups = result;
        
        return Task.CompletedTask;
    }

    private void AddStatus(Server server, string status)
    {
        lock (StatusCache)
        {
            if (!StatusCache.ContainsKey(server))
            {
                StatusCache.Add(server, status);
                InvokeAsync(StateHasChanged);
            }
        }
    }
}