defmodule Jido.Examples.InstructorDemo do
  @moduledoc """
  A demo module showcasing how to use Jido.AI with Instructor for structured prompting.
  """
  alias Jido.AI.Prompt
  alias Jido.AI.Actions.Instructor.{ChatCompletion, ChoiceResponse, BooleanResponse}
  alias Jido.AI.Model

  defmodule ResponseSchema do
    use Ecto.Schema
    use Instructor

    @llm_doc """
    A structured response from an AI assistant that includes:
    - response: The main text response
    - code_example: An optional code example
    - references: List of relevant references or links
    """
    @primary_key false
    embedded_schema do
      field(:response, :string)
      field(:code_example, :string)
      field(:references, {:array, :string})
    end
  end

  def model do
    {:ok, model} = Model.from({:anthropic, [model: "claude-3-haiku-20240307"]})
    model
  end

  def chat do
    # 1. Define a model using provider tuple format with model

    # 2. Create a sophisticated prompt with history and template substitution
    prompt =
      Prompt.new(%{
        messages: [
          %{
            role: :system,
            content:
              "You are a helpful AI assistant that provides clear, concise responses with code examples when relevant.",
            engine: :none
          },
          %{
            role: :user,
            content: "Explain the concept of pure functions in Elixir with an example.",
            engine: :none
          }
        ]
      })

    # 3. Make the chat completion call
    case Jido.Exec.run(ChatCompletion, %{
           model: model(),
           prompt: prompt,
           response_model: ResponseSchema,
           temperature: 0.7,
           max_tokens: 1000
         }) do
      {:ok, %{result: %ResponseSchema{} = response}, _} ->
        IO.puts("\n=== Response ===")
        IO.puts(response.response)
        IO.puts("\n=== Code Example ===")
        IO.puts(response.code_example)
        IO.puts("\n=== References ===")
        Enum.each(response.references, &IO.puts/1)

      {:error, reason, _} ->
        IO.puts("\n=== Error ===")
        IO.puts(inspect(reason))

      unknown ->
        IO.puts("\n=== Unknown ===")
        IO.puts(inspect(unknown))
    end
  end

  @doc """
  Demonstrates using the ChatResponse action for simpler chat interactions
  """
  def choice do
    # Define available options for the multiple choice question
    available_options = [
      %{
        id: "with_statement",
        name: "With Statement",
        description: "Use a with statement for handling multiple operations that can fail"
      },
      %{
        id: "try_rescue",
        name: "Try/Rescue",
        description: "Use try/rescue blocks for handling exceptions"
      },
      %{
        id: "ok_error_tuples",
        name: "OK/Error Tuples",
        description: "Return tagged tuples like {:ok, result} or {:error, reason}"
      }
    ]

    # Create prompt for multiple choice question
    prompt =
      Prompt.new(%{
        messages: [
          %{
            role: :system,
            content:
              "You are a helpful AI assistant that helps users learn about Elixir programming.",
            engine: :none
          },
          %{
            role: :user,
            content:
              "What's the best way to handle errors when performing multiple operations that can fail in Elixir?",
            engine: :none
          }
        ]
      })

    # Make the choice response call
    case Jido.Exec.run(ChoiceResponse, %{
           prompt: prompt,
           available_actions: available_options,
           model: model()
         }) do
      {:ok, %{result: %{selected_option: option, explanation: explanation}}} ->
        selected = Enum.find(available_options, &(&1.id == option))
        IO.puts("\n=== Selected Option ===")
        IO.puts("#{selected.name} (#{selected.id})")
        IO.puts("\n=== Explanation ===")
        IO.puts(explanation)

      {:error, reason} ->
        IO.puts("\n=== Error ===")
        IO.puts(inspect(reason))
        IO.puts("\nAvailable options:")

        Enum.each(available_options, fn opt ->
          IO.puts("- #{opt.id}: #{opt.name} (#{opt.description})")
        end)
    end
  end

  def boolean do
    prompt =
      Prompt.new(%{
        messages: [
          %{role: :user, content: "Is Elixir a functional programming language?", engine: :none}
        ]
      })

    case Jido.Exec.run(BooleanResponse, %{prompt: prompt, model: model()}) do
      {:ok,
       %{
         result: response,
         explanation: explanation,
         confidence: confidence,
         is_ambiguous: is_ambiguous
       }} ->
        IO.puts("\n=== Boolean Response ===")
        IO.puts(response)
        IO.puts("\n=== Explanation ===")
        IO.puts(explanation)
        IO.puts("\n=== Confidence ===")
        IO.puts(confidence)
        IO.puts("\n=== Is Ambiguous ===")
        IO.puts(is_ambiguous)
    end
  end
end
