# frozen_string_literal: true

require 'time'

module GDK
  module Command
    # Handles `gdk update` command execution
    class Update < BaseCommand
      GdkNotFoundError = Class.new(StandardError)
      WEEK_IN_SECONDS = 7 * 24 * 60 * 60

      def run(_args = [])
        success = update!

        success = run_rake(:reconfigure) if success && config.gdk.auto_reconfigure?

        if success
          Announcements.new.render_all
          GDK::Output.success('Successfully updated!')
          run_weekly_diagnostics
        else
          GDK::Output.error('Failed to update.', report_error: false)
          display_help_message
        end

        success
      rescue Support::Rake::TaskWithLogger::LoggerError => e
        e.print!
        false
      ensure
        check_gdk_available
      end

      private

      def update!
        GDK::Hooks.with_hooks(config.gdk.update_hooks, 'gdk update') do
          # Run `self-update` first to make sure Makefiles are up-to-date.
          # This ensures the next `make update` call works with the latest updates and instructions.
          if self_update?
            result = self_update!
            next false unless result
          end

          old_env = ENV.to_h
          ENV.merge! update_env

          success = run_rake('gdk:migrate')
          success = run_rake(:update) if success

          success
        ensure
          update_env.keys.map { |k| ENV.delete(k) }
          ENV.merge! old_env || {}
        end
      end

      def self_update!
        previous_revision = current_git_revision
        sh = GDK.make('self-update')

        return false unless sh.success?

        if previous_revision != current_git_revision
          Dir.chdir(config.gdk_root.to_s)
          ENV['GDK_SELF_UPDATE'] = '0'
          Kernel.exec 'gdk update'
        end

        true
      end

      def self_update?
        %w[1 yes true].include?(ENV.fetch('GDK_SELF_UPDATE', '1'))
      end

      def update_env
        {
          'PG_AUTO_UPDATE' => '1',
          'GDK_SKIP_MAKEFILE_TIMEIT' => '1'
        }
      end

      def current_git_revision
        Shellout.new(%w[git rev-parse HEAD], chdir: config.gdk_root).run
      end

      def check_gdk_available
        return if Utils.executable_exist_via_tooling_manager?('gdk')

        out.error('The `gdk` is no longer available after `gdk update`. This is unexpected, please report this in https://gitlab.com/gitlab-org/gitlab-development-kit/-/issues/2388.')

        GDK::Telemetry.capture_exception(GdkNotFoundError.new('`gdk` command is no longer available'))
      end

      def run_weekly_diagnostics
        return unless GDK::ReminderHelper.should_run_reminder?('diagnostics')

        # Empty for now. Add diagnostic checks here as needed.
        diagnostics = []

        diagnostics.each do |diagnostic|
          next if diagnostic.success?

          GDK::Output.puts
          GDK::Output.warn('Upcoming change notice - Action required:')
          GDK::Output.divider
          GDK::Output.puts(diagnostic.detail.strip)
          GDK::Output.divider
          GDK::Output.info('We will send you a reminder in a week to help you prepare for this change.')
        end

        GDK::ReminderHelper.update_reminder_timestamp!('diagnostics')
      end
    end
  end
end
