defmodule AI.Tools.File.Notes do
  @behaviour AI.Tools

  @impl AI.Tools
  def async?, do: true

  @impl AI.Tools
  def is_available?, do: AI.Tools.has_indexed_project()

  @impl AI.Tools
  def ui_note_on_request(_args), do: nil

  @impl AI.Tools
  def ui_note_on_result(_args, _result), do: nil

  @impl AI.Tools
  def read_args(%{"file" => file}), do: {:ok, %{"file" => file}}
  def read_args(%{"file_path" => file}), do: {:ok, %{"file" => file}}
  def read_args(_args), do: AI.Tools.required_arg_error("file")

  @impl AI.Tools
  def spec() do
    %{
      type: "function",
      function: %{
        name: "file_notes_tool",
        description: """
        Returns a summary and outline of the specified file. This is
        information that is automatically generated by `fnord` while indexing
        files for semantic search. This is often MUCH more useful than the raw
        file contents, especially if the file may be large and you need only a
        high-level understanding of the file's purpose, behaviour, and linkage
        to other files.

        Use this before using the file_contents_tool to avoid pulling in
        unnecessary content into your context window.
        """,
        strict: true,
        parameters: %{
          additionalProperties: false,
          type: "object",
          required: ["file"],
          properties: %{
            file: %{
              type: "string",
              description: """
              The absolute file path to the code file in the project. This
              parameter must be an *indexed file* in the repository, as
              returned by the file_list_tool or the file_search_tool.
              """
            }
          }
        }
      }
    }
  end

  @impl AI.Tools
  def call(args) do
    with {:ok, file} <- Map.fetch(args, "file"),
         {:ok, project} <- Store.get_project() do
      with {:ok, entry} <- AI.Tools.get_entry(file),
           {:ok, summary} <- Store.Project.Entry.read_summary(entry),
           {:ok, outline} <- Store.Project.Entry.read_outline(entry) do
        {:ok, format_notes(summary, outline)}
      else
        {:error, :project_not_found} ->
          {:error, "This project has not yet been indexed by the user."}

        {:error, :enoent} ->
          case Util.find_file_within_root(file, project.source_root) do
            {:ok, _} ->
              {:error,
               """
               The file you requested exists but has not yet been indexed by fnord.
               As a result, there are no notes available for this file.
               Use the `file_reindex_tool` to index this file if you would like to generate notes for it.
               Alternatively, you can use the `file_info_tool` to retrieve specialized information about this file directly.
               """}

            _ ->
              {:error,
               """
               File path not found.
               Please verify the correct path.
               """}
          end

        {:error, reason} ->
          {:error, "Unable to load notes: #{inspect(reason)}"}
      end
    else
      :error ->
        {:error, "Missing required parameter: file."}
    end
  end

  # Formats summary and outline
  defp format_notes(summary, outline) do
    """
    # Summary
    #{summary}

    # Outline
    #{outline}
    """
  end
end
