using System.Text.Json;

namespace ScadaLink.InboundAPI;

/// <summary>
/// WP-2: Validates and deserializes JSON request body against method parameter definitions.
/// Extended type system: Boolean, Integer, Float, String, Object, List.
/// </summary>
public static class ParameterValidator
{
    /// <summary>
    /// Validates the request body against the method's parameter definitions.
    /// Returns deserialized parameters or an error message.
    /// </summary>
    public static ParameterValidationResult Validate(
        JsonElement? body,
        string? parameterDefinitions)
    {
        if (string.IsNullOrEmpty(parameterDefinitions))
        {
            // No parameters defined — body should be empty or null
            return ParameterValidationResult.Valid(new Dictionary<string, object?>());
        }

        List<ParameterDefinition> definitions;
        try
        {
            definitions = JsonSerializer.Deserialize<List<ParameterDefinition>>(
                parameterDefinitions,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
                ?? [];
        }
        catch (JsonException)
        {
            return ParameterValidationResult.Invalid("Invalid parameter definitions in method configuration");
        }

        if (definitions.Count == 0)
        {
            return ParameterValidationResult.Valid(new Dictionary<string, object?>());
        }

        if (body == null || body.Value.ValueKind == JsonValueKind.Null || body.Value.ValueKind == JsonValueKind.Undefined)
        {
            // Check if all parameters are optional
            var required = definitions.Where(d => d.Required).ToList();
            if (required.Count > 0)
            {
                return ParameterValidationResult.Invalid(
                    $"Missing required parameters: {string.Join(", ", required.Select(r => r.Name))}");
            }

            return ParameterValidationResult.Valid(new Dictionary<string, object?>());
        }

        if (body.Value.ValueKind != JsonValueKind.Object)
        {
            return ParameterValidationResult.Invalid("Request body must be a JSON object");
        }

        var result = new Dictionary<string, object?>();
        var errors = new List<string>();

        foreach (var def in definitions)
        {
            if (body.Value.TryGetProperty(def.Name, out var prop))
            {
                var (value, error) = CoerceValue(prop, def.Type, def.Name);
                if (error != null)
                {
                    errors.Add(error);
                }
                else
                {
                    result[def.Name] = value;
                }
            }
            else if (def.Required)
            {
                errors.Add($"Missing required parameter: {def.Name}");
            }
        }

        if (errors.Count > 0)
        {
            return ParameterValidationResult.Invalid(string.Join("; ", errors));
        }

        return ParameterValidationResult.Valid(result);
    }

    private static (object? value, string? error) CoerceValue(JsonElement element, string expectedType, string paramName)
    {
        return expectedType.ToLowerInvariant() switch
        {
            "boolean" => element.ValueKind == JsonValueKind.True || element.ValueKind == JsonValueKind.False
                ? (element.GetBoolean(), null)
                : (null, $"Parameter '{paramName}' must be a Boolean"),

            "integer" => element.ValueKind == JsonValueKind.Number && element.TryGetInt64(out var intVal)
                ? (intVal, null)
                : (null, $"Parameter '{paramName}' must be an Integer"),

            "float" => element.ValueKind == JsonValueKind.Number
                ? (element.GetDouble(), null)
                : (null, $"Parameter '{paramName}' must be a Float"),

            "string" => element.ValueKind == JsonValueKind.String
                ? (element.GetString(), null)
                : (null, $"Parameter '{paramName}' must be a String"),

            "object" => element.ValueKind == JsonValueKind.Object
                ? (JsonSerializer.Deserialize<Dictionary<string, object?>>(element.GetRawText()), null)
                : (null, $"Parameter '{paramName}' must be an Object"),

            "list" => element.ValueKind == JsonValueKind.Array
                ? (JsonSerializer.Deserialize<List<object?>>(element.GetRawText()), null)
                : (null, $"Parameter '{paramName}' must be a List"),

            _ => (null, $"Unknown parameter type '{expectedType}' for parameter '{paramName}'")
        };
    }
}

/// <summary>
/// Defines a parameter in a method's parameter definitions.
/// </summary>
public class ParameterDefinition
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = "String";
    public bool Required { get; set; } = true;
}

/// <summary>
/// Result of parameter validation.
/// </summary>
public class ParameterValidationResult
{
    public bool IsValid { get; private init; }
    public string? ErrorMessage { get; private init; }
    public IReadOnlyDictionary<string, object?> Parameters { get; private init; } = new Dictionary<string, object?>();

    public static ParameterValidationResult Valid(Dictionary<string, object?> parameters) =>
        new() { IsValid = true, Parameters = parameters };

    public static ParameterValidationResult Invalid(string message) =>
        new() { IsValid = false, ErrorMessage = message };
}
