//
//  MessageParameter.swift
//
//
//  Created by James Rochabrun on 11/16/23.
//

import Foundation

/// [Create a message.](https://platform.openai.com/docs/api-reference/messages/createMessage)
public struct MessageParameter: Encodable {
  public init(
    role: Role,
    content: Content,
    attachments: [MessageAttachment]? = nil,
    metadata: [String: String]? = nil)
  {
    self.role = role.rawValue
    self.content = content
    self.attachments = attachments
    self.metadata = metadata
  }

  public enum Role: String {
    case user
    case assistant
  }

  public enum DetailLevel: String, Encodable {
    case low
    case high
    case auto
  }

  /// Wrapper to handle both a string content or an array of content parts.
  public enum Content: Encodable {
    case stringContent(String)
    case arrayContent([ContentItem])

    public func encode(to encoder: Encoder) throws {
      var container = encoder.singleValueContainer()
      switch self {
      case .stringContent(let text):
        try container.encode(text)
      case .arrayContent(let contentArray):
        try container.encode(contentArray)
      }
    }
  }

  /// Enum to represent different content parts (text, image URL, image file).
  public enum ContentItem: Encodable {
    case text(String)
    case imageURL(ImageURL)
    case imageFile(ImageFile)

    public func encode(to encoder: Encoder) throws {
      var container = encoder.container(keyedBy: CodingKeys.self)
      switch self {
      case .text(let textContent):
        try container.encode("text", forKey: .type)
        try container.encode(textContent, forKey: .text)

      case .imageURL(let imageURLContent):
        try container.encode("image_url", forKey: .type)
        try container.encode(imageURLContent, forKey: .imageURL)

      case .imageFile(let imageFileContent):
        try container.encode("image_file", forKey: .type)
        try container.encode(imageFileContent, forKey: .imageFile)
      }
    }

    enum CodingKeys: String, CodingKey {
      case type
      case text
      case imageURL = "image_url"
      case imageFile = "image_file"
    }
  }

  /// References an image URL in the content of a message.
  public struct ImageURL: Encodable {
    /// The external URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.
    let url: String

    /// Specifies the detail level of the image. low uses fewer tokens, you can opt in to high resolution using high. Default value is auto
    let detail: DetailLevel?

    public init(
      url: String,
      detail: DetailLevel? = nil)
    {
      self.url = url
      self.detail = detail
    }

    enum CodingKeys: String, CodingKey {
      case url
      case detail
    }
  }

  /// References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
  public struct ImageFile: Encodable {
    /// The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content. Set purpose="vision" when uploading the File if you need to later display the file content.
    let fileId: String
    /// Specifies the detail level of the image if specified by the user. low uses fewer tokens, you can opt in to high resolution using high.
    let detail: DetailLevel?

    public init(
      fileId: String,
      detail: DetailLevel? = nil)
    {
      self.fileId = fileId
      self.detail = detail
    }

    enum CodingKeys: String, CodingKey {
      case fileId = "file_id"
      case detail
    }
  }

  /// The role of the entity that is creating the message. Allowed values include:
  /// user: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.
  /// assistant: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.
  let role: String
  /// The content of the message, which can be a string or an array of content parts (text, image URL, image file).
  let content: Content
  /// A list of files attached to the message, and the tools they should be added to.
  let attachments: [MessageAttachment]?
  /// Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long.
  let metadata: [String: String]?
}
