using OpenAI.Internal;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace OpenAI.Chat;

/// <summary> An incremental update corresponding to a streaming chat completion generated by the model. </summary>
[CodeGenType("CreateChatCompletionStreamResponse")]
public partial class StreamingChatCompletionUpdate
{
    private ChatMessageContent _contentUpdate;
    private IReadOnlyList<StreamingChatToolCallUpdate> _toolCallUpdates;
    private IReadOnlyList<ChatTokenLogProbabilityDetails> _contentTokenLogProbabilities;
    private IReadOnlyList<ChatTokenLogProbabilityDetails> _refusalTokenLogProbabilities;
    internal InternalCreateChatCompletionStreamResponseChoice InternalChoice => (Choices?.Count > 0) ? Choices[0] : null;
    internal InternalChatCompletionStreamResponseDelta InternalChoiceDelta => InternalChoice?.Delta;

    // CUSTOM:
    // - Made private. This property does not add value in the context of a strongly-typed class.
    // - Changed type from string.
    /// <summary> The object type, which is always `chat.completion.chunk`. </summary>
    [CodeGenMember("Object")]
    internal string Object { get; } = "chat.completion.chunk";

    // CUSTOM: Renamed for clarity.
    [CodeGenMember("Id")]
    public string CompletionId { get; }

    // CUSTOM: Made internal.
    [CodeGenMember("ServiceTier")]
    internal InternalServiceTier? ServiceTier { get; }

    // CUSTOM: Made internal.We only get back a single choice, and instead we flatten the structure for usability.
    [CodeGenMember("Choices")]
    internal IReadOnlyList<InternalCreateChatCompletionStreamResponseChoice> Choices { get; }

    // CUSTOM: Renamed.
    /// <summary> The timestamp when the chat completion was created. </summary>
    /// <remarks> Each update has the same timestamp. </remarks>
    [CodeGenMember("Created")]
    public DateTimeOffset CreatedAt { get; }

    // CUSTOM: Changed type from InternalCreateChatCompletionStreamResponseUsage.
    /// <summary> The token usage statistics for the entire chat completion. </summary>
    /// <remarks> Present only in the last streaming update. </remarks>
    [CodeGenMember("Usage")]
    public ChatTokenUsage Usage { get; }

    // CUSTOM: Flattened choice property.
    /// <summary>
    ///     The reason the model stopped generating tokens. This will be:
    ///     <list>
    ///         <item>
    ///             <see cref="ChatFinishReason.Stop"/> if the model hit a natural stop point or a provided stop sequence
    ///         </item>
    ///         <item>
    ///             <see cref="ChatFinishReason.Length"/> if the maximum number of tokens specified in the request was reached
    ///         </item>
    ///         <item>
    ///             <see cref="ChatFinishReason.ContentFilter"/> if content was omitted due to a flag from our content filters
    ///         </item>
    ///         <item>
    ///             <see cref="ChatFinishReason.ToolCalls"/> if the model called a tool
    ///         </item>
    ///         <item>
    ///             <see cref="ChatFinishReason.FunctionCall"/> (obsolte) if the model called a function
    ///         </item>
    ///     </list>
    /// </summary>
    public ChatFinishReason? FinishReason => InternalChoice?.FinishReason;

    // CUSTOM: Flattened choice logprobs property.
    /// <summary> The message content tokens with log probability information. </summary>
    public IReadOnlyList<ChatTokenLogProbabilityDetails> ContentTokenLogProbabilities =>
        _contentTokenLogProbabilities
            ??= InternalChoice?.Logprobs?.Content
                ?? new ChangeTrackingList<ChatTokenLogProbabilityDetails>();

    // CUSTOM: Flattened choice logprobs property.
    /// <summary> The message refusal tokens with log probability information. </summary>
    public IReadOnlyList<ChatTokenLogProbabilityDetails> RefusalTokenLogProbabilities =>
        _refusalTokenLogProbabilities
            ??= InternalChoice?.Logprobs?.Refusal
                ?? new ChangeTrackingList<ChatTokenLogProbabilityDetails>();

    // CUSTOM: Flattened choice delta property.
    /// <summary> The role of the author of this message. </summary>
    /// <remarks> Typically present in a single streaming update but applicable to the entire chat completion. </remarks>
    public ChatMessageRole? Role => InternalChoiceDelta?.Role;

    // CUSTOM: Flattened choice delta property.
    /// <summary> The contents of the message update. </summary>
    /// <remarks>
    ///     Each streaming update contains only a small portion of tokens. To reconstitute the entire chat completion,
    ///     all <see cref="ContentUpdate"/> values across streaming updates must be combined.
    /// </remarks>
    public ChatMessageContent ContentUpdate => _contentUpdate ??= InternalChoiceDelta?.Content ?? [];

    // CUSTOM: Flattened choice delta property.
    /// <summary> The tool calls generated by the model, such as function calls. </summary>
    public IReadOnlyList<StreamingChatToolCallUpdate> ToolCallUpdates
        => _toolCallUpdates
            ??= InternalChoiceDelta?.ToolCalls
                ?? new ChangeTrackingList<StreamingChatToolCallUpdate>();

    // CUSTOM: Flattened choice delta property.
    /// <summary> The refusal message generated by the model. </summary>
    public string RefusalUpdate => InternalChoiceDelta?.Refusal;

    // CUSTOM: Flattened choice delta property.
    [Obsolete($"This property is obsolete. Please use {nameof(ToolCallUpdates)} instead.")]
    public StreamingChatFunctionCallUpdate FunctionCallUpdate => InternalChoiceDelta?.FunctionCall;

    // CUSTOM:
    // - Added Experimental attribute.
    // - Flattened choice delta property.
    /// <summary>
    /// Incremental output audio generated by the model. Only expected when output audio has been requested via providing
    /// <see cref="ChatAudioOptions"/> to <see cref="ChatCompletionOptions.AudioOptions"/> and only available with
    /// supported models.
    /// </summary>
    [Experimental("OPENAI001")]
    public StreamingChatOutputAudioUpdate OutputAudioUpdate => InternalChoiceDelta?.Audio;
}
