@page "/deployments"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.EntityFrameworkCore
@using ZB.MOM.WW.OtOpcUa.Commons.Interfaces
@using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin
@using ZB.MOM.WW.OtOpcUa.Configuration
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
@using ZB.MOM.WW.OtOpcUa.Configuration.Enums
@using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations

@attribute [Authorize(Roles = "Administrator,Designer")]

@inject IDbContextFactory<OtOpcUaConfigDbContext> DbFactory
@inject IAdminOperationsClient AdminOps
@inject AuthenticationStateProvider AuthState
@rendermode InteractiveServer

<PageTitle>Deployments</PageTitle>

<h1>Deployments</h1>

<div class="d-flex align-items-center gap-3 mb-3">
    <button class="btn btn-primary" @onclick="StartDeploymentAsync" disabled="@_busy">
        @(_busy ? "Deploying…" : "Deploy current configuration")
    </button>

    @if (_drift is not null)
    {
        <span class="badge @(_drift.Value ? "bg-warning text-dark" : "bg-success")">
            @(_drift.Value ? "Configuration drift" : "In sync")
        </span>
    }
</div>

@if (_lastMessage is not null)
{
    <div class="alert @(_lastSuccess ? "alert-success" : "alert-danger")">
        @_lastMessage
    </div>
}

<table class="table table-striped table-sm">
    <thead>
        <tr>
            <th>Deployment</th>
            <th>Revision</th>
            <th>Status</th>
            <th>Created by</th>
            <th>Created (UTC)</th>
            <th>Sealed (UTC)</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var d in _deployments)
        {
            <tr>
                <td><code>@Short(d.DeploymentId)</code></td>
                <td><code>@d.RevisionHash[..12]…</code></td>
                <td>@d.Status</td>
                <td>@d.CreatedBy</td>
                <td>@d.CreatedAtUtc.ToString("u")</td>
                <td>@(d.SealedAtUtc?.ToString("u") ?? "—")</td>
            </tr>
        }
    </tbody>
</table>

@code {
    private IReadOnlyList<Deployment> _deployments = Array.Empty<Deployment>();
    private bool _busy;
    private bool _lastSuccess;
    private string? _lastMessage;
    private bool? _drift;

    protected override async Task OnInitializedAsync()
    {
        await ReloadAsync();
    }

    private async Task ReloadAsync()
    {
        await using var db = await DbFactory.CreateDbContextAsync();
        _deployments = await db.Deployments
            .AsNoTracking()
            .OrderByDescending(d => d.CreatedAtUtc)
            .Take(50)
            .ToListAsync();

        // Drift: if no sealed deployment yet, no drift to report. Otherwise compare the latest
        // sealed revision hash to a fresh snapshot of the live-edit state.
        var latestSealed = _deployments.FirstOrDefault(d => d.Status == DeploymentStatus.Sealed);
        if (latestSealed is null)
        {
            _drift = null;
            return;
        }
        var current = await ConfigComposer.SnapshotAndFlattenAsync(db);
        _drift = !string.Equals(current.RevisionHash, latestSealed.RevisionHash, StringComparison.Ordinal);
    }

    private async Task StartDeploymentAsync()
    {
        _busy = true;
        _lastMessage = null;
        try
        {
            var auth = await AuthState.GetAuthenticationStateAsync();
            var createdBy = auth.User.Identity?.Name ?? "(anonymous)";
            var result = await AdminOps.StartDeploymentAsync(
                createdBy: createdBy,
                ct: CancellationToken.None);

            _lastSuccess = result.Outcome == StartDeploymentOutcome.Accepted;
            _lastMessage = result.Outcome switch
            {
                StartDeploymentOutcome.Accepted => $"Deployment {Short(result.DeploymentId!.Value.Value)} dispatched (rev {result.RevisionHash!.Value.Value[..12]}…).",
                StartDeploymentOutcome.AnotherDeploymentInFlight => result.Message ?? "Another deployment is already in flight.",
                StartDeploymentOutcome.NoChanges => "No changes detected since the last sealed deployment.",
                _ => result.Message ?? "Deployment rejected.",
            };
            await ReloadAsync();
        }
        catch (Exception ex)
        {
            _lastSuccess = false;
            _lastMessage = $"Deploy failed: {ex.Message}";
        }
        finally
        {
            _busy = false;
        }
    }

    private static string Short(Guid id) => id.ToString("N")[..8];
}
