@* Bulk equipment import modal wired straight into IUnsTreeService. Paste a CSV (header + rows),
   the modal parses it into EquipmentInput rows and calls ImportEquipmentAsync, then shows the
   Inserted / Skipped / Errors summary in place. The host owns visibility; "Close" raises OnImported
   so the page can reload the whole tree (an import can add equipment across many lines/clusters).
   Required header columns (in order): Name, MachineCode, UnsLineId.
   Optional: ZTag, SAPID, Manufacturer, Model. Existing rows are detected by MachineCode and
   skipped (additive-only — no updates), matching the retired /clusters/{id}/equipment/import page.
   v3: equipment no longer carries a driver, so the old DriverInstanceId column is gone. *@
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
@inject IUnsTreeService Svc

@if (Visible)
{
    <div class="modal-backdrop fade show" style="display:block"></div>
    <div class="modal fade show" tabindex="-1" role="dialog" style="display:block">
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Import equipment CSV</h5>
                    <button type="button" class="btn-close" aria-label="Close" @onclick="CloseAsync"></button>
                </div>
                <div class="modal-body">
                    <p class="text-muted small mb-2">
                        Paste CSV below. Required header columns (in order):
                        <span class="mono">Name, MachineCode, UnsLineId</span>.
                        Optional: <span class="mono">ZTag, SAPID, Manufacturer, Model</span>.
                        Each row inserts one equipment with a freshly-generated EquipmentId. Existing rows
                        are detected by MachineCode and skipped (additive-only — no updates).
                    </p>

                    <textarea class="form-control form-control-sm mono" rows="12"
                              @bind="_csv" @bind:event="oninput" disabled="@_busy"
                              placeholder="Name,MachineCode,UnsLineId,ZTag,SAPID,Manufacturer,Model&#10;mixer-01,MX_001,LINE-1,ZT-12345,SAP-9876,Siemens,SIMATIC-1500"></textarea>
                    <div class="form-text">Simple comma-separated values only — fields containing commas are not supported.</div>

                    @if (!string.IsNullOrWhiteSpace(_parseError))
                    {
                        <div class="text-danger small mt-2">@_parseError</div>
                    }

                    @if (_result is not null)
                    {
                        <div class="alert alert-info mt-3 mb-0" role="alert">
                            <div>
                                <strong>Inserted:</strong> @_result.Inserted
                                &middot; <strong>Skipped (existing MachineCode):</strong> @_result.Skipped
                                &middot; <strong>Errors:</strong> @_result.Errors.Count
                            </div>
                            @if (_result.Errors.Count > 0)
                            {
                                <ul class="small mb-0 mt-2">
                                    @foreach (var err in _result.Errors)
                                    {
                                        <li>@err</li>
                                    }
                                </ul>
                            }
                        </div>
                    }
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-outline-secondary" @onclick="CloseAsync" disabled="@_busy">
                        @(_result is null ? "Cancel" : "Close")
                    </button>
                    <button type="button" class="btn btn-primary" @onclick="ImportAsync" disabled="@_busy">
                        @if (_busy) { <span class="spinner-border spinner-border-sm me-1"></span> }
                        Import
                    </button>
                </div>
            </div>
        </div>
    </div>
}

@code {
    // Required, in order. v3: equipment no longer binds a driver, so DriverInstanceId is gone.
    private static readonly string[] RequiredColumns = ["Name", "MachineCode", "UnsLineId"];

    /// <summary>Whether the modal is shown. The host owns this flag.</summary>
    [Parameter] public bool Visible { get; set; }

    /// <summary>
    /// Raised when the user closes the modal after an import so the host can reload the tree and
    /// dismiss the modal. Fired on close regardless of whether anything was inserted — closing always
    /// returns control to the host.
    /// </summary>
    [Parameter] public EventCallback OnImported { get; set; }

    /// <summary>Raised when the user cancels before importing so the host can close.</summary>
    [Parameter] public EventCallback OnCancel { get; set; }

    private string _csv = "";
    private bool _busy;
    private string? _parseError;
    private EquipmentImportResult? _result;
    private bool _wasVisible;

    protected override void OnParametersSet()
    {
        // Reset only on the false→true transition so that re-renders while open don't wipe state.
        if (Visible && !_wasVisible)
        {
            _csv = "";
            _parseError = null;
            _result = null;
        }
        _wasVisible = Visible;
    }

    private async Task ImportAsync()
    {
        _busy = true;
        _parseError = null;
        _result = null;
        try
        {
            var rows = ParseCsv(_csv);
            if (rows is null) { return; }

            _result = await Svc.ImportEquipmentAsync(rows);
        }
        finally
        {
            _busy = false;
        }
    }

    /// <summary>
    /// Closes the modal. Once an import has run (the user has seen the summary), closing raises
    /// OnImported so the host reloads the tree; before any import it raises OnCancel.
    /// </summary>
    private Task CloseAsync() =>
        _result is not null ? OnImported.InvokeAsync() : OnCancel.InvokeAsync();

    /// <summary>
    /// Parses the pasted CSV into <see cref="EquipmentInput"/> rows. Requires a header row whose first
    /// three columns are Name, MachineCode, UnsLineId (case-insensitive); the optional
    /// ZTag/SAPID/Manufacturer/Model follow. Blank optional cells parse as <c>null</c>. Returns
    /// <c>null</c> (and sets <see cref="_parseError"/>) when the CSV is empty or the header is wrong.
    /// </summary>
    private List<EquipmentInput>? ParseCsv(string csv)
    {
        if (string.IsNullOrWhiteSpace(csv)) { _parseError = "CSV is empty."; return null; }

        var lines = csv.Replace("\r\n", "\n").Split('\n', StringSplitOptions.RemoveEmptyEntries);
        if (lines.Length < 2) { _parseError = "Need a header row and at least one data row."; return null; }

        var header = lines[0].Split(',').Select(c => c.Trim()).ToArray();
        for (var i = 0; i < RequiredColumns.Length; i++)
        {
            if (i >= header.Length || !string.Equals(header[i], RequiredColumns[i], StringComparison.OrdinalIgnoreCase))
            {
                _parseError = $"Header column #{i + 1} must be '{RequiredColumns[i]}' (got '{(i < header.Length ? header[i] : "")}').";
                return null;
            }
        }

        var rows = new List<EquipmentInput>();
        for (var lineIdx = 1; lineIdx < lines.Length; lineIdx++)
        {
            // NOTE: simple comma split — no RFC4180 quoting; values with commas are not supported.
            var parts = lines[lineIdx].Split(',').Select(c => c.Trim()).ToArray();
            if (parts.Length < RequiredColumns.Length)
            {
                _parseError = $"Row {lineIdx}: too few columns (got {parts.Length}, need {RequiredColumns.Length}).";
                return null;
            }

            rows.Add(new EquipmentInput(
                Name: parts[0],
                MachineCode: parts[1],
                UnsLineId: parts[2],
                ZTag: NullIfEmpty(parts, 3),
                SAPID: NullIfEmpty(parts, 4),
                Manufacturer: NullIfEmpty(parts, 5),
                Model: NullIfEmpty(parts, 6),
                SerialNumber: null,
                HardwareRevision: null,
                SoftwareRevision: null,
                YearOfConstruction: null,
                AssetLocation: null,
                ManufacturerUri: null,
                DeviceManualUri: null,
                Enabled: true));
        }
        return rows;
    }

    private static string? NullIfEmpty(string[] parts, int idx) =>
        idx < parts.Length && !string.IsNullOrWhiteSpace(parts[idx]) ? parts[idx] : null;
}
