@page "/queue/job/{id::guid:required}"
@using System.Text.Json
@using System.Text.Json.Nodes
@using System.Text.Json.Serialization
@using Iceshrimp.Backend.Core.Database.Tables
@using Iceshrimp.Backend.Core.Extensions
@model QueueJobModel

@{
    ViewData["title"] = $"Job details - {Model.InstanceName}";
}

@section head {
    <link rel="stylesheet" href="~/css/queue.css"/>
}

@section scripts {
    <script src="~/js/queue.js"></script>
}

<h1>Queue Dashboard</h1>

<button role="link" data-target="/queue/@Model.Job.Queue" onclick="navigate(event)">Return to job list</button>

<h2>Job details</h2>

<table>
    <tbody>
    <tr>
        <td class="width20">ID</td>
        <td>@Model.Job.Id.ToStringLower()</td>
    </tr>
    <tr>
        <td>Queue</td>
        <td>@Model.Job.Queue</td>
    </tr>
    <tr>
        @{
            var status = Model.Job is { Status: Job.JobStatus.Delayed, RetryCount: 0 } ? "Scheduled" : Model.Job.Status.ToString();
        }
        <td>Status</td>
        <td class="status-@status.ToLowerInvariant()">@status</td>
    </tr>
    @if (Model.Job.Status.Equals(Job.JobStatus.Failed))
    {
        <tr>
            <td>Actions</td>
            <td>
                <a class="fake-link" onclick="retry('@Model.Job.Id.ToStringLower()')">Retry</a>
            </td>
        </tr>
    }
    else if (Model.Job.Status.Equals(Job.JobStatus.Delayed))
    {
        var abandonName = Model.Job.RetryCount == 0 ? "Deschedule" : "Abandon";
        <tr>
            <td>Actions</td>
            <td>
                <a class="fake-link" onclick="abandon('@Model.Job.Id.ToStringLower()', event.target)">@abandonName</a>
            </td>
        </tr>
    }
    <tr>
        <td>Queued at</td>
        <td>@Model.Job.QueuedAt.ToLocalTime().ToDisplayStringTz()</td>
    </tr>
    @if (Model.Job.Status is not Job.JobStatus.Queued and not Job.JobStatus.Delayed)
    {
        <tr>
            <td>Started at</td>
            <td>@(Model.Job.StartedAt?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
        </tr>
    }
    @if (Model.Job.Status is Job.JobStatus.Completed or Job.JobStatus.Failed)
    {
        <tr>
            <td>Finished at</td>
            <td>@(Model.Job.FinishedAt?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
        </tr>
    }
    @if (Model.Job.Status == Job.JobStatus.Delayed)
    {
        <tr>
            <td>Delayed until</td>
            <td>@(Model.Job.DelayedUntil?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
        </tr>
    }
    @if (TimeSpan.FromMilliseconds(Model.Job.Duration).TotalHours <= 72)
    {
        <tr>
            <td>Duration</td>
            <td>@Model.Job.Duration.ToDurationDisplayString()</td>
        </tr>
    }
    @if (TimeSpan.FromMilliseconds(Model.Job.QueueDuration).TotalHours <= 72)
    {
        <tr>
            <td>Queue duration</td>
            <td>@Model.Job.QueueDuration.ToDurationDisplayString()</td>
        </tr>
    }
    @if (Model.Job.RetryCount > 0)
    {
        <tr>
            <td>Retry count</td>
            <td>@Model.Job.RetryCount</td>
        </tr>
    }
    @if (Model.Job is { ExceptionMessage: not null, Exception: null })
    {
        <tr>
            <td>Exception message</td>
            <td>@Model.Job.ExceptionMessage</td>
        </tr>
    }
    @if (Model.Job is { ExceptionSource: not null, Exception: null })
    {
        <tr>
            <td>Exception source</td>
            <td>@Model.Job.ExceptionSource</td>
        </tr>
    }
    </tbody>
</table>

@if (Model.Job is { StackTrace: not null, Exception: null })
{
    <h3>Exception stack trace</h3>
    <pre><code id="exceptionStackTrace">@Model.Job.StackTrace</code></pre>
    <button onclick="copyElementToClipboard('exceptionStackTrace')">Copy to clipboard</button>
}
@if (Model.Job.Exception != null)
{
    <h3>Exception details</h3>
    <pre><code id="exceptionDetails">@Model.Job.Exception</code></pre>
    <button onclick="copyElementToClipboard('exceptionDetails')">Copy to clipboard</button>
}

<h3>Job data</h3>

@{
    var dataOpts    = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
    var payloadOpts = new JsonSerializerOptions { WriteIndented = true };

    if (Model.Lookup.TryGetValue(Model.Job.Queue, out var payloadKey))
    {
        var data = JsonNode.Parse(Model.Job.Data)?.AsObject() ??
                   throw new Exception($"Failed to deserialize {Model.Job.Queue} job data");
        var payloadElem = data[payloadKey];
        var payload = payloadElem?.GetValue<string>() ??
                      throw new Exception($"Failed to deserialize {Model.Job.Queue} job data");
        var payloadJson = JsonNode.Parse(payload)?.ToJsonString(payloadOpts) ??
                          throw new Exception($"Failed to serialize {Model.Job.Queue} job data");

        data.Remove(payloadKey);
        foreach (var item in data.Where(p => p.Value?.GetValueKind() is null or JsonValueKind.Null).ToList())
            data.Remove(item.Key);

        var dataJson = data.ToJsonString(dataOpts);
        <pre><code>@dataJson</code></pre>
        <h3>Job payload</h3>
        <pre><code id="payload">@payloadJson</code></pre>
    }
    else
    {
        var json = JsonNode.Parse(Model.Job.Data)?.ToJsonString(payloadOpts) ??
                   throw new Exception($"Failed to serialize {Model.Job.Queue} job data");
        <pre><code id="payload">@json</code></pre>
    }
}

<button onclick="copyElementToClipboard('payload');">Copy to clipboard</button>