class ViteRuby::CLI::Install

def call(package_manager: nil, **)

def call(package_manager: nil, **)
  ENV["VITE_RUBY_PACKAGE_MANAGER"] ||= package_manager if package_manager
  $stdout.sync = true
  say "Creating binstub"
  ViteRuby.commands.install_binstubs
  say "Creating configuration files"
  create_configuration_files
  say "Installing sample files"
  install_sample_files
  say "Installing js dependencies"
  install_js_dependencies
  say "Adding files to .gitignore"
  install_gitignore
  say "\nVite ⚡️ Ruby successfully installed! 🎉"
end

def copy_template(path, to:)

def copy_template(path, to:)
  cp TEMPLATES_PATH.join(path), to
end

def create_configuration_files

Internal: Creates the Vite and vite-plugin-ruby configuration files.
def create_configuration_files
  copy_template "config/vite.config.ts", to: root.join("vite.config.ts")
  append root.join("Procfile.dev"), "vite: bin/vite dev"
  setup_app_files
  ViteRuby.reload_with(config_path: config.config_path)
end

def install_gitignore

Internal: Adds compilation output dirs to git ignore.
def install_gitignore
  return unless (gitignore_file = root.join(".gitignore")).exist?
  append(gitignore_file, <<~GITIGNORE)
    # Vite Ruby
    /public/vite*
    node_modules
    # Vite uses dotenv and suggests to ignore local-only env files. See
    # https://vitejs.dev/guide/env-and-mode.html#env-files
    *.local
  GITIGNORE
end

def install_js_dependencies

Internal: Installs vite and vite-plugin-ruby at the project level.
def install_js_dependencies
  package_json = root.join("package.json")
  unless package_json.exist?
    write package_json, <<~JSON
      {
        "private": true,
        "type": "module"
      }
    JSON
  end
  if (JSON.parse(package_json.read)["type"] != "module" rescue nil)
    FileUtils.mv root.join("vite.config.ts"), root.join("vite.config.mts"), force: true, verbose: true
  end
  install_js_packages js_dependencies.join(" ")
end

def install_js_packages(deps)

def install_js_packages(deps)
  run_with_capture("#{config.package_manager} add -D #{deps}", stdin_data: "\n")
end

def install_sample_files

Internal: Create a sample JS file and attempt to inject it in an HTML template.
def install_sample_files
  copy_template "entrypoints/application.js", to: config.resolved_entrypoints_dir.join("application.js")
end

def js_dependencies

Internal: The JS packages that should be added to the app.
def js_dependencies
  [
    "vite@#{ViteRuby::DEFAULT_VITE_VERSION}",
    "vite-plugin-ruby@#{ViteRuby::DEFAULT_PLUGIN_VERSION}",
  ]
end

def root

Internal: The root path for the Ruby application.
def root
  @root ||= silent_warnings { config.root }
end

def run_with_capture(*args, **options)

def run_with_capture(*args, **options)
  Dir.chdir(root) do
    _, stderr, status = ViteRuby::IO.capture(*args, **options)
    say(stderr) unless status.success? || stderr.empty?
  end
end

def say(*args)

def say(*args)
  $stdout.puts(*args)
end

def setup_app_files

Internal: Setup for a plain Rack application.
def setup_app_files
  copy_template "config/vite.json", to: config.config_path
  if (rackup_file = root.join("config.ru")).exist?
    inject_line_after_last rackup_file, "require", "use(ViteRuby::DevServerProxy, ssl_verify_none: true) if ViteRuby.run_proxy?"
  end
end

def silent_warnings

Internal: Avoid printing warning about missing vite.json, we will create one.
def silent_warnings
  old_stderr = $stderr
  $stderr = StringIO.new
  yield
ensure
  $stderr = old_stderr
end