@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers

<section class="panel rise mt-3" style="animation-delay:.14s">
    <div class="panel-head">Resilience overrides (optional)</div>
    <div style="padding:1rem">
        <p class="form-text mb-3">Blank fields use the driver type's stability-tier defaults
            (see <span class="mono">docs/v2/driver-stability.md</span>). Set only what you need to override.</p>

        @if (_m.ParseFailed)
        {
            <div class="alert alert-danger" role="alert">
                <strong>Stored resilience config could not be parsed.</strong>
                Editing is locked so an accidental keystroke can't overwrite it. Fix the stored JSON directly,
                or discard it and start from tier defaults.
                <div class="mt-2">
                    <button type="button" class="btn btn-sm btn-outline-danger" @onclick="DiscardAsync">
                        Discard stored JSON and start blank
                    </button>
                </div>
            </div>
        }

        <div class="row g-3">
            <div class="col-md-4"><label class="form-label">Recycle interval (s, Tier C only)</label>
                <input type="number" class="form-control form-control-sm" @bind="_m.RecycleIntervalSeconds" @bind:after="EmitAsync" placeholder="none" disabled="@_m.ParseFailed" /></div>
        </div>

        <div class="table-wrap mt-3">
            <table class="data-table">
                <thead><tr><th>Capability</th><th>Timeout (s)</th><th>Retries</th><th>Breaker threshold</th></tr></thead>
                <tbody>
                    @foreach (var cap in ResilienceFormModel.Capabilities)
                    {
                        var row = _m.Policies[cap];
                        var writeShaped = cap is "Write" or "AlarmAcknowledge";
                        <tr>
                            <td class="mono">@cap</td>
                            <td><input type="number" class="form-control form-control-sm" @bind="row.TimeoutSeconds" @bind:after="EmitAsync" placeholder="default" disabled="@_m.ParseFailed" /></td>
                            <td><input type="number" class="form-control form-control-sm" @bind="row.RetryCount" @bind:after="EmitAsync" placeholder="@(writeShaped ? "never" : "default")" disabled="@(writeShaped || _m.ParseFailed)" title="@(writeShaped ? "Writes/acks never retry — per-tag opt-in not yet available" : null)" /></td>
                            <td><input type="number" class="form-control form-control-sm" @bind="row.BreakerFailureThreshold" @bind:after="EmitAsync" placeholder="default" disabled="@_m.ParseFailed" /></td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>

        @{ var _warnings = _m.Validate(); }
        @if (_warnings.Count > 0)
        {
            <div class="alert alert-warning mt-3" role="alert">
                <strong>Out-of-range values will be clamped to a safe value on deploy:</strong>
                <ul class="mb-0">
                    @foreach (var w in _warnings)
                    {
                        <li>@w</li>
                    }
                </ul>
            </div>
        }

        <details class="mt-3">
            <summary class="small text-muted">Raw JSON (advanced)</summary>
            @* Render the bound STORED value, not the re-serialized model — shows the truth in both the
               healthy and the malformed case. *@
            <pre class="form-control form-control-sm mono mt-2" style="white-space:pre-wrap;min-height:3rem;">@(ResilienceConfig ?? "(null — all tier defaults)")</pre>
        </details>
    </div>
</section>

@code {
    [Parameter] public string? ResilienceConfig { get; set; }
    [Parameter] public EventCallback<string?> ResilienceConfigChanged { get; set; }

    private ResilienceFormModel _m = new();
    private string? _lastParsed;

    protected override void OnParametersSet()
    {
        // Re-parse only when the inbound value actually changed (avoid clobbering edits on re-render).
        if (!string.Equals(_lastParsed, ResilienceConfig, StringComparison.Ordinal))
        {
            _m = ResilienceFormModel.FromJson(ResilienceConfig);
            _lastParsed = ResilienceConfig;
        }
    }

    private async Task EmitAsync()
    {
        // Never emit while the stored config is unparseable — the section is locked, and an unrelated
        // keystroke must never silently overwrite what we couldn't read (04/C-7).
        if (_m.ParseFailed) return;

        var json = _m.ToJson();
        _lastParsed = json;
        ResilienceConfig = json;
        await ResilienceConfigChanged.InvokeAsync(json);
    }

    private async Task DiscardAsync()
    {
        // Explicit, visible destruction — replace the unparseable blob with a blank (tier-default) config.
        _m = new ResilienceFormModel();
        _lastParsed = null;
        ResilienceConfig = null;
        await ResilienceConfigChanged.InvokeAsync(null);
    }
}
