@page "/admin/security/malware"

@using Moonlight.Shared.Components.Navigations
@using Moonlight.App.Services.Background
@using Moonlight.App.Services
@using BlazorTable
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.ApiClients.Wings
@using Moonlight.App.Database.Entities
@using Moonlight.App.Events
@using Moonlight.App.Helpers
@using Moonlight.App.Models.Misc
@using Moonlight.App.Repositories
@using Moonlight.App.Services.Interop
@using Moonlight.App.Services.Sessions

@inject MalwareBackgroundScanService MalwareBackgroundScanService
@inject SmartTranslateService SmartTranslateService
@inject ServerService ServerService
@inject ToastService ToastService
@inject SessionServerService SessionServerService
@inject Repository<Server> ServerRepository
@inject Repository<User> UserRepository
@inject EventSystem Event

@implements IDisposable

@attribute [PermissionRequired(nameof(Permissions.AdminSecurityMalware))]

<AdminSecurityNavigation Index="1"/>

<div class="row">
    <div class="col-12 col-lg-6">
        <div class="card">
            <div class="card-body">
                @if (MalwareBackgroundScanService.IsRunning)
                {
                    <span class="fs-3 spinner-border align-middle me-3"></span>
                }

                <span class="fs-3">@(MalwareBackgroundScanService.Status)</span>
            </div>
            <div class="card-footer">
                @if (MalwareBackgroundScanService.IsRunning)
                {
                    <button class="btn btn-success disabled">
                        <TL>Scan in progress</TL>
                    </button>
                }
                else
                {
                    <div class="mb-5">
                        <div class="form-check">
                            <input class="form-check-input" type="checkbox" id="scanAllServers" @bind="MalwareBackgroundScanService.ScanAllServers">
                            <label class="form-check-label" for="scanAllServers">
                                <TL>Scan all servers</TL>
                            </label>
                        </div>
                    </div>

                    <WButton Text="@(SmartTranslateService.Translate("Start scan"))"
                             CssClasses="btn-success me-3"
                             OnClick="Scan">
                    </WButton>
                    
                    <WButton Text="@(SmartTranslateService.Translate("Purge page"))"
                             CssClasses="btn-danger"
                             OnClick="PurgeSelected">
                    </WButton>
                }
            </div>
        </div>
    </div>
    <div class="col-12 col-lg-6">
        <div class="card">
            <div class="card-header">
                <span class="card-title">
                    <TL>Results</TL>
                </span>
            </div>
            <div class="card-body">
                <LazyLoader @ref="LazyLoaderResults" Load="LoadResults">
                    <div class="table-responsive">
                        <Table @ref="Table" TableItem="Server" Items="ScanResults.Keys" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
                            <Column TableItem="Server" Title="@(SmartTranslateService.Translate("Server"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
                                <Template>
                                    <a href="/server/@(context.Uuid)">@(context.Name)</a>
                                </Template>
                            </Column>
                            <Column TableItem="Server" Title="@(SmartTranslateService.Translate("Title"))" Field="@(x => ScanResults[x].Title)" Sortable="false" Filterable="true" />
                            <Column TableItem="Server" Title="@(SmartTranslateService.Translate("Description"))" Field="@(x => ScanResults[x].Description)" Sortable="false" Filterable="true" />
                            <Pager ShowPageNumber="true" ShowTotalCount="true"/>
                        </Table>
                    </div>
                </LazyLoader>
            </div>
        </div>
    </div>
</div>

@code
{
    private readonly Dictionary<Server, MalwareScanResult> ScanResults = new();

    private Table<Server> Table;

    private LazyLoader LazyLoaderResults;

    protected override async Task OnInitializedAsync()
    {
        await Event.On<Object>("malwareScan.status", this, async o => { await InvokeAsync(StateHasChanged); });

        await Event.On<Server?>("malwareScan.result", this, async server =>
        {
            lock (MalwareBackgroundScanService.ScanResults)
            {
                if (server == null)
                    return;
                
                ScanResults.Add(server, MalwareBackgroundScanService.ScanResults[server]);
            }
            
            await InvokeAsync(StateHasChanged);
        });
    }

    private Task LoadResults(LazyLoader arg)
    {
        ScanResults.Clear();

        lock (MalwareBackgroundScanService.ScanResults)
        {
            foreach (var result in MalwareBackgroundScanService.ScanResults)
            {
                ScanResults.Add(result.Key, result.Value);
            }
        }

        return Task.CompletedTask;
    }

    public async void Dispose()
    {
        await Event.Off("malwareScan.status", this);
        await Event.Off("malwareScan.result", this);
    }

    private async Task PurgeSelected()
    {
        int users = 0;
        int servers = 0;
        
        int allServersCount = Table.FilteredItems.Count();
        int position = 0;

        await ToastService.CreateProcessToast("purgeProcess", "Purging");
        
        foreach (var item in Table.FilteredItems)
        {
            position++;
            
            if (item == null)
                continue;

            try
            {
                var server = ServerRepository.Get()
                    .Include(x => x.Owner)
                    .FirstOrDefault(x => x.Id == item.Id);
                
                if(server == null)
                    continue;

                await ToastService.UpdateProcessToast("purgeProcess", $"[{position}/{allServersCount}] {server.Name}");

                ScanResults.Remove(item);

                await InvokeAsync(StateHasChanged);

                // Owner
                
                server.Owner.Status = UserStatus.Banned;
                UserRepository.Update(server.Owner);
                users++;

                try
                {
                    await SessionServerService.ReloadUserSessions(server.Owner);
                }
                catch (Exception) {/* Ignored */}

    // Server itself

                await ServerService.SetPowerState(server, PowerSignal.Kill);
                await ServerService.Delete(server);
                servers++;
            }
            catch (Exception e)
            {
                Logger.Warn($"Error purging server: {item.Uuid}");
                Logger.Warn(e);

                await ToastService.Error(
                    $"Failed to purge server '{item.Name}': {e.Message}"
                );
            }
        }
        
        await ToastService.RemoveProcessToast("purgeProcess");
        await ToastService.Success($"Successfully purged {servers} servers by {users} users");
    }

    private async Task Scan()
    {
        ScanResults.Clear();
        await InvokeAsync(StateHasChanged);
        await MalwareBackgroundScanService.Start();
    }
}