@page "/design/transport/export"
@using ZB.MOM.WW.ScadaBridge.Security
@using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates
@using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts
@using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems
@using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications
@using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi
@using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)]

@*
   TransportExport wizard (Component #24, Task T21).

   A 4-step linear wizard:
     Step 1 — Select   : templates (tree, checkbox-mode) + flat artifact lists.
     Step 2 — Review   : resolved closure split into seed vs auto-included.
     Step 3 — Encrypt  : passphrase + confirm, or explicit unencrypted opt-out.
     Step 4 — Download : streams the bundle bytes to the browser via JS interop.

   The page is Design-role gated; deeper interactions (audit row, secrets
   warning) come from BundleExporter itself.
*@

<div class="container-fluid mt-3">
    @if (_loading)
    {
        <LoadingSpinner IsLoading="true" />
    }
    else if (_errorMessage != null)
    {
        <div class="alert alert-danger">@_errorMessage</div>
    }
    else
    {
        <h4 class="mb-3">Export Bundle</h4>

        @* Step indicator — Bootstrap progress with discrete numbered pills. *@
        <nav aria-label="Export wizard steps" class="mb-4">
            <ol class="list-unstyled d-flex flex-wrap gap-3 mb-0 small">
                <li class="@StepClass(ExportWizardStep.Select)">
                    <span class="badge rounded-pill me-1">1</span> Select
                </li>
                <li class="@StepClass(ExportWizardStep.Review)">
                    <span class="badge rounded-pill me-1">2</span> Review
                </li>
                <li class="@StepClass(ExportWizardStep.Encrypt)">
                    <span class="badge rounded-pill me-1">3</span> Encrypt
                </li>
                <li class="@StepClass(ExportWizardStep.Download)">
                    <span class="badge rounded-pill me-1">4</span> Download
                </li>
            </ol>
        </nav>

        @switch (_step)
        {
            case ExportWizardStep.Select:
                @RenderStepSelect();
                break;
            case ExportWizardStep.Review:
                @RenderStepReview();
                break;
            case ExportWizardStep.Encrypt:
                @RenderStepEncrypt();
                break;
            case ExportWizardStep.Download:
                @RenderStepDownload();
                break;
        }
    }
</div>

@code {
    private string StepClass(ExportWizardStep s) =>
        s == _step ? "fw-semibold text-primary"
        : (int)s < (int)_step ? "text-success"
        : "text-muted";

    // ============================================================
    // Step 1 — Select
    // ============================================================
    private RenderFragment RenderStepSelect() => __builder =>
    {
        <div>
            <div class="mb-3">
                <label for="export-filter" class="form-label">Search</label>
                <input id="export-filter" type="search" class="form-control"
                       placeholder="Filter all artifacts…"
                       @bind="_filter" @bind:event="oninput" />
            </div>

            <fieldset class="mb-4" data-testid="group-templates">
                <legend class="h6">Templates</legend>
                @if (_templates.Count == 0)
                {
                    <div class="text-muted small fst-italic">No templates.</div>
                }
                else
                {
                    <div style="max-height: 320px; overflow-y: auto; padding: 4px; border: 1px solid var(--bs-border-color); border-radius: 4px;">
                        <TemplateFolderTree Folders="_folders"
                                            Templates="_templates"
                                            SelectionMode="TreeViewSelectionMode.Checkbox"
                                            SelectedKeys="_selectedTemplateKeys"
                                            SelectedKeysChanged="OnTemplateSelectionChanged"
                                            Filter="@_filter" />
                    </div>
                }
            </fieldset>

            <fieldset class="mb-4" data-testid="group-shared-scripts">
                <legend class="h6">Shared Scripts</legend>
                @RenderCheckboxList(_sharedScripts, s => s.Id, s => s.Name, _selectedSharedScripts)
            </fieldset>

            <fieldset class="mb-4" data-testid="group-external-systems">
                <legend class="h6">External Systems</legend>
                @RenderCheckboxList(_externalSystems, e => e.Id, e => e.Name, _selectedExternalSystems)
            </fieldset>

            <fieldset class="mb-4" data-testid="group-db-connections">
                <legend class="h6">Database Connections</legend>
                @RenderCheckboxList(_dbConnections, d => d.Id, d => d.Name, _selectedDbConnections)
            </fieldset>

            <fieldset class="mb-4" data-testid="group-notification-lists">
                <legend class="h6">Notification Lists</legend>
                @RenderCheckboxList(_notificationLists, n => n.Id, n => n.Name, _selectedNotificationLists)
                <div class="alert alert-info small mt-2 mb-0 py-2" role="alert" data-testid="smtp-hint">
                    Selecting a notification list does <strong>not</strong> automatically include its
                    SMTP configuration. SMTP configurations are environment-specific and must be
                    selected separately if you want them in the bundle.
                </div>
            </fieldset>

            <fieldset class="mb-4" data-testid="group-smtp-configs">
                <legend class="h6">SMTP Configurations</legend>
                @RenderCheckboxList(_smtpConfigs, s => s.Id, s => s.Host, _selectedSmtpConfigs)
            </fieldset>

            <fieldset class="mb-4" data-testid="group-api-methods">
                <legend class="h6">API Methods</legend>
                @RenderCheckboxList(_apiMethods, m => m.Id, m => m.Name, _selectedApiMethods)
                <div class="alert alert-info small mt-2 mb-0 py-2" role="alert" data-testid="api-keys-not-transported">
                    <strong>API keys are not part of config transport.</strong> Inbound API keys
                    live in each environment's own secret store and cannot be exported. After
                    importing, re-create the keys on the destination and re-grant their method
                    scopes via the admin UI/CLI.
                </div>
            </fieldset>

            <div class="d-flex justify-content-end gap-2 mt-4">
                <button class="btn btn-primary"
                        disabled="@(!HasAnySelection || _resolving)"
                        @onclick="GoToReviewAsync">
                    @(_resolving ? "Resolving…" : "Next")
                </button>
            </div>
        </div>
    };

    private void OnTemplateSelectionChanged(HashSet<object> keys)
    {
        // TemplateFolderTree hands back a fresh HashSet each time; mirror it
        // into our owned set so subsequent renders see the same instance the
        // tree is binding against.
        _selectedTemplateKeys.Clear();
        foreach (var k in keys)
        {
            _selectedTemplateKeys.Add(k);
        }
    }

    private RenderFragment RenderCheckboxList<T>(
        IReadOnlyList<T> items,
        Func<T, int> idOf,
        Func<T, string> nameOf,
        HashSet<int> selected) => __builder =>
    {
        var visible = items.Where(x => MatchesFilter(nameOf(x))).ToList();
        if (visible.Count == 0)
        {
            <div class="text-muted small fst-italic">No matches.</div>
            return;
        }

        <div class="d-flex flex-column gap-1">
            @foreach (var item in visible)
            {
                var id = idOf(item);
                var inputId = $"chk-{typeof(T).Name}-{id}";
                <div class="form-check">
                    <input class="form-check-input" type="checkbox"
                           id="@inputId"
                           checked="@selected.Contains(id)"
                           @onchange="e => Toggle(selected, id, ((bool?)e.Value) == true)" />
                    <label class="form-check-label" for="@inputId">@nameOf(item)</label>
                </div>
            }
        </div>
    };

    // ============================================================
    // Step 2 — Review
    // ============================================================
    private RenderFragment RenderStepReview() => __builder =>
    {
        if (_resolved is null)
        {
            <div class="alert alert-warning">Nothing resolved yet — please go back to step 1.</div>
            return;
        }

        var seedTemplateIds = new HashSet<int>(SelectedTemplateIds());
        var seedSharedScripts = new HashSet<int>(_selectedSharedScripts);
        var seedExternalSystems = new HashSet<int>(_selectedExternalSystems);

        var autoTemplates = AutoIncluded(_resolved.Templates, seedTemplateIds, t => t.Id);
        var autoShared = AutoIncluded(_resolved.SharedScripts, seedSharedScripts, s => s.Id);
        var autoExternals = AutoIncluded(_resolved.ExternalSystems, seedExternalSystems, e => e.Id);

        <div>
            <p class="text-body-secondary">
                The resolver walked your selection's dependency graph and produced the closure
                below. Items under <em>Auto-included</em> were pulled in because the items you
                ticked reference them; unticking
                <em>Include all dependencies</em> exports the seed alone.
            </p>

            <div class="form-check form-switch mb-3">
                <input class="form-check-input" type="checkbox" id="include-deps"
                       checked="@_includeDependencies"
                       @onchange="async e => { _includeDependencies = ((bool?)e.Value) == true; await ReresolveAsync(); }" />
                <label class="form-check-label" for="include-deps">Include all dependencies</label>
            </div>

            <div class="row g-3">
                <div class="col-md-6" data-testid="seed-group">
                    <h6>Selected by you</h6>
                    <ul class="small list-unstyled mb-0">
                        @foreach (var t in _resolved.Templates.Where(t => seedTemplateIds.Contains(t.Id)).OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>Template: @t.Name</li>
                        }
                        @foreach (var s in _resolved.SharedScripts.Where(s => seedSharedScripts.Contains(s.Id)).OrderBy(s => s.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>SharedScript: @s.Name</li>
                        }
                        @foreach (var e in _resolved.ExternalSystems.Where(e => seedExternalSystems.Contains(e.Id)).OrderBy(e => e.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>ExternalSystem: @e.Name</li>
                        }
                        @foreach (var d in _resolved.DatabaseConnections.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>DatabaseConnection: @d.Name</li>
                        }
                        @foreach (var n in _resolved.NotificationLists.OrderBy(n => n.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>NotificationList: @n.Name</li>
                        }
                        @foreach (var s in _resolved.SmtpConfigs.OrderBy(s => s.Host, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>SmtpConfig: @s.Host</li>
                        }
                        @* Inbound API keys are not transported (re-arch C4) — methods only. *@
                        @foreach (var m in _resolved.ApiMethods.OrderBy(m => m.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            <li>ApiMethod: @m.Name</li>
                        }
                    </ul>
                </div>
                <div class="col-md-6" data-testid="auto-group">
                    <h6>Auto-included (dependencies)</h6>
                    @if (autoTemplates.Count + autoShared.Count + autoExternals.Count + _resolved.TemplateFolders.Count == 0)
                    {
                        <div class="small text-muted fst-italic">No additional dependencies.</div>
                    }
                    else
                    {
                        <ul class="small list-unstyled mb-0">
                            @foreach (var f in _resolved.TemplateFolders.OrderBy(f => f.Name, StringComparer.OrdinalIgnoreCase))
                            {
                                <li>TemplateFolder: @f.Name</li>
                            }
                            @foreach (var t in autoTemplates.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase))
                            {
                                <li>Template: @t.Name</li>
                            }
                            @foreach (var s in autoShared.OrderBy(s => s.Name, StringComparer.OrdinalIgnoreCase))
                            {
                                <li>SharedScript: @s.Name</li>
                            }
                            @foreach (var e in autoExternals.OrderBy(e => e.Name, StringComparer.OrdinalIgnoreCase))
                            {
                                <li>ExternalSystem: @e.Name</li>
                            }
                        </ul>
                    }
                </div>
            </div>

            <div class="d-flex justify-content-between mt-4">
                <button class="btn btn-outline-secondary" @onclick="BackToSelect">Back</button>
                <button class="btn btn-primary" @onclick="GoToEncrypt">Next</button>
            </div>
        </div>
    };

    // ============================================================
    // Step 3 — Encrypt
    // ============================================================
    private RenderFragment RenderStepEncrypt() => __builder =>
    {
        var strength = PassphraseStrength(_passphrase);
        var strengthLabel = strength switch
        {
            0 => "Too short",
            1 => "Weak",
            2 => "Fair",
            3 => "Good",
            _ => "Strong",
        };
        var strengthColor = strength switch
        {
            <= 1 => "bg-danger",
            2 => "bg-warning",
            3 => "bg-info",
            _ => "bg-success",
        };

        <div>
            @if (_secretCount > 0)
            {
                <div class="alert alert-warning" role="alert" data-testid="secrets-warning">
                    <strong>@_secretCount</strong> secret @(_secretCount == 1 ? "field" : "fields")
                    will be encrypted (external-system credentials, SMTP credentials, and database
                    connection strings).
                </div>
            }
            else
            {
                <div class="alert alert-info small" role="alert" data-testid="secrets-warning">
                    The selected closure contains no secret fields, but the bundle's content
                    payload will still be encrypted in full when a passphrase is supplied.
                </div>
            }

            <div class="mb-3">
                <label for="passphrase" class="form-label">Passphrase</label>
                <input id="passphrase" type="password" class="form-control"
                       autocomplete="new-password"
                       @bind="_passphrase" @bind:event="oninput" />
                <div class="progress mt-1" style="height: 4px;">
                    <div class="progress-bar @strengthColor"
                         role="progressbar"
                         style="width: @(strength * 25)%;"
                         aria-valuenow="@(strength * 25)" aria-valuemin="0" aria-valuemax="100"></div>
                </div>
                <div class="form-text">Strength: @strengthLabel · minimum 8 characters.</div>
            </div>

            <div class="mb-3">
                <label for="passphrase-confirm" class="form-label">Confirm passphrase</label>
                <input id="passphrase-confirm" type="password" class="form-control"
                       autocomplete="new-password"
                       @bind="_passphraseConfirm" @bind:event="oninput" />
                @if (!string.IsNullOrEmpty(_passphraseConfirm) && _passphrase != _passphraseConfirm)
                {
                    <div class="form-text text-danger">Passphrases do not match.</div>
                }
            </div>

            <p class="small">
                <a href="javascript:void(0)" class="link-danger" @onclick="OpenUnencryptedConfirm">
                    Export without encryption…
                </a>
            </p>

            @if (_showUnencryptedConfirm)
            {
                <div class="alert alert-danger" data-testid="unencrypted-confirm">
                    <strong>Unencrypted export</strong> — the bundle will contain all secret fields
                    in plaintext. Anyone with the file can read external-system credentials, SMTP
                    passwords, and database connection strings. The audit log will record this as
                    <code>UnencryptedBundleExport</code>.
                    <div class="mt-2 d-flex gap-2">
                        <button class="btn btn-sm btn-danger" @onclick="ConfirmUnencryptedExport">
                            Yes, export without encryption
                        </button>
                        <button class="btn btn-sm btn-outline-secondary" @onclick="CancelUnencryptedConfirm">
                            Cancel
                        </button>
                    </div>
                </div>
            }

            <div class="d-flex justify-content-between mt-4">
                <button class="btn btn-outline-secondary" @onclick="BackToReview">Back</button>
                <button class="btn btn-primary"
                        disabled="@(!PassphraseValid)"
                        @onclick="StartEncryptedExportAsync">
                    Export
                </button>
            </div>
        </div>
    };

    // ============================================================
    // Step 4 — Download
    // ============================================================
    private RenderFragment RenderStepDownload() => __builder =>
    {
        <div>
            @if (_downloadInProgress)
            {
                <LoadingSpinner IsLoading="true" />
                <p class="text-body-secondary">Building bundle…</p>
            }
            else if (_downloadError != null)
            {
                <div class="alert alert-danger">
                    <strong>Export failed:</strong> @_downloadError
                </div>
                <button class="btn btn-outline-secondary" @onclick="BackToReview">Back</button>
            }
            else
            {
                <div class="alert alert-success" data-testid="download-summary">
                    <strong>Bundle ready.</strong> Your browser is downloading the file.
                </div>

                <dl class="row small">
                    <dt class="col-sm-3">Filename</dt>
                    <dd class="col-sm-9"><code>@_downloadFilename</code></dd>

                    <dt class="col-sm-3">Size</dt>
                    <dd class="col-sm-9">@FormatBytes(_downloadSize)</dd>

                    <dt class="col-sm-3">SHA-256</dt>
                    <dd class="col-sm-9"><code>@_downloadSha256</code></dd>

                    <dt class="col-sm-3">Encryption</dt>
                    <dd class="col-sm-9">
                        @if (_exportUnencrypted)
                        {
                            <span class="text-danger">Unencrypted (audited as <code>UnencryptedBundleExport</code>)</span>
                        }
                        else
                        {
                            <span class="text-success">AES-256-GCM with PBKDF2-SHA256</span>
                        }
                    </dd>
                </dl>

                <button class="btn btn-primary" @onclick="Done">Done</button>
            }
        </div>
    };

    private static string FormatBytes(long bytes)
    {
        if (bytes < 1024) return $"{bytes} B";
        if (bytes < 1024 * 1024) return $"{bytes / 1024.0:0.0} KB";
        return $"{bytes / (1024.0 * 1024.0):0.00} MB";
    }
}
