defmodule Mix.Tasks.AshTypescript.Codegen do
  @moduledoc """
  Generates TypeScript types for Ash Rpc-calls.

  Usage:
    mix ash_typescript.codegen --output "assets/js/ash_generated.ts"
  """

  @shortdoc "Generates TypeScript types for Ash Rpc-calls"

  use Mix.Task
  import AshTypescript.Rpc.Codegen

  def run(args) do
    Mix.Task.run("compile")

    {opts, _remaining, _invalid} =
      OptionParser.parse(args,
        switches: [
          output: :string,
          check: :boolean,
          dry_run: :boolean,
          run_endpoint: :string,
          validate_endpoint: :string
        ],
        aliases: [o: :string, r: :run_endpoint, v: :validate_endpoint]
      )

    otp_app = Mix.Project.config()[:app]

    output_file =
      Keyword.get(opts, :output) || Application.get_env(:ash_typescript, :output_file) ||
        "assets/js/ash_rpc.ts"

    run_endpoint =
      Keyword.get(opts, :run_endpoint) || Application.get_env(:ash_typescript, :run_endpoint) ||
        "/rpc/run"

    validate_endpoint =
      Keyword.get(opts, :run_endpoint) || Application.get_env(:ash_typescript, :run_endpoint) ||
        "/rpc/validate"

    codegen_opts = [
      run_endpoint: run_endpoint,
      validate_endpoint: validate_endpoint
    ]

    # Generate TypeScript types and write to file
    typescript_content = generate_typescript_types(otp_app, codegen_opts)

    current_content =
      if File.exists?(output_file) do
        File.read!(output_file)
      else
        ""
      end

    cond do
      opts[:check] ->
        if typescript_content != current_content do
          raise Ash.Error.Framework.PendingCodegen,
            diff: %{
              output_file => typescript_content
            }
        end

      opts[:dry_run] ->
        if typescript_content != current_content do
          "##{output_file}:\n\n#{typescript_content}"
        end

      true ->
        File.write!(output_file, typescript_content)
    end
  end
end
