#
#    Copyright 2024 OpenAI
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

from dataclasses import dataclass
from typing import List, Optional

from qwak.llmops.generation.base import ModelResponse
from .chat_completion_message import ChatCompletionMessage
from .chat_completion_token_logprob import ChatCompletionTokenLogprob
from ..completion_usage import CompletionUsage

__all__ = ["ChatCompletion", "Choice", "ChoiceLogprobs"]


@dataclass
class ChoiceLogprobs:
    content: Optional[List[ChatCompletionTokenLogprob]] = None
    """A list of message content tokens with log probability information."""


@dataclass
class Choice:
    finish_reason: str
    """The reason the model stopped generating tokens.

    This will be `stop` if the model hit a natural stop point or a provided stop
    sequence, `length` if the maximum number of tokens specified in the request was
    reached, `content_filter` if content was omitted due to a flag from our content
    filters, `tool_calls` if the model called a tool, or `function_call`
    (deprecated) if the model called a function.
    """

    index: int
    """The index of the choice in the list of choices."""

    message: ChatCompletionMessage
    """A chat completion message generated by the model."""

    logprobs: Optional[ChoiceLogprobs] = None
    """Log probability information for the choice."""

    def __post_init__(self):
        """Validates that finish_reason is one of the allowed values."""
        allowed_reasons = {
            "stop",
            "length",
            "tool_calls",
            "content_filter",
            "function_call",
        }
        if self.finish_reason not in allowed_reasons:
            raise ValueError(
                f"Invalid finish_reason: '{self.finish_reason}'. "
                f"Must be one of {allowed_reasons}"
            )


@dataclass
class ChatCompletion(ModelResponse):
    id: str
    """A unique identifier for the chat completion."""

    choices: List[Choice]
    """A list of chat completion choices.

    Can be more than one if `n` is greater than 1.
    """

    created: int
    """The Unix timestamp (in seconds) of when the chat completion was created."""

    model: str
    """The model used for the chat completion."""

    object: str
    """The object type, which is always `chat.completion`."""

    system_fingerprint: Optional[str] = None
    """This fingerprint represents the backend configuration that the model runs with.

    Can be used in conjunction with the `seed` request parameter to understand when
    backend changes have been made that might impact determinism.
    """

    usage: Optional[CompletionUsage] = None
    """Usage statistics for the completion request."""

    def __post_init__(self):
        """Validates that the object type is correct."""
        if self.object != "chat.completion":
            raise ValueError(
                f"Invalid object type: '{self.object}'. Must be 'chat.completion'"
            )
