# frozen_string_literal: true

module GDK
  # GDK Commands
  module Command
    # This is a list of existing supported commands and their associated
    # implementation class
    COMMANDS = {
      'cells' => -> { GDK::Command::Cells },
      'cleanup' => -> { GDK::Command::Cleanup },
      'clickhouse' => -> { GDK::Command::Clickhouse },
      'config' => -> { GDK::Command::Config },
      'console' => -> { GDK::Command::Console },
      'bao' => -> { GDK::Command::Bao },
      'debug-info' => -> { GDK::Command::DebugInfo },
      'diff-config' => -> { GDK::Command::DiffConfig },
      'doctor' => -> { GDK::Command::Doctor },
      'env' => -> { GDK::Command::Env },
      'install' => -> { GDK::Command::Install },
      'kill' => -> { GDK::Command::Kill },
      'help' => -> { GDK::Command::Help },
      '-help' => -> { GDK::Command::Help },
      '--help' => -> { GDK::Command::Help },
      '-h' => -> { GDK::Command::Help },
      nil => -> { GDK::Command::Help },
      'measure' => -> { GDK::Command::MeasureUrl },
      'measure-workflow' => -> { GDK::Command::MeasureWorkflow },
      'open' => -> { GDK::Command::Open },
      'telemetry' => -> { GDK::Command::Telemetry },
      'psql' => -> { GDK::Command::Psql },
      'psql-geo' => -> { GDK::Command::PsqlGeo },
      'pristine' => -> { GDK::Command::Pristine },
      'rails' => -> { GDK::Command::Rails },
      'reconfigure' => -> { GDK::Command::Reconfigure },
      'redis-cli' => -> { GDK::Command::RedisCli },
      'report' => -> { GDK::Command::Report },
      'reset-data' => -> { GDK::Command::ResetData },
      'reset-praefect-data' => -> { GDK::Command::ResetPraefectData },
      'reset-registry-data' => -> { GDK::Command::ResetRegistryData },
      'import-registry-data' => -> { GDK::Command::ImportRegistryData },
      'restart' => -> { GDK::Command::Restart },
      'start' => -> { GDK::Command::Start },
      'status' => -> { GDK::Command::Status },
      'stop' => -> { GDK::Command::Stop },
      'switch' => -> { GDK::Command::Switch },
      'tail' => -> { GDK::Command::Tail },
      'truncate-legacy-tables' => -> { GDK::Command::TruncateLegacyTables },
      'update' => -> { GDK::Command::Update },
      'version' => -> { GDK::Command::Version },
      '-version' => -> { GDK::Command::Version },
      '--version' => -> { GDK::Command::Version }
    }.freeze

    # Entry point for gem/bin/gdk.
    #
    # It must return true/false or an exit code.
    def self.run(argv)
      name = argv.shift
      command = ::GDK::Command::COMMANDS[name]

      if command
        klass = command.call

        check_gem_version!
        validate_config! if klass.validate_config?
        result = GDK::Telemetry.with_telemetry(name) { klass.new.run(argv) }

        exit result
      else
        suggestions = DidYouMean::SpellChecker.new(dictionary: ::GDK::Command::COMMANDS.keys).correct(name)
        message = ["#{name} is not a GDK command"]

        if suggestions.any?
          message << ', did you mean - '
          message << suggestions.map { |suggestion| "'gdk #{suggestion}'" }.join(' or ')
          message << '?'
        else
          message << '.'
        end

        GDK::Output.warn message.join
        GDK::Output.puts

        GDK::Output.info "See 'gdk help' for more detail."
        false
      end
    end

    def self.validate_config!
      GDK.config.validate!
      GDK::Services.enabled.each(&:validate!)
      nil
    rescue StandardError => e
      GDK::Output.error("Your GDK configuration is invalid.\n\n", e)
      GDK::Output.puts(e.message, stderr: true)
      abort('')
    end

    def self.check_gem_version!
      return if Gem::Version.new(GDK::GEM_VERSION) >= Gem::Version.new(GDK::REQUIRED_GEM_VERSION)

      GDK::Output.warn("You are running an old version of the `gitlab-development-kit` gem (#{GDK::GEM_VERSION})")
      GDK::Output.info("Please update your `gitlab-development-kit` to version #{GDK::REQUIRED_GEM_VERSION}:")
      GDK::Output.info("gem install gitlab-development-kit -v #{GDK::REQUIRED_GEM_VERSION}")
      GDK::Output.puts
    end
  end
end
