@* CSV tag export for a Raw-tree Device / TagGroup (B2-WP5).

   Reads the device's tags (via the WP5-owned RawTagCsvExportReader — no IRawTreeService extension) and
   renders them in the same column shape RawCsvImportModal consumes (T0-2 CsvWriter, per-driver
   CsvColumnMap). The CSV is offered as a self-contained data-URI download anchor (no JS interop, no
   external host — matches the app's CSP posture) plus a preview textarea.

   Modal contract (the RawTree coordinator wires this in place of the OnExportCsv stub):
     <RawCsvExportModal @bind-Visible="_exportVisible"
                        DeviceId="@_exportDeviceId"      (may be blank if TagGroupId is supplied)
                        TagGroupId="@_exportTagGroupId"  (when set, only that group's subtree is exported)
                        DriverType="@_exportDriverType" /> *@
@using System.Text
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
@inject RawTagCsvExportReader ExportReader

@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">Export tags — CSV</h5>
                    <button type="button" class="btn-close" aria-label="Close" @onclick="Close"></button>
                </div>
                <div class="modal-body">
                    @if (_loading)
                    {
                        <p class="text-muted"><span class="spinner-border spinner-border-sm"></span> Reading tags…</p>
                    }
                    else if (_error is not null)
                    {
                        <div class="alert alert-danger">@_error</div>
                    }
                    else
                    {
                        <p class="text-muted">
                            <span class="badge bg-secondary">@_rowCount tags</span>
                            for <span class="mono">@DriverType</span> device
                            @if (!string.IsNullOrEmpty(_groupFilter))
                            {
                                <span>, group <span class="mono">@_groupFilter</span></span>
                            }
                        </p>
                        <textarea class="form-control mono small" rows="10" readonly>@_csv</textarea>
                    }
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-outline-secondary" @onclick="Close">Close</button>
                    @if (!_loading && _error is null)
                    {
                        <a class="btn btn-primary" download="@_fileName" href="@_dataUri">Download CSV</a>
                    }
                </div>
            </div>
        </div>
    </div>
}

@code {
    /// <summary>Whether the modal is shown (two-way bound).</summary>
    [Parameter] public bool Visible { get; set; }

    /// <summary>Raised when <see cref="Visible"/> changes (supports <c>@bind-Visible</c>).</summary>
    [Parameter] public EventCallback<bool> VisibleChanged { get; set; }

    /// <summary>The device to export. May be blank when <see cref="TagGroupId"/> is supplied.</summary>
    [Parameter] public string DeviceId { get; set; } = "";

    /// <summary>When set, only tags under this group's subtree are exported; null exports the whole device.</summary>
    [Parameter] public string? TagGroupId { get; set; }

    /// <summary>The device's driver-type string — selects the per-driver typed column set.</summary>
    [Parameter] public string DriverType { get; set; } = "";

    private bool _loading;
    private bool _built;
    private string? _error;
    private string _csv = "";
    private string _dataUri = "";
    private string _fileName = "tags.csv";
    private int _rowCount;
    private string? _groupFilter;

    protected override async Task OnParametersSetAsync()
    {
        if (Visible && !_built)
        {
            _built = true;
            await BuildAsync();
        }
        else if (!Visible)
        {
            _built = false;
        }
    }

    private async Task BuildAsync()
    {
        _loading = true;
        _error = null;
        try
        {
            var deviceId = DeviceId;
            _groupFilter = null;

            if (!string.IsNullOrEmpty(TagGroupId))
            {
                var ctx = await ExportReader.ResolveGroupContextAsync(TagGroupId!);
                if (ctx is { } c)
                {
                    _groupFilter = c.GroupPath;
                    if (string.IsNullOrEmpty(deviceId))
                    {
                        deviceId = c.DeviceId;
                    }
                }
            }

            if (string.IsNullOrEmpty(deviceId))
            {
                _error = "Could not resolve the target device.";
                return;
            }

            var tags = await ExportReader.ReadDeviceTagsAsync(deviceId);

            // When exporting a single group, keep only that group's subtree (its path + descendants).
            if (!string.IsNullOrEmpty(_groupFilter))
            {
                var prefix = _groupFilter;
                tags = tags
                    .Where(t => t.TagGroupPath is not null &&
                                (t.TagGroupPath == prefix || t.TagGroupPath.StartsWith(prefix + "/", StringComparison.Ordinal)))
                    .ToList();
            }

            _rowCount = tags.Count;
            _csv = RawTagCsvMapper.Export(DriverType, tags);
            _fileName = BuildFileName(deviceId);

            var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(_csv));
            _dataUri = $"data:text/csv;charset=utf-8;base64,{base64}";
        }
        catch (Exception ex)
        {
            _error = ex.Message;
        }
        finally
        {
            _loading = false;
        }
    }

    private static string BuildFileName(string deviceId)
    {
        var safe = new string(deviceId.Select(c => char.IsLetterOrDigit(c) ? c : '-').ToArray());
        return $"tags-{safe}.csv";
    }

    private async Task Close()
    {
        _built = false;
        _csv = "";
        _dataUri = "";
        Visible = false;
        await VisibleChanged.InvokeAsync(false);
    }
}
