using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.OtOpcUa.Core.Hosting;

namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;

/// <summary>
///     Registers the OPC UA Client driver with the <see cref="DriverFactoryRegistry"/>. The driver's
///     <c>DriverConfig</c> JSON deserialises directly into <see cref="OpcUaClientDriverOptions"/>
///     (the same shape <see cref="OpcUaClientDriverProbe"/> reads), so no separate DTO is needed —
///     unlike the protocol drivers, this driver has no pre-declared "Tags" DTO path to retire.
///     v3: the deploy artifact's authored raw tags bind straight onto
///     <see cref="OpcUaClientDriverOptions.RawTags"/> (a <c>RawTagEntry</c> list) through the same
///     deserialiser; the <c>EndpointUrl</c> binding is unchanged (endpoint→DeviceConfig is Wave C).
///     Mirrors <c>ModbusDriverFactoryExtensions</c> / <c>GalaxyDriverFactoryExtensions</c>.
/// </summary>
public static class OpcUaClientDriverFactoryExtensions
{
    /// <summary>Driver type name — matches <c>DriverInstance.DriverType</c> values.</summary>
    public const string DriverTypeName = "OpcUaClient";

    // Match OpcUaClientDriverProbe exactly so factory + probe parse a config identically.
    // The JsonStringEnumConverter lets enum-valued knobs (SecurityMode / SecurityPolicy /
    // AuthType / TargetNamespaceKind) be authored as their string names — the natural form
    // for human-edited + AdminUI-emitted DriverConfig JSON.
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
        Converters = { new JsonStringEnumConverter() },
    };

    /// <summary>Register the OpcUaClient factory with the driver registry.</summary>
    /// <param name="registry">The driver factory registry to register with.</param>
    /// <param name="loggerFactory">Optional logger factory used to create per-instance loggers.</param>
    public static void Register(DriverFactoryRegistry registry, ILoggerFactory? loggerFactory = null)
    {
        ArgumentNullException.ThrowIfNull(registry);
        registry.Register(DriverTypeName, (id, json) => CreateInstance(id, json, loggerFactory));
    }

    /// <summary>Public for the Server-side bootstrapper + test consumers.</summary>
    /// <param name="driverInstanceId">The unique identifier for the driver instance.</param>
    /// <param name="driverConfigJson">The JSON configuration string for the driver.</param>
    /// <param name="loggerFactory">Optional logger factory for the per-instance logger.</param>
    /// <returns>A configured <see cref="OpcUaClientDriver"/>.</returns>
    public static OpcUaClientDriver CreateInstance(
        string driverInstanceId, string driverConfigJson, ILoggerFactory? loggerFactory = null)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
        ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);

        var options = JsonSerializer.Deserialize<OpcUaClientDriverOptions>(driverConfigJson, JsonOptions)
            ?? throw new InvalidOperationException(
                $"OpcUaClient driver config for '{driverInstanceId}' deserialised to null");

        return new OpcUaClientDriver(options, driverInstanceId, loggerFactory?.CreateLogger<OpcUaClientDriver>());
    }
}
