@* Create/edit modal for an equipment-bound tag, wired straight into IUnsTreeService. The host page
   owns visibility and supplies the owning equipment id (create) or the loaded TagEditDto (edit), plus
   the equipment-scoped candidate-driver list. Tree tags are always equipment-bound (decision #110), so
   the legacy SystemPlatform/FolderPath branch is dropped entirely — there is no equipment selector
   either, the owning equipment is fixed. On a successful save it raises OnSaved so the host can
   refresh the equipment's children in place. *@
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Components.Forms
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors
@using ZB.MOM.WW.OtOpcUa.Configuration.Enums
@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">
                <EditForm Model="_form" OnValidSubmit="SaveAsync" FormName="tagModal">
                    <DataAnnotationsValidator />
                    <div class="modal-header">
                        <h5 class="modal-title">@(IsNew ? "New tag" : "Edit tag")</h5>
                        <button type="button" class="btn-close" aria-label="Close" @onclick="CancelAsync"></button>
                    </div>
                    <div class="modal-body">
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label class="form-label" for="tag-id">TagId</label>
                                <InputText id="tag-id" @bind-Value="_form.TagId" disabled="@(!IsNew)"
                                           class="form-control form-control-sm mono"
                                           placeholder="tag-line3-temp-01" />
                                <ValidationMessage For="@(() => _form.TagId)" />
                            </div>
                            <div class="col-md-6 mb-3">
                                <label class="form-label" for="tag-name">Name</label>
                                <InputText id="tag-name" @bind-Value="_form.Name" class="form-control form-control-sm"
                                           placeholder="Temperature setpoint" />
                                <ValidationMessage For="@(() => _form.Name)" />
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label class="form-label" for="tag-driver">Driver instance</label>
                                <InputSelect id="tag-driver" @bind-Value="_form.DriverInstanceId" @bind-Value:after="OnDriverChanged" class="form-select form-select-sm">
                                    <option value="">— pick a driver —</option>
                                    @foreach (var (id, display, _) in Drivers)
                                    {
                                        <option value="@id">@display</option>
                                    }
                                </InputSelect>
                                <ValidationMessage For="@(() => _form.DriverInstanceId)" />
                            </div>
                            <div class="col-md-6 mb-3">
                                <label class="form-label" for="tag-dtype">Data type</label>
                                <InputSelect id="tag-dtype" @bind-Value="_form.DataType" class="form-select form-select-sm">
                                    @foreach (var dt in DataTypes)
                                    {
                                        <option value="@dt">@dt</option>
                                    }
                                </InputSelect>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label class="form-label" for="tag-access">Access level</label>
                                <InputSelect id="tag-access" @bind-Value="_form.AccessLevel" class="form-select form-select-sm">
                                    <option value="@TagAccessLevel.Read">Read</option>
                                    <option value="@TagAccessLevel.ReadWrite">ReadWrite</option>
                                </InputSelect>
                            </div>
                            <div class="col-md-6 mb-3">
                                <label class="form-label">WriteIdempotent</label>
                                <div class="form-check form-switch">
                                    <InputCheckbox @bind-Value="_form.WriteIdempotent" class="form-check-input" />
                                    <label class="form-check-label">Safe to retry writes (decision #44–45)</label>
                                </div>
                            </div>
                        </div>
                        <div class="mb-3">
                            <label class="form-label" for="tag-pgroup">PollGroupId (optional)</label>
                            <InputText id="tag-pgroup" @bind-Value="_form.PollGroupId" class="form-control form-control-sm mono" />
                        </div>
                        <div class="mb-3">
                            <label class="form-label">Tag config</label>
                            @{
                                var editorType = TagConfigEditorMap.Resolve(SelectedDriverType);
                            }
                            @if (string.IsNullOrEmpty(_form.DriverInstanceId))
                            {
                                <div class="form-text">Pick a driver above to configure this tag.</div>
                            }
                            else if (editorType is not null)
                            {
                                <DynamicComponent Type="editorType" Parameters="BuildEditorParameters()" />
                            }
                            else
                            {
                                <InputTextArea id="tag-config" @bind-Value="_form.TagConfig" rows="6"
                                               class="form-control form-control-sm mono"
                                               placeholder='{ "register": 40001, "scale": 0.1 }' />
                                <div class="form-text">Schemaless per driver type. Validated server-side at deploy.</div>
                            }
                            <ValidationMessage For="@(() => _form.TagConfig)" />
                        </div>

                        @if (!string.IsNullOrWhiteSpace(_error))
                        {
                            <div class="text-danger small mt-2">@_error</div>
                        }
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-outline-secondary" @onclick="CancelAsync" disabled="@_busy">Cancel</button>
                        <button type="submit" class="btn btn-primary" disabled="@_busy">
                            @if (_busy) { <span class="spinner-border spinner-border-sm me-1"></span> }
                            @(IsNew ? "Create" : "Save changes")
                        </button>
                    </div>
                </EditForm>
            </div>
        </div>
    </div>
}

@code {
    private static readonly string[] DataTypes =
        ["Boolean", "SByte", "Byte", "Int16", "UInt16", "Int32", "UInt32",
         "Int64", "UInt64", "Float", "Double", "String", "DateTime", "Guid", "ByteString"];

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

    /// <summary><c>true</c> to create a new tag; <c>false</c> to edit <see cref="Existing"/>.</summary>
    [Parameter] public bool IsNew { get; set; }

    /// <summary>The owning equipment id the created tag binds to (used only on create).</summary>
    [Parameter] public string? EquipmentId { get; set; }

    /// <summary>The tag being edited, when <see cref="IsNew"/> is <c>false</c>.</summary>
    [Parameter] public TagEditDto? Existing { get; set; }

    /// <summary>The candidate drivers — scoped to the equipment's cluster by the host — as <c>(Id, Display)</c> pairs.</summary>
    [Parameter] public IReadOnlyList<(string Id, string Display, string DriverType)> Drivers { get; set; } = Array.Empty<(string, string, string)>();

    /// <summary>Raised after a successful create/save so the host can refresh the equipment's children and close.</summary>
    [Parameter] public EventCallback OnSaved { get; set; }

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

    private FormModel _form = new();
    private bool _busy;
    private string? _error;

    // The DriverType of the currently-selected driver (drives editor dispatch). Null when no driver chosen.
    private string? SelectedDriverType =>
        Drivers.FirstOrDefault(d => d.Id == _form.DriverInstanceId).DriverType;

    // When the operator switches drivers, the previous driver's TagConfig schema no longer applies —
    // reset it so the newly-dispatched typed editor starts clean (no stale/leaked keys from the old
    // driver). Fires only on a user dropdown change (@bind-Value:after), not on the initial edit-load.
    private void OnDriverChanged() => _form.TagConfig = "{}";

    private IDictionary<string, object> BuildEditorParameters() => new Dictionary<string, object>
    {
        ["ConfigJson"] = _form.TagConfig,
        ["ConfigJsonChanged"] = EventCallback.Factory.Create<string>(this, v => _form.TagConfig = v),
    };

    protected override void OnParametersSet()
    {
        // Rebuild the working form whenever the host (re)opens the modal for a fresh target.
        if (IsNew)
        {
            _form = new FormModel();
        }
        else if (Existing is not null)
        {
            _form = new FormModel
            {
                TagId = Existing.TagId,
                Name = Existing.Name,
                DriverInstanceId = Existing.DriverInstanceId,
                DataType = Existing.DataType,
                AccessLevel = Existing.AccessLevel,
                WriteIdempotent = Existing.WriteIdempotent,
                PollGroupId = Existing.PollGroupId,
                TagConfig = Existing.TagConfig,
            };
        }
        _error = null;
    }

    private async Task SaveAsync()
    {
        _busy = true;
        _error = null;
        try
        {
            // Client-side per-driver config validation (the typed editor's Validate()), so a blank
            // required field is caught here rather than silently saving and failing at deploy/connect.
            var configError = TagConfigValidator.Validate(SelectedDriverType, _form.TagConfig);
            if (configError is not null)
            {
                _error = configError;
                return;
            }

            var input = new TagInput(
                _form.TagId,
                _form.Name,
                _form.DriverInstanceId,
                _form.DataType,
                _form.AccessLevel,
                _form.WriteIdempotent,
                _form.PollGroupId,
                _form.TagConfig);

            var result = IsNew
                ? await Svc.CreateTagAsync(EquipmentId!, input)
                : await Svc.UpdateTagAsync(Existing!.TagId, input, Existing.RowVersion);

            if (result.Ok)
            {
                await OnSaved.InvokeAsync();
            }
            else
            {
                _error = result.Error;
            }
        }
        finally
        {
            _busy = false;
        }
    }

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

    private sealed class FormModel
    {
        [Required, RegularExpression("^[A-Za-z0-9_-]+$")] public string TagId { get; set; } = "";
        [Required] public string Name { get; set; } = "";
        [Required] public string DriverInstanceId { get; set; } = "";
        public string DataType { get; set; } = "Float";
        public TagAccessLevel AccessLevel { get; set; } = TagAccessLevel.Read;
        public bool WriteIdempotent { get; set; }
        public string? PollGroupId { get; set; }
        [Required] public string TagConfig { get; set; } = "{}";
    }
}
