@page "/missions"
@attribute [Authorize]

@using RobotNet.Script.Shares
@using RobotNet.Shares
@using RobotNet.Clients
@using RobotNet.WebApp.Scripts.Models

@inject IHttpClientFactory httpFactory
@inject IDialogService DialogService
@inject ISnackbar Snackbar

<PageTitle>Runner Missions</PageTitle>

<div @ref="divRef" class="w-100 h-100">
    <MudTable @ref="table" ServerData="ServerReload" Style="margin: 10px;" Items="runnerMissions" Height="@TableHeight" Dense FixedHeader Virtualize Hover Elevation="6">
        <ToolBarContent>
            <div @ref="toolbarRef" class="w-100 d-flex flex-row">
                <h3>Danh sách Missions</h3>
                <MudSpacer />
                <MudIconButton Class="me-2" Icon="@Icons.Material.Filled.Refresh" Color="Color.Primary" Size="Size.Small" OnClick="ReloadTable" />
                <MudTextField T="string" ValueChanged="@(s => OnSearch(s))" DebounceInterval="1000" Value="@searchString" Margin="Margin.Dense"
                              Placeholder="Search" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search"
                              IconSize="Size.Medium" Class="mt-0" Variant="Variant.Outlined" />
            </div>
        </ToolBarContent>
        <ColGroup>
            <col style="width:60px;" />
            <col style="width:100px;" />
            <col style="width:150px;" />
            <col style="width:120px;" />
            <col />
            <col style="width:100px;" />
            <col />
            <col style="width:100px;" />
        </ColGroup>
        <HeaderContent>
            <MudTh>STT</MudTh>
            <MudTh>Tên</MudTh>
            <MudTh>Id</MudTh>
            <MudTh>Create at</MudTh>
            <MudTh>Parameters</MudTh>
            <MudTh>Status</MudTh>
            <MudTh>Log</MudTh>
            <MudTh></MudTh>
        </HeaderContent>
        <RowTemplate>
            <MudTd>@context.Index</MudTd>
            <MudTd DataLabel="Tên">@context.MissionName</MudTd>
            <MudTd DataLabel="Id">@context.Id.ToString()[..13]</MudTd>
            <MudTd DataLabel="Create at">@context.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")</MudTd>
            <MudTd DataLabel="Parameters">
                @if (context.Parameters?.Any() == true)
                {
                    <span title="@context.Parameters">
                        @ShortenLog(context.Parameters, 30)
                    </span>
                    <MudIconButton Icon="@Icons.Material.Filled.Info" Color="Color.Primary" Size="Size.Small"
                                   OnClick="@(() => ShowParametersDialog(context))" />
                }
                else
                {
                    <span>{}</span>
                }
            </MudTd>
            <MudTd DataLabel="Status">
                @if (context.Status == MissionStatus.Running)
                {
                    <MudChip T="string" Color="Color.Success" Variant="Variant.Filled">@context.Status</MudChip>
                }
                else if (context.Status == MissionStatus.Completed)
                {
                    <MudChip T="string" Color="Color.Primary" Variant="Variant.Filled">@context.Status</MudChip>
                }
                else if (context.Status == MissionStatus.Error)
                {
                    <MudChip T="string" Color="Color.Error" Variant="Variant.Filled">@context.Status</MudChip>
                }
                else
                {
                    <MudChip T="string" Color="Color.Warning" Variant="Variant.Filled">@context.Status</MudChip>
                }
            </MudTd>
            <MudTd DataLabel="Log">
                @if (!string.IsNullOrEmpty(context.Log))
                {
                    <span title="@context.Log">
                        @ShortenLog(context.Log, 30)
                    </span>
                    <MudIconButton Icon="@Icons.Material.Filled.Visibility" Color="Color.Primary" Size="Size.Small"
                                   OnClick="@(() => ShowLogDialog(context.Log))" />
                }
            </MudTd>
            <MudTd DataLabel="" Class="text-center">
                @if (context.Status == MissionStatus.Running)
                {
                    <MudIconButton Icon="@Icons.Material.Filled.Stop" Color="Color.Error" Size="Size.Small" OnClick="@(() => PauseMission(context))" />
                }
                else if (context.Status == MissionStatus.Paused)
                {
                    <MudIconButton Icon="@Icons.Material.Filled.PlayArrow" Color="Color.Primary" Size="Size.Small" OnClick="@(() => ResumeMission(context))" />
                }

                @if (context.Status == MissionStatus.Running || context.Status == MissionStatus.Paused)
                {
                    <MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Size="Size.Small" OnClick="@(() => CancelMission(context))" />
                }
            </MudTd>
        </RowTemplate>
        <NoRecordsContent>
            <MudText>No matching records found</MudText>
        </NoRecordsContent>
        <LoadingContent>
            <MudText>Loading...</MudText>
        </LoadingContent>
        <PagerContent>
            <MudTablePager />
        </PagerContent>
    </MudTable>
</div>

<MudDialog @bind-Visible="_logDialogOpen" Style="min-width: 900px;">
    <DialogContent>
        <MudText Typo="Typo.h6">Mission Log</MudText>
        <MudPaper Class="pa-2" Style="overflow-x:auto;">
            <pre style="white-space:pre-wrap; word-break:break-all;">@_selectedLog</pre>
        </MudPaper>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="() => _logDialogOpen = false" Color="Color.Primary">Close</MudButton>
    </DialogActions>
</MudDialog>

<MudDialog @bind-Visible="_parametersDialogOpen" Style="min-width: 600px;">
    <DialogContent>
        <MudText Typo="Typo.h6">Parameters of mission "@_missionName"</MudText>
        <MudPaper Class="pa-2" Style="overflow-x:auto;">
            <table class="mud-table mud-table-dense" style="width:100%;">
                <thead>
                    <tr>
                        <th style="text-align:left;">Key</th>
                        <th style="text-align:left;">Value</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var kv in _selectedParameters)
                    {
                        <tr>
                            <td style="vertical-align:top;">@kv.Key</td>
                            <td style="vertical-align:top;">@kv.Value</td>
                        </tr>
                    }
                </tbody>
            </table>
        </MudPaper>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="() => _parametersDialogOpen = false" Color="Color.Primary">Close</MudButton>
    </DialogActions>
</MudDialog>

@code {
    private string TableHeight = "0px";
    private ElementReference divRef;
    private ElementReference toolbarRef;
    private MudTable<InstanceMissionModel> table = default!;
    private List<InstanceMissionModel> runnerMissions = [];
    private string searchString = "";

    private string _missionName = "";
    private bool _parametersDialogOpen = false;
    private IDictionary<string, string> _selectedParameters = new Dictionary<string, string>();

    private bool _logDialogOpen = false;
    private string _selectedLog = string.Empty;

    private HttpClient http = default!;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);
        if (firstRender)
        {
            var rect = await divRef.MudGetBoundingClientRectAsync();
            var toolbarRect = await toolbarRef.MudGetBoundingClientRectAsync();
            TableHeight = $"{rect.Height - 70 - Math.Max(toolbarRect.Height, 64)}px";
            StateHasChanged();

            await table.ReloadServerData();
        }
    }

    private async Task<TableData<InstanceMissionModel>> ServerReload(TableState state, CancellationToken token)
    {
        if (http == null)
        {
            http = httpFactory.CreateClient("ScriptManagerAPI");
        }

        var url = $"api/ScriptMissions/Runner?txtSearch={Uri.EscapeDataString(searchString)}&page={state.Page + 1}&size={state.PageSize}";
        var response = await http.GetFromJsonAsync<SearchResult<InstanceMissionDto>>(url) ?? new();

        return new TableData<InstanceMissionModel>()
        {
            TotalItems = response.Total,
            Items = [.. response.Items.Select((item, index) => new InstanceMissionModel(index + 1 + state.Page * state.PageSize, item))]
        };
    }

    private void ShowParametersDialog(InstanceMissionModel mission)
    {
        try
        {
            _selectedParameters = System.Text.Json.JsonSerializer.Deserialize<IDictionary<string, string>>(mission.Parameters)
                                    ?? throw new Exception($"Can not convert parameters data from \"{mission.Parameters}\"");
            _missionName = mission.MissionName;
            _parametersDialogOpen = true;
            StateHasChanged();
        }
        catch (Exception ex)
        {
            Snackbar.Add($"Failed to parse parameters: {ex.Message}", Severity.Error);
        }
    }

    private string ShortenLog(string? log, int maxLength)
    {
        if (string.IsNullOrEmpty(log)) return string.Empty;
        return log.Length > maxLength ? log.Substring(0, maxLength) + "..." : log;
    }

    private void ShowLogDialog(string log)
    {
        _selectedLog = log;
        _logDialogOpen = true;
        StateHasChanged();
    }

    private async Task OnSearch(string text)
    {
        searchString = text;
        await table.ReloadServerData();
    }

    private async Task CancelMission(InstanceMissionModel model)
    {
        var response = await http.DeleteFromJsonAsync<MessageResult>($"api/ScriptMissions/Runner/{model.Id}");
        if (response == null)
        {
            Snackbar.Add("Failed to cancel mission: server response error", Severity.Error);
        }
        else if (response.IsSuccess)
        {
            Snackbar.Add("Mission canceled successfully", Severity.Success);
            await table.ReloadServerData();
        }
        else
        {
            Snackbar.Add($"Failed to cancel mission: {response.Message}", Severity.Error);
        }
    }

    private async Task PauseMission(InstanceMissionModel model)
    {
        var response = await http.PutFromJsonAsync<MessageResult>($"api/ScriptMissions/Runner/{model.Id}/pause", new object());
        if (response == null)
        {
            Snackbar.Add("Failed to pause mission: server response error", Severity.Error);
        }
        else if (response.IsSuccess)
        {
            Snackbar.Add("Mission paused successfully", Severity.Success);
            await table.ReloadServerData();
        }
        else
        {
            Snackbar.Add($"Failed to pause mission: {response.Message}", Severity.Error);
        }
    }

    private async Task ResumeMission(InstanceMissionModel model)
    {
        var response = await http.PutFromJsonAsync<MessageResult>($"api/ScriptMissions/Runner/{model.Id}/resume", new object());
        if (response == null)
        {
            Snackbar.Add("Failed to resume mission: server response error", Severity.Error);
        }
        else if (response.IsSuccess)
        {
            Snackbar.Add("Mission resumed successfully", Severity.Success);
            await table.ReloadServerData();
        }
        else
        {
            Snackbar.Add($"Failed to resume mission: {response.Message}", Severity.Error);
        }
    }

    private async Task ReloadTable()
    {
        await table.ReloadServerData();
    }
}
