#! /usr/bin/env ruby

# frozen_string_literal: true

LKP_SRC = ENV['LKP_SRC'] || File.dirname(File.dirname(File.realpath($PROGRAM_NAME)))

require 'json'
require 'yaml'
require 'optparse'
require 'rbconfig'
require 'fileutils'
require "#{LKP_SRC}/sbin/cli/ccb_common"
require "#{LKP_SRC}/sbin/cli/ccb_api_client"

debug_flag = false
debug_stage = nil
clean_flag = false
config_hash = {}

options = OptionParser.new do |opts|
  opts.banner = 'Usage: ccb local-build key1=val1 key2=val2... [options]'
  opts.separator ''

  opts.separator '    eg.1: ccb local-build os_project=openEuler:Mainline package=gcc --rm --debug=bp'
  opts.separator '    eg.2: ccb local-build os_project=openEuler:Mainline package=gcc spec=gcc.spec --rm --debug=bp'
  opts.separator ''
  opts.separator 'options:'

  opts.on('--rm', 'clean container') do
    clean_flag = true
  end

  opts.on('--debug [stage]', 'set rpmbuild debug stage, include: bp/bc/bi') do |debug|
    debug_flag = true
    debug_stage = debug
  end

  opts.on('-h', '--help', 'show this message') do
    puts options
    exit
  end
end

options.parse!(ARGV)

if ARGV.empty?
  puts(options)
  exit
end

ARGV.each do |arg|
  k, v = arg.split('=', 2)
  config_hash[k] = v.strip
end


def get_response(jwt, hash, my_config)
  ccb_api_client = CcbApiClient.new(my_config['GATEWAY_IP'], my_config['GATEWAY_PORT'])
  response = ccb_api_client.search(jwt, hash)
  response = JSON.parse(response)
  check_return_code(response)
  response
end

def search_project_info(os_project, my_config)
  query = {"index": 'projects',
           "query": {
             "size": 1,
             "_source": ["spec_branch", "build_targets", "bootstrap_rpm_repo", "build_env_macros"],
             "query": {"bool": {"must": ["term": {"os_project": os_project}]}}
           }}
  jwt = load_jwt?(force_update = true)
  response = get_response(jwt, query.to_json, my_config)
  source = response['hits']['hits'][0]
  if source.nil?
    puts "Not found the project: #{os_project}"
    exit
  end
  source['_source']
end

def search_builds_info(os_project, arch, my_config)
  query = {"index": 'builds',
           "query": {
             "size": 1,
             "_source": ['emsx', 'build_target', 'snapshot_id', 'repo_id'],
             "query": {"bool": {"must": [{"term": {"os_project": os_project}}, {"term": {"build_target.architecture": arch}}]}},
             "sort": [{'create_time' => {"order": 'desc'}}]
           }}
  jwt = load_jwt?(force_update = true)
  response = get_response(jwt, query.to_json, my_config)
  source = response['hits']['hits'][0]
  if source.nil?
    puts "Not found builds of the project: #{os_project}"
    exit
  end
  source['_source']
end

def search_package_info(config_hash, my_config)
  query = {"index": 'snapshots',
           "query": {
             "_source": ["spec_commits"],
             "query": {"bool": {"must": ["term": {"os_project": config_hash["os_project"]}]}},
             "sort": [{'create_time' => {"order": 'desc'}}]
           }}
  jwt = load_jwt?(force_update = true)
  response = get_response(jwt, query.to_json, my_config)
  source = response['hits']['hits']
  if source.nil?
    puts "Not found the project: #{config_hash['os_project']}"
    exit
  end
  source
end

def modify_config_hash(data_hash, config_hash)
  config_hash["branch"] = data_hash["spec_branch"] || ''
  config_hash["build_env_macros"] = data_hash["build_env_macros"] || ''
  unless data_hash["bootstrap_rpm_repo"].empty?
    repos = []
    data_hash["bootstrap_rpm_repo"].each do |bts_repo|
      repos << bts_repo["repo"]
    end
    config_hash["bootstrap_rpm_repo"] = repos || ''
  end
end

def parse_builds_info(project_name, builds_info)
  tmp_hash = {}
  tmp_hash["emsx"] = builds_info["emsx"] || 'ems1'
  tmp_hash["os_project"] = project_name
  tmp_hash["snapshot_id"] = builds_info["snapshot_id"] || ''
  tmp_hash["repo_id"] = builds_info["repo_id"] || ''

  build_target = builds_info["build_target"] || ''
  unless build_target.empty?
    tmp_hash["os_variant"] = build_target["os_variant"] || ''
    tmp_hash["arch"] = build_target["architecture"] || ''
  end
  tmp_hash
end

def get_projects_data(os_project, my_config, config_hash)
  projects_info = []
  project_result = search_project_info(os_project, my_config)
  modify_config_hash(project_result, config_hash)

  builds_info = search_builds_info(os_project, config_hash['arch'], my_config)
  tmp_hash = parse_builds_info(os_project, builds_info)
  projects_info << tmp_hash

  ground_projects = nil
  unless project_result["build_targets"].empty?
    project_result["build_targets"].each do |bt_hash|
      if bt_hash["architecture"] == config_hash["arch"]
        unless bt_hash["ground_projects"].empty?
          ground_projects = bt_hash["ground_projects"]
        end
      end
    end
  end
  unless ground_projects.nil?
    ground_projects.each do |project|
      result = search_builds_info(project, config_hash['arch'], my_config)
      tmp_hash = parse_builds_info(project, result)
      projects_info << tmp_hash
    end
  end
  projects_info
end

def get_package_data(config_hash, my_config)
  spec_url = nil
  spec_branch = nil
  result = search_package_info(config_hash, my_config)
  result.each do |spt|
    unless spt["_source"]["spec_commits"].empty?
      if spt["_source"]["spec_commits"].has_key?(config_hash["package"])
        spec_url = spt["_source"]["spec_commits"][config_hash["package"]]["spec_url"]
        spec_branch = spt["_source"]["spec_commits"][config_hash["package"]]["spec_branch"]
        break
      end
    end
  end
  if spec_branch.empty? || spec_url.empty?
    puts "Not found package: #{config_hash['package']} in the project: #{config_hash['os_project']}"
    exit
  else
    config_hash["spec_url"] = spec_url
    config_hash["spec_branch"] = spec_branch
  end
end

def construct_repo_url(projects_info, config_hash)
  repourl_array = []
  prefix = "https://eulermaker.compass-ci.openeuler.openatom.cn/api"
  unless projects_info.empty?
    projects_info.each do |project|
      if project["emsx"].empty? || project["snapshot_id"].empty? || project["repo_id"].empty?
        next 
      else
        repo = "#{prefix}/#{project['emsx']}/repositories/#{project['os_project']}/#{project['os_variant']}/#{project['arch']}/history/#{project['snapshot_id']}/steps/#{project['repo_id']}"
        repourl_array << repo
      end
    end
  end

  unless config_hash["bootstrap_rpm_repo"].nil?
    repourl_array += config_hash["bootstrap_rpm_repo"]
  end
  repourl_array
end

def parse_config_info(config_hash)
  if config_hash.empty?
    exit
  end
  file_str = ""
  tmp_array = ["preinstall", "prefer", "use_xz", "disable_check_path", "use_kmod_libs", "use_git_lfs"]
  tmp_config_hash = config_hash.dup
  tmp_config_hash.each do |key, value|
    if key == "build_env_macros"
      yaml_data = YAML.load(value)
      yaml_data.each do |key1, value1|
        if key1 == "remove_rpms"
          tmp_pkg = ""
          value1.each do |key2, value2|
            if value2.include?(config_hash["package"])
              tmp_pkg = key2 + " " + tmp_pkg
            end
          end
          file_str += "export #{key1}=\"#{tmp_pkg}\"\n"
        elsif key1 == "macros"
          file_str += "export #{key1}=\"#{value1.join(',')}\"\n"
        elsif key1 == "use_root"
          if value1.include?(config_hash["package"])
            file_str += "export #{key1}=\"y\"\n"
            file_str += "export build_user=\"root\"\n"
            config_hash["build_user"] = "root"
          else
            file_str += "export #{key1}=\"n\"\n"
            file_str += "export build_user=\"lkp\"\n"
            config_hash["build_user"] = "lkp"
          end
        elsif tmp_array.include?(key1)
          file_str += "export #{key1}=\"#{value1.join(' ')}\"\n"
        else
          file_str += "export #{key1}=\"#{value1}\"\n"
        end
      end
    else
      file_str += "export #{key}=\"#{value}\"\n"
    end
  end
  file_str
end

def write_config_file(config_hash, repo_array)
  %x(rm -rf #{config_hash['output_dir']} && mkdir -p #{config_hash['output_dir']})
  File.open(config_hash["config_file_path"], "w") do |file|
    file_str = parse_config_info(config_hash)
    file.write(file_str)
    unless repo_array.empty?
      file.write("export repo_url=\"#{repo_array.join(' ')}\"\n")
    end
  end
end

def download_docker_image(config_hash)
  image_id = nil
  image_name = "openEuler-docker.#{config_hash['arch']}.tar.xz"
  image_url = "http://121.36.84.172/EulerMaker/#{config_hash['branch']}/docker_img/#{config_hash['arch']}/#{image_name}"
  puts "Download docker image, please wait"
  %x(wget #{image_url} -P #{config_hash['output_dir']} &>/dev/null)
  image_id=%x(docker load -i #{config_hash['output_dir']}/#{image_name} | awk -F':' '{print $NF}')
  %x(rm -f #{config_hash['output_dir']}/#{image_name})
  if image_id.nil?
    puts "Docker load failed"
    exit
  end
  image_id
end

def build_package(image_id, clean_flag, config_hash)
  container_name = "local-build-#{config_hash["package"]}"
  %x(docker rm -f #{container_name} &>/dev/null)
  system("docker run -itd --net=host --name #{container_name} #{image_id} /bin/bash -c 'exit'")
  exec_result=$?.exitstatus
  if exec_result != 0
    puts "Run a container failed"
    exit
  end
  %x(docker cp #{LKP_SRC} #{container_name}:/lkp-tests)
  %x(docker cp #{config_hash["config_file_path"]} #{container_name}:/tmp/)
  system("docker exec -it #{container_name} /bin/bash -c 'source /tmp/.project_config;bash /lkp-tests/tests/local-rpmbuild 2>&1 | tee /build.log'")
  exec_result=$?.exitstatus
  %x(docker cp #{container_name}:/build.log #{config_hash["output_dir"]})
  if exec_result != 0
    puts "Build failed"
    exit
  else
    if config_hash['debug_stage'].nil?
      puts "Build success"
    else
      puts "Build success with debug:#{config_hash['debug_stage']}"
    end
  end
  if config_hash['debug_stage'].nil?
    if config_hash['build_user'] == "lkp"
      rpmbuild_dir = "/home/lkp/rpmbuild"
    else
      rpmbuild_dir = "/root/rpmbuild"
    end
    %x(docker cp #{container_name}:#{rpmbuild_dir}/SRPMS #{config_hash["output_dir"]})
    %x(docker cp #{container_name}:#{rpmbuild_dir}/RPMS #{config_hash["output_dir"]})
  end
  puts "output_dir: #{config_hash["output_dir"]}"
  if clean_flag
    %x(docker rm -f #{container_name} &>/dev/null)
  else
    id = %x(docker ps -qf"name=#{container_name}")
    puts "container_id: #{id}"
  end
end


os_project = config_hash['os_project'] || ''
package = config_hash['package'] || ''
spec_name = config_hash['spec'] || ''

if os_project.empty? || package.empty?
  puts options
  puts 'Both empty are not allowed for argument: os_project, package'
  exit
end

if debug_flag
  if debug_stage.nil?
    puts options
    puts "\nError, argument --debug: expected one argument"
    exit
  else
    unless ["bp", "bc", "bi"].include?(debug_stage)
      puts options
      puts "\nError, argument --debug not support: #{debug_stage}"
      exit
    end
  end
end

if spec_name.empty?
  config_hash["spec_name"] = "#{package}.spec"
else
  config_hash["spec_name"] = spec_name
end

my_config = load_my_config
arch = RbConfig::CONFIG['arch'].split('-')[0]
config_hash["arch"] = arch
config_hash['output_dir'] = "/tmp/#{os_project}/#{package}"
config_hash['config_file_path'] = "#{config_hash['output_dir']}/.project_config"
config_hash["debug_stage"] = debug_stage

result=search_builds_info(os_project, arch, my_config)
projects_info = get_projects_data(os_project, my_config, config_hash)
get_package_data(config_hash, my_config)
repo_array = construct_repo_url(projects_info, config_hash)
write_config_file(config_hash, repo_array)
image_id = download_docker_image(config_hash)
build_package(image_id, clean_flag, config_hash)
