@namespace ZB.MOM.WW.Secrets.Ui.Components
@implements IDisposable
@inject ISecretStore Store
@inject IAuditWriter Audit
@inject IServiceProvider Services
@* Components/ConfirmDeleteModal.razor — an in-page Blazor overlay (NOT a JS confirm/alert dialog).
   Shown while Visible is true; Confirm tombstones + audits the delete, Cancel dismisses. *@

@if (Visible)
{
    @* The modal ships its own styles and uses zb-secrets-* class names (scadaproj#2): no host
       links a Secrets.Ui stylesheet, three of the four family hosts never link a scoped-CSS
       bundle at all, and host frameworks must not be able to match these elements — Bootstrap's
       bare `.modal { display: none }` rendered the previous markup permanently invisible on
       every Bootstrap host. Theme tokens with fallbacks keep it legible even without the kit. *@
    <style>
        .zb-secrets-modal-backdrop {
            position: fixed; inset: 0; z-index: 1080;
            background: rgba(15, 18, 20, 0.45);
        }
        .zb-secrets-modal {
            position: fixed; inset: 0; z-index: 1081;
            display: flex; align-items: center; justify-content: center;
            font-family: var(--sans, system-ui, sans-serif);
        }
        .zb-secrets-modal-card {
            background: var(--card, #ffffff); color: var(--ink, #1a1d1f);
            border: 1px solid var(--rule-strong, #c9ced2);
            box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
            padding: 1.25rem 1.5rem; max-width: 30rem; width: calc(100% - 2rem);
        }
        .zb-secrets-modal-title { margin: 0 0 0.5rem; font-size: 1.05rem; }
        .zb-secrets-modal-card .btn-row { display: flex; gap: 0.5rem; margin-top: 1rem; }
    </style>
    <div class="zb-secrets-modal-backdrop" data-testid="delete-modal-backdrop"></div>
    <div class="zb-secrets-modal" role="dialog" aria-modal="true" data-testid="delete-modal">
        <div class="zb-secrets-modal-card">
            <h3 class="zb-secrets-modal-title">Delete secret '@Name.Value'?</h3>
            <p>This tombstones the secret so the removal can propagate. The action is audited.</p>

            @if (_error is not null)
            {
                <p class="field-error s-bad" data-testid="delete-error">@_error</p>
            }

            <div class="btn-row">
                <TechButton Variant="ButtonVariant.Danger" Busy="_busy"
                            data-testid="confirm-delete" @onclick="ConfirmAsync">
                    Delete
                </TechButton>
                <TechButton Variant="ButtonVariant.Secondary"
                            data-testid="cancel-delete" @onclick="CancelAsync">
                    Cancel
                </TechButton>
            </div>
        </div>
    </div>
}

@code {
    private readonly CancellationTokenSource _cts = new();
    private string? _error;
    private bool _busy;

    /// <summary>Whether the modal overlay is rendered.</summary>
    [Parameter] public bool Visible { get; set; }

    /// <summary>The secret the modal offers to delete.</summary>
    [Parameter] public SecretName Name { get; set; }

    /// <summary>Raised after the secret has been tombstoned and audited.</summary>
    [Parameter] public EventCallback OnDeleted { get; set; }

    /// <summary>Raised when the user cancels the deletion.</summary>
    [Parameter] public EventCallback OnCancelled { get; set; }

    /// <summary>The cascading authentication state, used as the fallback source of the audit actor.</summary>
    [CascadingParameter] private Task<AuthenticationState>? AuthState { get; set; }

    private async Task ConfirmAsync()
    {
        _error = null;
        _busy = true;
        bool deleted = false;
        try
        {
            string actor = await SecretActorResolver.ResolveAsync(Services, AuthState);

            try
            {
                await Store.DeleteAsync(Name, actor, _cts.Token);

                await Audit.WriteAsync(
                    new AuditEvent
                    {
                        EventId = Guid.NewGuid(),
                        OccurredAtUtc = DateTimeOffset.UtcNow,
                        Actor = actor,
                        Action = "secret.delete",
                        Outcome = AuditOutcome.Success,
                        Category = "Secrets",
                        Target = Name.Value,
                    },
                    _cts.Token);

                deleted = true;
            }
            catch (Exception)
            {
                // An unexpected store fault must still be audited as a Failure and surfaced to the
                // operator instead of tearing down the circuit.
                _error = "Unable to delete the secret.";
                await WriteFailureAuditAsync(actor);
            }
        }
        finally
        {
            _busy = false;
        }

        if (deleted)
        {
            // Evict any cached plaintext so the ${secret:} / ISecretResolver path does not serve a
            // stale value after this delete for the remainder of its TTL. Optional seam — a no-op
            // when the host has not registered an invalidator.
            Services.GetService<ISecretCacheInvalidator>()?.Invalidate(Name);

            await OnDeleted.InvokeAsync();
        }
    }

    private async Task WriteFailureAuditAsync(string actor)
    {
        try
        {
            await Audit.WriteAsync(
                new AuditEvent
                {
                    EventId = Guid.NewGuid(),
                    OccurredAtUtc = DateTimeOffset.UtcNow,
                    Actor = actor,
                    Action = "secret.delete",
                    Outcome = AuditOutcome.Failure,
                    Category = "Secrets",
                    Target = Name.Value,
                    DetailsJson = "{\"reason\":\"error\"}",
                },
                _cts.Token);
        }
        catch
        {
            // Best-effort audit of a failure; never let audit itself surface into the event pipeline.
        }
    }

    private Task CancelAsync() => OnCancelled.InvokeAsync();

    /// <inheritdoc />
    public void Dispose()
    {
        _cts.Cancel();
        _cts.Dispose();
    }
}
