namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers;

/// <summary>
///     Converts raw string values into typed values based on the current value's runtime type.
///     Ported from the CLI tool's OpcUaHelper.ConvertValue.
/// </summary>
public static class ValueConverter
{
    /// <summary>
    ///     Converts a raw string value into the runtime type expected by the target node.
    /// </summary>
    /// <param name="rawValue">The raw string supplied by the user.</param>
    /// <param name="currentValue">
    ///     The current node value used to infer the target type. When <c>null</c> the raw string
    ///     is returned unchanged; callers should validate this case before writing.
    /// </param>
    /// <returns>A typed value suitable for an OPC UA write request.</returns>
    /// <exception cref="FormatException">
    ///     Thrown with a descriptive message when <paramref name="rawValue"/> cannot be parsed
    ///     into the type inferred from <paramref name="currentValue"/>.
    /// </exception>
    public static object ConvertValue(string rawValue, object? currentValue)
    {
        try
        {
            return currentValue switch
            {
                bool => ParseBool(rawValue),
                byte => byte.Parse(rawValue),
                short => short.Parse(rawValue),
                ushort => ushort.Parse(rawValue),
                int => int.Parse(rawValue),
                uint => uint.Parse(rawValue),
                long => long.Parse(rawValue),
                ulong => ulong.Parse(rawValue),
                float => float.Parse(rawValue),
                double => double.Parse(rawValue),
                _ => rawValue
            };
        }
        catch (Exception ex) when (ex is FormatException or OverflowException)
        {
            var targetType = currentValue?.GetType().Name ?? "unknown";
            throw new FormatException(
                $"Cannot convert value \"{rawValue}\" to target type {targetType}: {ex.Message}", ex);
        }
    }

    /// <summary>
    ///     Parses a boolean from common string representations including numeric and word forms.
    ///     Accepts: <c>true</c>/<c>false</c>, <c>1</c>/<c>0</c>, <c>yes</c>/<c>no</c> (case-insensitive).
    /// </summary>
    private static bool ParseBool(string rawValue)
    {
        return rawValue.Trim().ToLowerInvariant() switch
        {
            "true" or "1" or "yes" => true,
            "false" or "0" or "no" => false,
            _ => throw new FormatException($"String '{rawValue}' was not recognized as a valid Boolean.")
        };
    }
}