@* Create / edit modal for a raw-tree Device's connection endpoint (v3 endpoint→DeviceConfig split).
   Dispatches to the right per-driver device form by DriverType. Test-connect builds the merged
   Driver+Device config transiently (so unsaved edits probe correctly), via DriverDeviceConfigMerger —
   the same merge LoadMergedProbeConfigAsync uses. The coordinator wires this into RawTree via @bind-Visible. *@
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.DeviceForms
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
@using ZB.MOM.WW.OtOpcUa.Commons.Types
@using ZB.MOM.WW.OtOpcUa.Core.Abstractions
@inject IRawTreeService 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">@(_isNew ? "New device" : "Edit device") &middot; <span class="mono">@_driverType</span></h5>
                    <button type="button" class="btn-close" aria-label="Close" @onclick="CloseAsync"></button>
                </div>
                <div class="modal-body">
                    @if (_loadError is not null)
                    {
                        <div class="panel notice mb-3" style="border-color:var(--alert)">@_loadError</div>
                    }
                    else
                    {
                        <div class="row g-3 mb-2">
                            <div class="col-md-6">
                                <label class="form-label">Device name</label>
                                <input class="form-control form-control-sm mono" @bind="_name" placeholder="Device1" />
                                <div class="form-text">A RawPath segment — letters, digits, dash, underscore.</div>
                            </div>
                            <div class="col-md-3">
                                <div class="form-check form-switch mt-4">
                                    <input type="checkbox" class="form-check-input" id="devEnabled" @bind="_enabled" />
                                    <label class="form-check-label" for="devEnabled">Enabled</label>
                                </div>
                            </div>
                        </div>

                        @switch (_driverType)
                        {
                            case DriverTypeNames.Modbus:
                                <ModbusDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.S7:
                                <S7DeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.AbCip:
                                <AbCipDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.AbLegacy:
                                <AbLegacyDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.TwinCAT:
                                <TwinCATDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.FOCAS:
                                <FocasDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.OpcUaClient:
                                <OpcUaClientDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            case DriverTypeNames.Galaxy:
                                <GalaxyDeviceForm @bind-DeviceConfigJson="_deviceConfigJson" />
                                break;
                            default:
                                <div class="alert alert-warning">No typed device form for driver type <span class="mono">@_driverType</span>.</div>
                                break;
                        }

                        <div class="mt-3">
                            <DriverTestConnectButton DriverType="@_driverType" GetConfigJson="@BuildMergedProbeConfig" />
                        </div>

                        @if (_saveError is not null)
                        {
                            <div class="panel notice mt-3" style="border-color:var(--alert)">@_saveError</div>
                        }
                    }
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-outline-secondary" @onclick="CloseAsync" disabled="@_busy">Cancel</button>
                    @if (_loadError is null)
                    {
                        <button type="button" class="btn btn-primary" @onclick="SaveAsync" disabled="@_busy">
                            @if (_busy) { <span class="spinner-border spinner-border-sm me-1"></span> }
                            @(_isNew ? "Create device" : "Save changes")
                        </button>
                    }
                </div>
            </div>
        </div>
    </div>
}

@code {
    /// <summary>Whether the modal is shown (supports @bind-Visible).</summary>
    [Parameter] public bool Visible { get; set; }
    /// <summary>Two-way visibility callback.</summary>
    [Parameter] public EventCallback<bool> VisibleChanged { get; set; }

    /// <summary>The device to edit; null ⇒ create a new device under <see cref="DriverInstanceId"/>.</summary>
    [Parameter] public string? DeviceId { get; set; }
    /// <summary>The owning driver instance (required for create; resolved from the device on edit).</summary>
    [Parameter] public string? DriverInstanceId { get; set; }
    /// <summary>The driver type (required for create; joined in from the device on edit).</summary>
    [Parameter] public string? DriverType { get; set; }

    /// <summary>Raised after a successful create/update so the tree can refresh.</summary>
    [Parameter] public EventCallback OnSaved { get; set; }

    private bool _isNew;
    private string _driverType = "";
    private string? _resolvedDriverInstanceId;
    private string _name = "";
    private bool _enabled = true;
    private string _deviceConfigJson = "{}";
    private byte[] _rowVersion = [];
    private string _parentDriverConfig = "{}";

    private bool _busy;
    private string? _loadError;
    private string? _saveError;
    private bool _openHandled;

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

    private async Task LoadAsync()
    {
        _busy = false; _loadError = null; _saveError = null;
        _isNew = string.IsNullOrEmpty(DeviceId);

        if (_isNew)
        {
            _resolvedDriverInstanceId = DriverInstanceId;
            _driverType = DriverType ?? "";
            _name = "Device1";
            _enabled = true;
            _deviceConfigJson = "{}";
            _rowVersion = [];
        }
        else
        {
            var dto = await Svc.LoadDeviceForEditAsync(DeviceId!);
            if (dto is null) { _loadError = $"Device '{DeviceId}' was not found."; return; }
            _resolvedDriverInstanceId = dto.DriverInstanceId;
            _driverType = dto.DriverType;
            _name = dto.Name;
            _enabled = dto.Enabled;
            _deviceConfigJson = string.IsNullOrWhiteSpace(dto.DeviceConfig) ? "{}" : dto.DeviceConfig;
            _rowVersion = dto.RowVersion;
        }

        // The parent driver's DriverConfig — folded with the (possibly unsaved) DeviceConfig for Test-connect.
        if (!string.IsNullOrEmpty(_resolvedDriverInstanceId))
        {
            var drv = await Svc.LoadDriverForEditAsync(_resolvedDriverInstanceId);
            _parentDriverConfig = drv?.DriverConfig ?? "{}";
        }
    }

    /// <summary>Builds the merged Driver+Device probe config from the CURRENT (possibly unsaved) form —
    /// same merge as <c>LoadMergedProbeConfigAsync</c>, so Test-connect works before the device is saved.</summary>
    private string BuildMergedProbeConfig()
        => DriverDeviceConfigMerger.Merge(
            _parentDriverConfig,
            new[] { new DriverDeviceConfigMerger.DeviceRow(_name, _deviceConfigJson) },
            Array.Empty<RawTagEntry>());

    private async Task SaveAsync()
    {
        _busy = true; _saveError = null;
        try
        {
            UnsMutationResult result;
            if (_isNew)
            {
                if (string.IsNullOrEmpty(_resolvedDriverInstanceId))
                {
                    _saveError = "No owning driver instance was supplied.";
                    return;
                }
                result = await Svc.CreateDeviceAsync(_resolvedDriverInstanceId, _name, _deviceConfigJson);
            }
            else
            {
                result = await Svc.UpdateDeviceAsync(DeviceId!, _name, _deviceConfigJson, _enabled, _rowVersion);
            }

            if (!result.Ok)
            {
                _saveError = result.Error ?? "Save failed.";
                return;
            }

            await OnSaved.InvokeAsync();
            await SetVisibleAsync(false);
        }
        catch (Exception ex) { _saveError = ex.Message; }
        finally { _busy = false; }
    }

    private Task CloseAsync() => SetVisibleAsync(false);

    private async Task SetVisibleAsync(bool value)
    {
        Visible = value;
        _openHandled = value;
        await VisibleChanged.InvokeAsync(value);
    }
}
