require_relative "spork"
require_relative "runner"
require "fileutils"
require "logger"
require_relative "repomanage"
require "digest"
require "date"

BUILD_STRUCTURE = {
  :SRC => "src",
  :RPMS => "rpms",
  :RESULT => "result",
  :RESULT_SRPM => "result_srpm",
}

class MockManager
  attr :path, :config, :error, :last_status, :last_pid, :prep_dir, :db, :resultpath, :process_log, :repo_path, :git_path, :build_id, :log, :recips, :spec, :repo_lock, :git_id, :tmp_bld

  def initialize(path, config, cfg_counter_path, db, result_path, 
    repo_path, git_path, build_id, recips, spec_file, repo_lock, 
    git_id, tmp_bld, build_script)
    @error = nil
    unless File.exist? (path)
      Dir.mkdir(path)
    end
    @path = path
    @config = config
    cntr = 0
    @db = db
    @resultpath = result_path
    @repo_path = repo_path
    @git_path = git_path
    @build_id = build_id
    @recips = recips
    @spec = spec_file
    @repo_lock = repo_lock
    @git_id = git_id
    @tmp_bld = tmp_bld
    @build_script = build_script

    File.open(cfg_counter_path, "r+") do |f|
      f.flock(File::LOCK_EX)
      counter = f.gets.strip
      i_counter = counter.to_i
      i_counter = i_counter + 1
      f.puts("#{i_counter}")
      cnt = i_counter
    end
    tmp_name = (0...10).map { ("a".."z").to_a[rand(26)] }.join
    @prep_dir = File.join(path, "#{cntr}_#{tmp_name}")
    @process_log = File.join(@prep_dir, "process.log")
    Dir.mkdir(@prep_dir)
    FileUtils.touch(@process_log)
    @log = nil
    @tmp_src = ""
  end

  def get_build_process_log()
    @process_log
  end

  def clean_build
    FileUtils.rm_rf(@path)
  end

  def prepare_structure()
    @log.info("Подготовка структуры каталогов")
    BUILD_STRUCTURE.each_pair do |key, value|
      new_path = File.join(@prep_dir, value)
      @log.info("Создан каталог #{new_path}")
      Dir.mkdir(new_path)
    end
  end

  def prepare_src()
    @log.info("Подготовка исходных файлов проекта к формированию SRPMS")
    if File.exist?(@git_path)
      if File.directory?(@git_path)
        @tmp_src = File.join(@prep_dir, BUILD_STRUCTURE[:SRC], File.basename(@git_path))
        FileUtils.cp_r(@git_path, @tmp_src, verbose: true, remove_destination: true)
        FileUtils.rm_rf(File.join(@tmp_src, ".git"), secure: true)
      else
        @log.error("Это файл #{@git_path}, а не каталог")
        @error = true
      end
    else
      @log.error("Каталог #{@git_path} не существует")
      @error = true
    end
  end

  def prepare_source()
    @log.info("Запукс подготовительных скриптов")
    @recips.each_with_index do |item, index|
      @log.info("Формируем рецепт #{item[:filepath]}")
      rcp_name = "#{index}rcp_#{item[:filepath]}"
      File.open(File.join(@tmp_src, rcp_name), "w") do |f|
        f.write(item[:content].gsub(/\r$/, ""))
      end
      Dir.chdir(@tmp_src) do
        spec_file = File.join(@tmp_src, @spec)
        script = File.join(@tmp_src, rcp_name)
        cmd_args = %Q(/usr/bin/bash -x "#{script}" "#{spec_file}")
        @log.debug(cmd_args)
        cmd = Runner.new(cmd_args, @log)
        cmd.run_clean
        @error = true if cmd.exit_status != 0
        @log.error("Ошибка операции") if @error
      end
      break if @error
    end
  end

  def prepare_src_rpm()
    @log.info("Подготовка SRCRPM")
    spec_file = File.join(@tmp_src, @spec)
    if File.exist?(spec_file)
      if @tmp_bld
        dt = DateTime.now
        bld_id = @build_id.to_i
        bld_str = "%010d" % bld_id
        dt_str = dt.strftime("%Y%m%d_#{bld_str}")
        cmd_args = %Q(/usr/bin/rpm -q --specfile #{spec_file} --queryformat "%{RELEASE}\n" 2>/dev/null)
        @log.debug(cmd_args)
        cmd = Runner.new(cmd_args, @log)
        cmd.run
        if cmd.exit_status == 0
          res = "#{cmd.stdout}.#{dt_str}"
          versions = "#{cmd.stdout}".split("\n")
          if versions.length()>0
            res = versions[0].strip
            res = "#{res}.#{dt_str}"
          else
            res = cmd.stdout.strip
            res = "#{res}.#{dt_str}"
          end
          line_array = []
          File.readlines(spec_file).each do |line|
              if line =~ /^[\t ]*[Rr]elease:/
                line = "Release: #{res}"
              end
              line_array << line
          end
          File.open(spec_file, "w") do |f|
              line_array.each do |line|
                f.puts(line)
              end
          end
        end
      end
      Dir.chdir(@tmp_src) do
        cmd_args = %Q(/usr/bin/mock -r "#{@config}" --buildsrpm --spec "#{spec_file}" --sources "#{@tmp_src}" --resultdir "#{File.join(@prep_dir, BUILD_STRUCTURE[:RESULT_SRPM])}" --isolation=simple --disable-plugin=ccache)
        @log.debug(cmd_args)
        cmd = Runner.new(cmd_args, @log)
        cmd.run_clean
        @error = true if cmd.exit_status != 0
        @log.error("Ошибка операции") if @error
      end
    else
      @error = true
      @log.error("Не могу найти sepc файл #{spec_file}")
    end
  end

  def build_rpm()
    @log.info("Начало сборки пакетов")
    srpm_result_dir = File.join(@prep_dir, BUILD_STRUCTURE[:RESULT_SRPM])
    srpms = get_src_rpm_files_in_dir(srpm_result_dir)
    if srpms.nil? || srpms.length == 0
      @error = true
      @log.info("Нечего собирать, нет src.rpm пакетов")
    else
      srpm = srpms.first
      path_srpm = File.join(srpm_result_dir, srpm)
      cmd_args = %Q(/usr/bin/mock -r "#{@config}" "#{path_srpm}" --resultdir "#{File.join(@prep_dir, BUILD_STRUCTURE[:RESULT])}" --isolation simple)
      @log.debug(cmd_args)
      cmd = Runner.new(cmd_args, @log)
      cmd.run_clean
      @error = true if cmd.exit_status != 0
      @log.error("Ошибка операции") if @error
    end
  end

  def build_custom()
    @log.info("Начало сборки пакетов (сценарий кастомной сборки)")
    result_dir = File.join(@prep_dir, BUILD_STRUCTURE[:RESULT])

    
    @log.info("Формируем скрипт кастомной сборки #{@build_script[:script_name]}")
    rcp_name = "custom_#{@build_script[:script_name]}"
    File.open(File.join(@tmp_src, rcp_name), "w") do |f|
      f.write(@build_script[:content].gsub(/\r$/, ""))
    end
    Dir.chdir(@tmp_src) do
      script = File.join(@tmp_src, rcp_name)
      cmd_args = %Q(/usr/bin/bash -x "#{script}" "#{result_dir}")
      @log.debug(cmd_args)
      cmd = Runner.new(cmd_args, @log)
      cmd.run_clean
      @error = true if cmd.exit_status != 0
      @log.error("Ошибка операции") if @error
    end
  end

  def save_logs()
    FileUtils.mkdir_p(File.join(@resultpath, "#{@build_id}"))
    src_result = File.join(@prep_dir, BUILD_STRUCTURE[:RESULT_SRPM])
    rpm_result = File.join(@prep_dir, BUILD_STRUCTURE[:RESULT])
    if File.exist?(src_result)
      logs = get_log_paths(src_result)
      logs.each do |item|
        src = File.join(src_result, item)
        dst_dir = File.dirname(item)
        dst_fname = File.basename(item)
        dst = File.join(@resultpath, "#{@build_id}", dst_dir, "srpms_build_#{dst_fname}")
        FileUtils.cp_r(src, dst, verbose: true, remove_destination: true)
      end
    end
    if File.exist?(rpm_result)
      logs = get_log_paths(rpm_result)
      logs.each do |item|
        src = File.join(rpm_result, item)
        dst_dir = File.dirname(item)
        dst_fname = File.basename(item)
        dst = File.join(@resultpath, "#{@build_id}", dst_dir, "rpm_build_#{dst_fname}")
        FileUtils.cp_r(src, dst, verbose: true, remove_destination: true)
      end
    end
  end

  def save_rpms()
    @log.info("Формирование репозитория")
    repo = RepoManager.new(@repo_path)
    rpms = get_rpm_paths(File.join(@prep_dir, BUILD_STRUCTURE[:RESULT]))
    if rpms.nil? || rpms.length == 0
      @error = true
      @log.error("Пакеты не найдены")
    else
      prep_rpms = []
      rpms.each do |item|
        res = {}
        rpm_path = File.join(@prep_dir, BUILD_STRUCTURE[:RESULT], item)
        result = repo.get_rpm_info(rpm_path)
        if result[:error].nil?
          res[:src] = rpm_path
          res[:name] = result[:pkginfo].to_s
          arch = result[:pkginfo].arch
          if rpm_path =~ /\.src\.rpm$/
            res[:dst] = File.join(@repo_path, "SRPMS", File.basename(rpm_path))
          elsif rpm_path =~ /(debuginfo.+rpm$)|(debugsource.+rpm$)/
            res[:dst] = File.join(@repo_path, "Debug", File.basename(rpm_path))
          else
            if arch.nil? || arch.strip == ""
              arch = "noarch"
            end
            res[:dst] = File.join(@repo_path, arch, File.basename(rpm_path))
          end
          prep_rpms << res
        else
          @error = true
          @log.error("Ошибка пакета #{rpm_path}")
          break
        end
      end
      if @error == false
        File.open(@repo_lock, File::RDWR | File::CREAT) do |f|
          f.flock(File::LOCK_EX)
          # выклдака  пакетов и пересоздание repodata
          packages_uniq = true
          prep_rpms.each do |item|
            if File.exist?(item[:dst])
              packages_uniq = false
              break
            end
          end
          if packages_uniq
            prep_rpms.each do |item|
              @log.info("Копируется пакет #{item[:src]} в репозиторий #{item[:dst]}")
              FileUtils.mkdir_p(File.dirname(item[:dst]))
              FileUtils.cp_r(item[:src], item[:dst], verbose: true, remove_destination: true)
              sha256 = Digest::SHA256.file(item[:dst])
              @db.save_rpm(@build_id, item[:dst], item[:name], @git_id, sha256.hexdigest)
            end
            repo.create_repo
          else
            @error = true
            @log.error("Такие пакеты уже существуют в репозитории. Поднимите версию пакета")
          end
        end
      end
    end
  end

  def save_prg_log()
    FileUtils.mkdir_p(File.join(@resultpath, "#{@build_id}"))
    if File.exist?(@process_log)
      dst = File.join(@resultpath, "#{@build_id}")
      FileUtils.cp_r(@process_log, dst, verbose: false, remove_destination: true)
    end
  end

  def build_task(build_lock)
    @error = false
    @db.before_fork
    spock = Spork.spork(:logger => log) do
      @db.after_fork
      old_stdout = $stdout.dup
      $stdout = File.open(@process_log, "w")
      @log = Logger.new($stdout)

      File.open(build_lock,"wb") do |global_lock|
        global_lock.flock(File::LOCK_EX)
        global_lock.rewind
        build_info = @db.get_build_task_status(build_id)
        unless build_info.nil?
          if build_info[:result].to_i == 4
            return
          end
        end
        @db.update_build_task_status(@build_id, 0)
        @db.update_build_task_begin_time(@build_id)

        if @spec == ""
          @error = true
          @log.error("Не могу найти spec файл")
        end
        begin
          prepare_structure if @error == false
          prepare_src if @error == false
          prepare_source if @error == false
          if @build_script[:script_name].nil?
            prepare_src_rpm if @error == false
            build_rpm if @error == false
          else
            build_custom if @error == false
          end
          save_logs
          save_rpms if @error == false
        rescue => e
          @error = true
          puts e
        end
        $stdout = old_stdout
        @log.close
        save_prg_log
        clean_build
        if @error
          @db.update_build_task_status(@build_id, 1)
        else
          @db.update_build_task_status(@build_id, 2)
        end
        @db.update_build_task_end_time(@build_id)
        global_lock.flock(File::LOCK_UN)

      end
    end
    @db.after_fork

  end

  def self.clean_mock
    cmd_args = %Q(/usr/bin/mock --scrub-all-chroots)
    cmd = Runner.new(cmd_args)
    cmd.run_clean
  end
end
