class Tapioca::Cli

def __print_version

def __print_version
  puts "Tapioca v#{Tapioca::VERSION}"
end

def addon_mode!

def addon_mode!
  @addon_mode = true
end

def annotations

def annotations
  if !options[:netrc] && options[:netrc_file]
    raise Thor::Error, set_color("Options `--no-netrc` and `--netrc-file` can't be used together", :bold, :red)
  end
  command = Commands::Annotations.new(
    central_repo_root_uris: options[:sources],
    auth: options[:auth],
    netrc_file: netrc_file(options),
    typed_overrides: options[:typed_overrides],
  )
  command.run
end

def check_shims

def check_shims
  command = Commands::CheckShims.new(
    gem_rbi_dir: options[:gem_rbi_dir],
    dsl_rbi_dir: options[:dsl_rbi_dir],
    shim_rbi_dir: options[:shim_rbi_dir],
    annotations_rbi_dir: options[:annotations_rbi_dir],
    todo_rbi_file: options[:todo_rbi_file],
    payload: options[:payload],
    number_of_workers: options[:workers],
  )
  command.run
end

def configure

def configure
  command = Commands::Configure.new(
    sorbet_config: SORBET_CONFIG_FILE,
    tapioca_config: options[:config],
    default_postrequire: options[:postrequire],
  )
  command.run
end

def dsl(*constant_or_paths)

def dsl(*constant_or_paths)
  set_environment(options)
  # Assume anything starting with a capital letter or colon is a class, otherwise a path
  constants, paths = constant_or_paths.partition { |c| c =~ /\A[A-Z:]/ }
  command_args = {
    requested_constants: constants,
    requested_paths: paths.map { |p| Pathname.new(p) },
    outpath: Pathname.new(options[:outdir]),
    only: options[:only],
    exclude: options[:exclude],
    file_header: options[:file_header],
    tapioca_path: TAPIOCA_DIR,
    skip_constant: options[:skip_constant],
    quiet: options[:quiet],
    verbose: options[:verbose],
    number_of_workers: options[:workers],
    rbi_formatter: rbi_formatter(options),
    app_root: options[:app_root],
    halt_upon_load_error: options[:halt_upon_load_error],
    compiler_options: options[:compiler_options],
    lsp_addon: self.class.addon_mode,
  }
  command = if options[:verify]
    Commands::DslVerify.new(**command_args)
  elsif options[:list_compilers]
    Commands::DslCompilerList.new(**command_args)
  else
    Commands::DslGenerate.new(**command_args)
  end
  command.run
end

def exit_on_failure?

def exit_on_failure?
  !@addon_mode
end

def gem(*gems)

def gem(*gems)
  set_environment(options)
  all = options[:all]
  verify = options[:verify]
  include_dependencies = options[:include_dependencies]
  raise MalformattedArgumentError, "Options '--all' and '--verify' are mutually exclusive" if all && verify
  if gems.empty?
    raise MalformattedArgumentError,
      "Option '--include-dependencies' must be provided with gems" if include_dependencies
  else
    raise MalformattedArgumentError, "Option '--all' must be provided without any other arguments" if all
    raise MalformattedArgumentError, "Option '--verify' must be provided without any other arguments" if verify
  end
  command_args = {
    gem_names: all ? [] : gems,
    exclude: options[:exclude],
    include_dependencies: options[:include_dependencies],
    prerequire: options[:prerequire],
    postrequire: options[:postrequire],
    typed_overrides: options[:typed_overrides],
    outpath: Pathname.new(options[:outdir]),
    file_header: options[:file_header],
    include_doc: options[:doc],
    include_loc: options[:loc],
    include_exported_rbis: options[:exported_gem_rbis],
    number_of_workers: options[:workers],
    auto_strictness: options[:auto_strictness],
    dsl_dir: options[:dsl_dir],
    rbi_formatter: rbi_formatter(options),
    halt_upon_load_error: options[:halt_upon_load_error],
    lsp_addon: options[:lsp_addon],
  }
  command = if verify
    Commands::GemVerify.new(**command_args)
  elsif !gems.empty? || all
    Commands::GemGenerate.new(**command_args)
  else
    Commands::GemSync.new(**command_args)
  end
  command.run
end

def init

def init
  # We need to make sure that trackers stay enabled until the `gem` command is invoked
  Runtime::Trackers.with_trackers_enabled do
    invoke(:configure)
    invoke(:annotations)
    invoke(:gem)
  end
  # call the command directly to skip deprecation warning
  Commands::Todo.new(
    todo_file: DEFAULT_TODO_FILE,
    file_header: true,
  ).run
  print_init_next_steps
end

def print_init_next_steps

def print_init_next_steps
  say(<<~OUTPUT)
    #{set_color("This project is now set up for use with Sorbet and Tapioca", :bold)}
    The sorbet/ folder should exist and look something like this:
    ├── config             # Default options to be passed to Sorbet on every run
    └── rbi/
      ├── annotations/     # Type definitions pulled from the rbi-central repository
      ├── gems/            # Autogenerated type definitions for your gems
      └── todo.rbi         # Constants which were still missing after RBI generation
    └── tapioca/
      ├── config.yml       # Default options to be passed to Tapioca
      └── require.rb       # A file where you can make requires from gems that might be needed for gem RBI generation
    Please check this folder into version control.
    #{set_color("🤔 What's next", :bold)}
    1. Many Ruby applications use metaprogramming DSLs to dynamically generate constants and methods.
      To generate type definitions for any DSLs in your application, run:
      #{set_color("bin/tapioca dsl", :cyan)}
    2. Check whether the constants in the #{set_color("sorbet/rbi/todo.rbi", :cyan)} file actually exist in your project.
      It is possible that some of these constants are typos, and leaving them in #{set_color("todo.rbi", :cyan)} will
      hide errors in your application. Ideally, you should be able to remove all definitions
      from this file and delete it.
    3. Typecheck your project:
      #{set_color("bundle exec srb tc", :cyan)}
      There should not be any typechecking errors.
    4. Upgrade a file marked "#{set_color("# typed: false", :cyan)}" to "#{set_color("# typed: true", :cyan)}".
      Then, run: #{set_color("bundle exec srb tc", :cyan)} and try to fix any errors.
      You can use Spoom to bump files for you:
      #{set_color("spoom bump --from false --to true", :cyan)}
      To learn more about Spoom, visit: #{set_color("https://github.com/Shopify/spoom", :cyan)}
    5. Add signatures to your methods with #{set_color("sig", :cyan)}. To learn how, read: #{set_color("https://sorbet.org/docs/sigs", :cyan)}
    #{set_color("Documentation", :bold)}
    We recommend skimming these docs to get a feel for how to use Sorbet:
    - Gradual Type Checking: #{set_color("https://sorbet.org/docs/gradual", :cyan)}
    - Enabling Static Checks: #{set_color("https://sorbet.org/docs/static", :cyan)}
    - RBI Files: #{set_color("https://sorbet.org/docs/rbi", :cyan)}
  OUTPUT
end

def require

def require
  command = Commands::Require.new(
    requires_path: options[:postrequire],
    sorbet_config_path: SORBET_CONFIG_FILE,
  )
  command.run
end

def todo

def todo
  command = Commands::Todo.new(
    todo_file: options[:todo_file],
    file_header: options[:file_header],
  )
  command.run_with_deprecation
end