#!/usr/bin/env ruby

# frozen_string_literal: true

ENV['RAILS_ENV'] = 'test'

require 'optparse'
require 'open3'
require 'fileutils'
require 'uri'
require 'gitlab-chronic'

class MigrationsTimestampRefresher
  ##
  # Directories where migrations are stored
  MIGRATION_DIRS = %w[db/migrate db/post_migrate].join(' ').freeze

  def initialize(options)
    @options = options
  end

  def execute
    Dir.chdir(File.expand_path('..', __dir__)) do
      refresh_migrations
      regenerate_schema
    end
  end

  private

  attr_reader :options

  def refresh_migrations
    migrations = untracked_schema_migrations + committed_schema_migrations
    migrations = migrations.sort_by { |file| file.slice(/\d{14}/) }
    migrations.each do |file|
      new_file = file.gsub(/\d{14}/, next_migration_number)
      puts "\e[32m$ mv #{file} #{new_file}\e[37m"

      FileUtils.mv(file, new_file)
    end
  end

  def regenerate_schema
    run('./scripts/regenerate-schema')
  end

  def next_migration_number
    @next_migration_number ||= seed_migration_number.to_i
    @next_migration_number += rand(2..5)
    @next_migration_number.to_s
  end

  def seed_migration_number
    Chronic
      .parse(options.fetch(:after, 'now'))
      .utc.strftime("%Y%m%d%H%M%S")
  end

  def untracked_schema_migrations
    git_command = "git ls-files --others --exclude-standard -- #{MIGRATION_DIRS}"
    run(git_command).chomp.split("\n")
  end

  def committed_schema_migrations
    git_command = "git diff --name-only --diff-filter=A #{merge_base} -- #{MIGRATION_DIRS}"
    run(git_command).chomp.split("\n")
  end

  ##
  # Run the given +cmd+.
  #
  # The command is colored green, and the output of the command is
  # colored gray.
  # When the command failed an exception is raised.
  def run(cmd)
    puts "\e[32m$ #{cmd}\e[37m"
    stdout_str, stderr_str, status = Open3.capture3(cmd)
    puts "#{stdout_str}#{stderr_str}\e[0m"
    raise("Command failed: #{stderr_str}") unless status.success?

    stdout_str
  end

  ##
  # Return the base commit between source and target branch.
  def merge_base
    @merge_base ||= run("git merge-base #{target_branch} #{source_ref}").chomp
  end

  ##
  # Return the name of the target branch
  #
  # Get source ref from CI environment variable, or read the +TARGET+
  # environment+ variable, or default to +HEAD+.
  def target_branch
    ENV['CI_MERGE_REQUEST_TARGET_BRANCH_NAME'] || ENV['TARGET'] || ENV['CI_DEFAULT_BRANCH'] || 'master'
  end

  ##
  # Return the source ref
  #
  # Get source ref from CI environment variable, or default to +HEAD+.
  def source_ref
    ENV['CI_COMMIT_SHA'] || 'HEAD'
  end
end

if $PROGRAM_NAME == __FILE__
  options = {}

  OptionParser.new do |opts|
    opts.on("-a", "--after VALUE", String, "Start value for the timestamp") do |value|
      options[:after] = value
    end

    opts.on("-h", "--help", "Prints this help") do
      puts opts
      exit
    end
  end.parse!

  MigrationsTimestampRefresher.new(options).execute
end
