using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

using AIRuntime.Runtime.Models;
using AIRuntime.Runtime.Providers.Models;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;


namespace AIRuntime.Runtime.Providers.OpenAI
{
    /// <summary>
    /// OpenAI implementation of the AIRuntime provider contracts.
    /// </summary>
    public class OpenAIProvider :
        IProviderWithRequestOptions, IProviderInputCapabilities
    {
        private const int MaxRejectedToolArgumentCharacters =
            2048;

        private const int MaxExpandedToolCallsPerProviderCall =
            16;

        private readonly IOpenAIClient _client;

        public string Model => _client.Model;

        // Opt in only after confirming support for this model at this endpoint.
        public bool SupportsImageInput { get; set; }

        private readonly bool _useMaxCompletionTokens;

        private readonly double _maximumTemperature;

        private readonly IReadOnlyList<string> _requestTags;


        public string Name
        {
            get;
        }


        public OpenAIProvider(
            IOpenAIClient client)
            : this(
                client,
                "OpenAI",
                useMaxCompletionTokens: true,
                maximumTemperature: 2,
                requestTags: null)
        {
        }


        protected OpenAIProvider(
            IOpenAIClient client,
            string providerName,
            bool useMaxCompletionTokens,
            double maximumTemperature,
            IReadOnlyList<string> requestTags = null)
        {
            if (client == null)
            {
                throw new ArgumentNullException(
                    nameof(client));
            }


            if (string.IsNullOrWhiteSpace(
                    client.Model))
            {
                throw new ArgumentException(
                    "OpenAI client model cannot be empty.",
                    nameof(client));
            }

            if (string.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentException(
                    "Provider name cannot be empty.",
                    nameof(providerName));
            }

            if (double.IsNaN(maximumTemperature) ||
                double.IsInfinity(maximumTemperature) ||
                maximumTemperature <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(maximumTemperature),
                    "Maximum temperature must be finite and greater than zero.");
            }


            _client =
                client;

            Name =
                providerName.Trim();

            _useMaxCompletionTokens =
                useMaxCompletionTokens;

            _maximumTemperature =
                maximumTemperature;

            _requestTags =
                CopyAndValidateRequestTags(
                    requestTags);
        }


        public Task<ChatCompletion> ChatAsync(
            IReadOnlyList<ConversationMessage> messages,
            IReadOnlyList<ProviderTool> tools,
            CancellationToken cancellationToken = default)
        {
            return ChatAsync(
                messages,
                tools,
                ProviderRequestOptions.Default,
                cancellationToken);
        }


        public async Task<ChatCompletion> ChatAsync(
            IReadOnlyList<ConversationMessage> messages,
            IReadOnlyList<ProviderTool> tools,
            ProviderRequestOptions options,
            CancellationToken cancellationToken = default)
        {
            using IDisposable trace = ProviderTransportDebug.EnsureAttempt(Name);
            if (messages == null)
            {
                throw new ArgumentNullException(
                    nameof(messages));
            }


            if (tools == null)
            {
                throw new ArgumentNullException(
                    nameof(tools));
            }


            if (options == null)
            {
                throw new ArgumentNullException(
                    nameof(options));
            }


            if (options.Temperature >
                _maximumTemperature)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(options),
                    $"{Name} temperature must be between 0 and {_maximumTemperature}.");
            }


            cancellationToken
                .ThrowIfCancellationRequested();


            ProviderInputGuard.Validate(this, messages);

            OpenAIChatRequest request =
                CreateRequest(
                    messages,
                    tools,
                    options,
                    cancellationToken);


            string requestJson =
                JsonConvert.SerializeObject(
                    request);


            string responseJson =
                await _client.ChatAsync(
                    requestJson,
                    cancellationToken);


            if (ProviderStreamingContext.IsActive)
            {
                responseJson =
                    ProviderStreamProtocol
                        .ConvertToCompletionJson(
                            responseJson, Name);
            }


            cancellationToken
                .ThrowIfCancellationRequested();


            if (string.IsNullOrWhiteSpace(responseJson))
            {
                throw new InvalidOperationException(
                    $"{Name} returned an empty response.");
            }


            ProviderTransportDebug.Publish(Name, "ADAPTER INPUT", string.Empty, responseJson,
                ProviderStreamingContext.IsActive);
            OpenAIChatResponse response;


            try
            {
                response =
                    JsonConvert.DeserializeObject<OpenAIChatResponse>(
                        responseJson);
            }
            catch (JsonException exception)
            {
                ProviderTransportDebug.WriteEvent(Name, "RESPONSE REJECTED",
                    new { Stage = "JSON parsing", Exception = exception.ToString() });
                throw new InvalidOperationException(
                    $"{Name} returned invalid JSON.",
                    exception);
            }


            cancellationToken
                .ThrowIfCancellationRequested();


            try
            {
                ChatCompletion completion = ConvertResponse(response, tools);
                ProviderTransportDebug.WriteEvent(Name, "ADAPTER RESULT", new {
                    completion.Content, completion.ToolCalls, completion.RejectedToolCalls,
                    completion.NormalizationMessages
                });
                return completion;
            }
            catch (Exception exception)
            {
                ProviderTransportDebug.WriteEvent(Name, "RESPONSE REJECTED",
                    new { Stage = "Completion conversion", Exception = exception.ToString() });
                throw;
            }
        }


        private OpenAIChatRequest CreateRequest(
            IReadOnlyList<ConversationMessage> messages,
            IReadOnlyList<ProviderTool> tools,
            ProviderRequestOptions options,
            CancellationToken cancellationToken)
        {
            OpenAIChatRequest request =
                new OpenAIChatRequest
                {
                    Model =
                        _client.Model,

                    Tags =
                        _requestTags.Count > 0
                            ? new List<string>(
                                _requestTags)
                            : null,

                    Temperature =
                        options.Temperature,

                    MaxCompletionTokens =
                        _useMaxCompletionTokens &&
                        options.MaxTokens > 0
                            ? options.MaxTokens
                            : null,

                    MaxTokens =
                        !_useMaxCompletionTokens &&
                        options.MaxTokens > 0
                            ? options.MaxTokens
                            : null,

                    ReasoningEffort =
                        tools.Count > 0 &&
                        UsesGpt56Family(
                            _client.Model)
                            ? "none"
                            : null,

                    Reasoning =
                        string.Equals(
                            Name,
                            "OpenRouter",
                            StringComparison.Ordinal) ||
                        string.Equals(
                            Name,
                            "Nous Portal",
                            StringComparison.Ordinal)
                            ? new OpenAIReasoningArguments
                            {
                                Effort = "none",
                                Exclude = true
                            }
                            : null,

                    Stream =
                        ProviderStreamingContext.IsActive
                };


            bool useNvidiaToolCalling =
                tools.Count > 0 &&
                string.Equals(
                    Name,
                    "NVIDIA Developer",
                    StringComparison.Ordinal);

            if (useNvidiaToolCalling)
            {
                request.ToolChoice =
                    "auto";

                request.ChatTemplateArguments =
                    new OpenAIChatTemplateArguments
                    {
                        EnableThinking =
                            false
                    };
            }


            foreach (ConversationMessage message in messages)
            {
                cancellationToken
                    .ThrowIfCancellationRequested();


                if (message == null)
                {
                    throw new ArgumentException(
                        "OpenAI messages cannot contain null entries.",
                        nameof(messages));
                }


                OpenAIMessage requestMessage =
                    new OpenAIMessage
                    {
                        Role =
                            message.Role,

                        Content =
                            BuildMessageContent(message),

                        ToolCallId =
                            message.ToolCallId
                    };


                if (message.ToolCalls != null &&
                    message.ToolCalls.Count > 0)
                {
                    requestMessage.ToolCalls =
                        new List<OpenAIToolCall>();


                    foreach (ToolCall toolCall in message.ToolCalls)
                    {
                        cancellationToken
                            .ThrowIfCancellationRequested();


                        if (toolCall == null)
                        {
                            throw new ArgumentException(
                                "OpenAI message tool calls cannot contain null entries.",
                                nameof(messages));
                        }


                        requestMessage.ToolCalls.Add(
                            new OpenAIToolCall
                            {
                                Id =
                                    toolCall.Id,

                                Function =
                                    new OpenAIFunctionCall
                                    {
                                        Name =
                                            toolCall.Name,

                                        Arguments =
                                            toolCall.Arguments
                                    }
                            });
                    }
                }


                request.Messages.Add(
                    requestMessage);
            }


            if (tools.Count > 0)
            {
                request.Tools =
                    new List<OpenAITool>();


                foreach (ProviderTool tool in tools)
                {
                    cancellationToken
                        .ThrowIfCancellationRequested();


                    if (tool?.Function == null)
                    {
                        throw new ArgumentException(
                            "OpenAI tools must contain a function definition.",
                            nameof(tools));
                    }


                    request.Tools.Add(
                        new OpenAITool
                        {
                            Function =
                                new OpenAIFunction
                                {
                                    Name =
                                        tool.Function.Name,

                                    Description =
                                        tool.Function.Description,

                                    Parameters =
                                        tool.Function.Parameters
                                }
                        });
                }
            }


            return request;
        }


        private static IReadOnlyList<string>
            CopyAndValidateRequestTags(
                IReadOnlyList<string> requestTags)
        {
            if (requestTags == null ||
                requestTags.Count == 0)
            {
                return Array.Empty<string>();
            }


            List<string> copy =
                new List<string>(
                    requestTags.Count);


            foreach (string requestTag in requestTags)
            {
                if (string.IsNullOrWhiteSpace(requestTag))
                {
                    throw new ArgumentException(
                        "OpenAI-compatible request tags cannot contain empty entries.",
                        nameof(requestTags));
                }


                copy.Add(
                    requestTag.Trim());
            }


            return copy;
        }


        private static bool UsesGpt56Family(
            string model)
        {
            return string.Equals(
                       model,
                       "gpt-5.6",
                       StringComparison.OrdinalIgnoreCase) ||
                   model.StartsWith(
                       "gpt-5.6-",
                       StringComparison.OrdinalIgnoreCase);
        }


        private ChatCompletion ConvertResponse(
            OpenAIChatResponse response,
            IReadOnlyList<ProviderTool> offeredTools)
        {
            if (response?.Choices == null ||
                response.Choices.Count == 0)
            {
                throw new InvalidOperationException(
                    $"{Name} response contained no choices.");
            }


            OpenAIMessage message =
                response.Choices[0]?.Message;


            if (message == null)
            {
                throw new InvalidOperationException(
                    $"{Name} response contained no assistant message.");
            }


            ChatCompletion completion =
                new ChatCompletion
                {
                    Content =
                        GetTextContent(message.Content)
                };


            if (message.ToolCalls == null ||
                message.ToolCalls.Count == 0)
            {
                ConvertContentInvocation(
                    message,
                    offeredTools,
                    completion);


                if (string.IsNullOrWhiteSpace(
                        completion.Content) &&
                    completion.ToolCalls.Count ==
                    0)
                {
                    throw new ProviderEmptyResponseException(
                        Name);
                }

                return completion;
            }


            List<string> rejectedToolCalls =
                new List<string>();

            InvalidOperationException firstToolCallError =
                null;

            for (int index = 0;
                 index < message.ToolCalls.Count;
                 index++)
            {
                OpenAIToolCall toolCall =
                    message.ToolCalls[index];


                IReadOnlyList<string> normalizedArgumentObjects;


                try
                {
                    normalizedArgumentObjects =
                        ValidateToolCall(
                        toolCall,
                        index);
                }
                catch (InvalidOperationException exception)
                {
                    firstToolCallError =
                        firstToolCallError ??
                        exception;

                    rejectedToolCalls.Add(
                        exception.Message);

                    completion.RejectedToolCalls.Add(
                        CreateRejectedToolCall(
                            toolCall,
                            index,
                            exception.Message));

                    continue;
                }


                for (int argumentIndex = 0;
                     argumentIndex < normalizedArgumentObjects.Count;
                     argumentIndex++)
                {
                    completion.ToolCalls.Add(
                        new ToolCall
                        {
                            Id =
                                normalizedArgumentObjects.Count == 1
                                    ? toolCall.Id
                                    : toolCall.Id +
                                      "__airuntime_part_" +
                                      (argumentIndex + 1),

                            Name =
                                toolCall.Function.Name,

                            Arguments =
                                normalizedArgumentObjects[argumentIndex]
                        });
                }


                if (normalizedArgumentObjects.Count > 1)
                {
                    completion.NormalizationMessages.Add(
                        $"AIRuntime expanded provider tool call '{toolCall.Id}' for '{toolCall.Function.Name}' from one concatenated argument payload into {normalizedArgumentObjects.Count} ordered calls. Each object remains subject to normal capability permissions, validation, batching, and duplicate-call safeguards.");
                }
            }


            if (completion.ToolCalls.Count ==
                0)
            {
                if (completion.RejectedToolCalls.Count > 0)
                {
                    return completion;
                }


                throw firstToolCallError ??
                      new InvalidOperationException(
                          $"{Name} returned no valid tool calls.");
            }


            if (rejectedToolCalls.Count >
                0)
            {
                string warning =
                    "AIRuntime rejected malformed provider tool calls while preserving the valid calls from the same response: " +
                    string.Join(
                        " ",
                        rejectedToolCalls);

                completion.Content =
                    string.IsNullOrWhiteSpace(
                        completion.Content)
                        ? warning
                        : completion.Content.TrimEnd() +
                          "\n\n" +
                          warning;
            }


            return completion;
        }


        private static RejectedToolCall CreateRejectedToolCall(
            OpenAIToolCall toolCall,
            int index,
            string reason)
        {
            return new RejectedToolCall
            {
                Index =
                    index,

                Id =
                    toolCall?.Id ??
                    string.Empty,

                Name =
                    toolCall?.Function?.Name ??
                    string.Empty,

                Arguments =
                    BoundRejectedToolArguments(
                        toolCall?.Function?.Arguments),

                Reason =
                    reason ??
                    "The provider tool call was malformed."
            };
        }


        private static string BoundRejectedToolArguments(
            string arguments)
        {
            if (string.IsNullOrEmpty(arguments))
                return string.Empty;


            if (arguments.Length <=
                MaxRejectedToolArgumentCharacters)
            {
                return arguments;
            }


            return arguments.Substring(
                       0,
                       MaxRejectedToolArgumentCharacters) +
                   $"...[truncated; original characters={arguments.Length}]";
        }


        private void ConvertContentInvocation(
            OpenAIMessage message,
            IReadOnlyList<ProviderTool> offeredTools,
            ChatCompletion completion)
        {
            string messageContent = GetTextContent(message.Content);
            if (string.IsNullOrWhiteSpace(messageContent))
            {
                return;
            }

            JObject content;

            try
            {
                content =
                    JObject.Parse(
                        messageContent);
            }
            catch (JsonException)
            {
                ConvertTaggedContentInvocations(
                    messageContent,
                    offeredTools,
                    completion);

                return;
            }

            JToken invocation =
                content["invocation"];

            if (invocation == null)
                return;

            if (invocation.Type != JTokenType.Object)
            {
                throw new InvalidOperationException(
                    $"{Name} returned an invalid response: content invocation was not a JSON object.");
            }

            string toolName =
                invocation.Value<string>(
                    "name");

            JToken arguments =
                invocation["arguments"];

            if (string.IsNullOrWhiteSpace(toolName))
            {
                throw new InvalidOperationException(
                    $"{Name} returned an invalid response: content invocation name was missing.");
            }

            bool toolWasOffered =
                false;

            foreach (ProviderTool tool in offeredTools ?? Array.Empty<ProviderTool>())
            {
                if (tool?.Function != null &&
                    string.Equals(
                        tool.Function.Name,
                        toolName,
                        StringComparison.Ordinal))
                {
                    toolWasOffered =
                        true;

                    break;
                }
            }

            if (!toolWasOffered)
            {
                throw new InvalidOperationException(
                    $"{Name} returned an invalid response: content invocation requested unavailable tool '{toolName}'.");
            }

            IReadOnlyList<string> normalizedArguments;

            if (arguments == null ||
                !TryNormalizeToolArguments(
                    arguments.Type == JTokenType.String
                        ? arguments.Value<string>()
                        : arguments.ToString(
                            Formatting.None),
                    out normalizedArguments))
            {
                throw new InvalidOperationException(
                    $"{Name} returned an invalid response: content invocation arguments were not a JSON object.");
            }

            completion.Content =
                string.Empty;

            for (int index = 0;
                 index < normalizedArguments.Count;
                 index++)
            {
                completion.ToolCalls.Add(
                    new ToolCall
                    {
                        Id =
                            "call_content_invocation_" +
                            index,

                        Name =
                            toolName,

                        Arguments =
                            normalizedArguments[index]
                    });
            }


            if (normalizedArguments.Count > 1)
            {
                completion.NormalizationMessages.Add(
                    $"AIRuntime expanded textual provider invocation for '{toolName}' into {normalizedArguments.Count} ordered calls. Each object remains subject to normal capability permissions, validation, batching, and duplicate-call safeguards.");
            }
        }


        private static object BuildMessageContent(ConversationMessage message)
        {
            if (message?.Attachments == null || message.Attachments.Count == 0)
                return message?.Content;

            List<OpenAIContentPart> parts = new List<OpenAIContentPart>();
            if (!string.IsNullOrWhiteSpace(message.Content))
                parts.Add(new OpenAIContentPart { Type = "text", Text = message.Content });
            foreach (ConversationAttachment attachment in message.Attachments)
            {
                if (attachment == null || string.IsNullOrWhiteSpace(attachment.Path) ||
                    string.IsNullOrWhiteSpace(attachment.MediaType) ||
                    !attachment.MediaType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                    continue;
                string fullPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory,
                    attachment.Path.Replace('/', Path.DirectorySeparatorChar)));
                if (!File.Exists(fullPath)) continue;
                FileInfo info = new FileInfo(fullPath);
                if (info.Length <= 0 || info.Length > 20L * 1024L * 1024L) continue;
                string dataUrl = "data:" + attachment.MediaType + ";base64," +
                    Convert.ToBase64String(File.ReadAllBytes(fullPath));
                parts.Add(new OpenAIContentPart
                {
                    Type = "image_url",
                    ImageUrl = new OpenAIImageUrl { Url = dataUrl }
                });
            }
            return parts.Count == 0 ? (object)(message.Content ?? string.Empty) : parts;
        }


        private static string GetTextContent(object content)
        {
            if (content == null) return string.Empty;
            if (content is string text) return text;
            return content is JToken token ? token.ToString(Formatting.None) : content.ToString();
        }


        private void ConvertTaggedContentInvocations(
            string content,
            IReadOnlyList<ProviderTool> offeredTools,
            ChatCompletion completion)
        {
            if (string.IsNullOrWhiteSpace(content) ||
                content.IndexOf(
                    "<tool_call:",
                    StringComparison.OrdinalIgnoreCase) <
                0)
            {
                return;
            }

            if (offeredTools == null ||
                offeredTools.Count ==
                0)
            {
                throw new InvalidOperationException(
                    $"{Name} returned tagged tool calls when no tools were offered.");
            }


            MatchCollection starts =
                Regex.Matches(
                    content,
                    @"<tool_call:(?<id>[^>]+)>",
                    RegexOptions.IgnoreCase |
                    RegexOptions.CultureInvariant);

            int convertedCount =
                0;

            foreach (Match start in starts.Cast<Match>())
            {
                string providerId =
                    start.Groups["id"].Value;

                string endMarker =
                    $"</tool_call:{providerId}>";

                int endIndex =
                    content.IndexOf(
                        endMarker,
                        start.Index + start.Length,
                        StringComparison.OrdinalIgnoreCase);

                if (endIndex <
                    0)
                {
                    throw new InvalidOperationException(
                        $"{Name} returned a malformed tagged tool call without closing marker '{endMarker}'.");
                }


                string body =
                    content.Substring(
                        start.Index + start.Length,
                        endIndex - start.Index - start.Length)
                        .TrimStart();

                ProviderTool offeredTool =
                    offeredTools.FirstOrDefault(
                        tool =>
                            tool?.Function != null &&
                            !string.IsNullOrWhiteSpace(
                                tool.Function.Name) &&
                            body.StartsWith(
                                tool.Function.Name,
                                StringComparison.Ordinal));

                if (offeredTool ==
                    null)
                {
                    throw new InvalidOperationException(
                        $"{Name} returned a tagged invocation for an unavailable or unidentifiable tool.");
                }


                JObject arguments =
                    new JObject();

                string escapedId =
                    Regex.Escape(
                        providerId);

                MatchCollection argumentMatches =
                    Regex.Matches(
                        body,
                        $@"<arg_key:{escapedId}>(?<key>.*?)</arg_key:{escapedId}>\s*<arg_value:{escapedId}>(?<value>.*?)</arg_value:{escapedId}>",
                        RegexOptions.IgnoreCase |
                        RegexOptions.Singleline |
                        RegexOptions.CultureInvariant);

                foreach (Match argument in argumentMatches.Cast<Match>())
                {
                    string key =
                        argument.Groups["key"].Value.Trim();

                    if (string.IsNullOrWhiteSpace(key) ||
                        arguments.Property(
                            key,
                            StringComparison.OrdinalIgnoreCase) !=
                        null)
                    {
                        throw new InvalidOperationException(
                            $"{Name} returned a malformed tagged invocation with an empty or duplicate argument name.");
                    }

                    arguments[key] =
                        argument.Groups["value"].Value.Trim();
                }

                if (arguments.Count ==
                    0)
                {
                    throw new InvalidOperationException(
                        $"{Name} returned a tagged invocation for '{offeredTool.Function.Name}' without parseable arguments.");
                }


                completion.ToolCalls.Add(
                    new ToolCall
                    {
                        Id =
                            $"call_tagged_{convertedCount}_{providerId}",

                        Name =
                            offeredTool.Function.Name,

                        Arguments =
                            arguments.ToString(
                                Formatting.None)
                    });

                convertedCount++;
            }

            if (convertedCount ==
                0)
            {
                throw new InvalidOperationException(
                    $"{Name} returned textual tool markup that could not be converted safely.");
            }

            completion.Content =
                string.Empty;
        }


        private IReadOnlyList<string> ValidateToolCall(
            OpenAIToolCall toolCall,
            int index)
        {
            if (toolCall == null)
            {
                throw InvalidToolCall(
                    index,
                    "the entry was null");
            }


            if (toolCall.Function == null)
            {
                throw InvalidToolCall(
                    index,
                    "the function definition was missing");
            }


            if (string.IsNullOrWhiteSpace(toolCall.Id))
            {
                throw InvalidToolCall(
                    index,
                    "the call ID was missing");
            }


            if (string.IsNullOrWhiteSpace(
                    toolCall.Function.Name))
            {
                throw InvalidToolCall(
                    index,
                    "the function name was missing");
            }


            if (string.IsNullOrWhiteSpace(
                    toolCall.Function.Arguments))
            {
                throw InvalidToolCall(
                    index,
                    "the function arguments were missing");
            }


            IReadOnlyList<string> normalizedArguments;

            if (!TryNormalizeToolArguments(
                    toolCall.Function.Arguments,
                    out normalizedArguments))
            {
                throw InvalidToolCall(
                    index,
                    "the function arguments were not a JSON object");
            }

            return normalizedArguments;
        }


        private static bool TryNormalizeToolArguments(
            string arguments,
            out IReadOnlyList<string> normalizedArguments)
        {
            normalizedArguments =
                null;

            if (string.IsNullOrWhiteSpace(
                    arguments))
            {
                return false;
            }

            if (!TryReadJsonValues(
                    arguments,
                    out List<JToken> values))
            {
                return false;
            }


            // Some OpenAI-compatible providers, including Nous models,
            // occasionally JSON-encode the arguments payload twice. One
            // deterministic unwrap is safe; deeper encodings remain invalid.
            if (values.Count == 1 &&
                values[0].Type == JTokenType.String)
            {
                if (!TryReadJsonValues(
                        values[0].Value<string>(),
                        out values) ||
                    (values.Count == 1 &&
                     values[0].Type == JTokenType.String))
                {
                    return false;
                }
            }


            // Some models batch repeated invocations of one function either
            // as an object array or as adjacent root objects. Both encodings
            // are unambiguous because the provider supplied one function name.
            if (values.Count == 1 &&
                values[0].Type == JTokenType.Array)
            {
                values =
                    ((JArray)values[0])
                    .Children()
                    .ToList();
            }


            if (values.Count == 0 ||
                values.Count > MaxExpandedToolCallsPerProviderCall ||
                values.Any(value =>
                    value == null ||
                    value.Type != JTokenType.Object))
            {
                return false;
            }


            normalizedArguments =
                values
                    .Select(value =>
                        value.ToString(
                            Formatting.None))
                    .ToList();

            return true;
        }


        private static bool TryReadJsonValues(
            string json,
            out List<JToken> values)
        {
            values =
                new List<JToken>();


            if (string.IsNullOrWhiteSpace(json))
                return false;


            try
            {
                using (StringReader textReader =
                       new StringReader(json))
                using (JsonTextReader jsonReader =
                       new JsonTextReader(textReader)
                       {
                           SupportMultipleContent = true,
                           DateParseHandling = DateParseHandling.None
                       })
                {
                    while (jsonReader.Read())
                    {
                        values.Add(
                            JToken.ReadFrom(
                                jsonReader));

                        if (values.Count >
                            MaxExpandedToolCallsPerProviderCall)
                        {
                            return false;
                        }
                    }
                }
            }
            catch (JsonException)
            {
                return false;
            }


            return values.Count > 0;
        }


        private InvalidOperationException InvalidToolCall(
            int index,
            string reason)
        {
            return new InvalidOperationException(
                $"{Name} returned an invalid response: tool call at index {index} was malformed because {reason}.");
        }
    }
}
