# frozen_string_literal: true

require 'rainbow/ext/string'
require_relative 'pitr_file'

# The first path works on production, while the second path works for tests
begin
  require_relative '../../../omnibus-ctl/lib/gitlab_ctl/util'
rescue LoadError
  require_relative '../../../gitlab-ctl-commands/lib/gitlab_ctl/util'
end

module Geo
  # PromoteDb promotes standby database as usual "pg-ctl promote" but
  # if point-in-time LSN file is found, the database will be recovered to that state first
  class PromoteDb
    attr_accessor :base_path, :postgresql_dir_path

    def initialize(ctl)
      @ctl = ctl
      @base_path = ctl.base_path
      @postgresql_dir_path = GitlabCtl::Util.get_public_node_attributes.dig('postgresql', 'dir')
    end

    def execute
      return true if recovery_to_point_in_time

      puts
      puts 'Promoting the PostgreSQL read-only replica to primary...'.color(:yellow)
      puts

      run_command('/opt/gitlab/embedded/bin/gitlab-pg-ctl promote', live: true).error!

      success_message

      true
    end

    private

    def postgresql_version
      @postgresql_version ||= GitlabCtl::PostgreSQL.postgresql_version
    end

    def recovery_to_point_in_time
      lsn = lsn_from_pitr_file

      return if lsn.nil?

      puts
      puts "Recovery to point #{lsn} and promoting...".color(:yellow)
      puts

      write_recovery_settings(lsn)

      run_command('gitlab-ctl restart postgresql', live: true).error!

      success_message

      true
    end

    def lsn_from_pitr_file
      lsn = Geo::PitrFile.new(@ctl).get
      lsn.empty? ? nil : lsn
    rescue Geo::PitrFileError
      # It is not an error if the file does not exist
      nil
    end

    def built_recovery_setting_for_pitr(lsn)
      <<-EOF
        recovery_target_lsn = '#{lsn}'
        recovery_target_action = 'promote'
      EOF
    end

    def write_recovery_settings(lsn)
      settings = built_recovery_setting_for_pitr(lsn)

      write_geo_config_file(settings)
    end

    def write_geo_config_file(settings)
      geo_conf_file = "#{postgresql_dir_path}/data/gitlab-geo.conf"

      File.open(geo_conf_file, "w", 0640) do |file|
        file.write(settings)
      end
    end

    def run_command(cmd, live: false)
      GitlabCtl::Util.run_command(cmd, live: live)
    end

    def success_message
      puts
      puts 'The database is successfully promoted!'.color(:green)
    end
  end
end
