class QualityAnalysisController < ApplicationController
  before_filter :find_project_by_project_id#, :except => [:getattachtype]
  before_filter :find_quality_analysis, :only => [:edit, :update_jenkins_job]
  before_filter :authorize
  before_filter :connect_jenkins, :only => [:create, :edit, :update_jenkins_job, :index, :delete]
  layout "base_projects"
  include ApplicationHelper
  include QualityAnalysisHelper
  require 'jenkins_api_client'
  require 'nokogiri'
  require 'json'
  require 'open-uri'

  def show

  end

  # params 说明：{identifier：版本库名}
  # type: 1 新的分析 2 重新分析
  def create
    begin
      user_name = User.find(params[:user_id]).try(:login)
      identifier = params[:identifier]
      rep_id = params[:rep_id]

      # job_name and sonar_name 前者为job名字，后者为jenkins配置名
      job_name = "#{user_name}-#{rep_id}"
      sonar_name = "#{user_name}:#{rep_id}"

      # 考虑到历史数据：有些用户创建类job但是build失败,即sonar没有结果，这个时候需要把job删除,并且删掉quality_analyses表数据
      # 如果不要这句则需要迁移数据
      @sonar_address = Redmine::Configuration['sonar_address']
      # projects_date = open(@sonar_address + "/api/projects/index").read
      # arr = JSON.parse(projects_date).map {|m| m["nm"]}   # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
      quality_an = QualityAnalysis.where(:sonar_name => sonar_name)
      # if @client_jenkins.job.exists?(job_name) && QualityAnalysis.where(:sonar_name => sonar_name).select{|qa| arr.include?(qa.sonar_name)}.blank?
      #   aa = @client_jenkins.job.delete("#{job_name}")
      #   quality_an.delete unless quality_an.blank?
      # end

      # type 1的时候之所以判断job是否存在，为了防止特殊情况，正常情况是不会出现的
      # 重新分析的时候需要删除以前的分析结果
      @client_jenkins.job.delete("#{job_name}") if @client_jenkins.job.exists?(job_name)
      quality_an.delete_all unless quality_an.blank?

      # Checks if the given job exists in Jenkins.
      @g = Gitlab.client
      branch = params[:branch]
      language = swith_language_type(params[:language])
      path = params[:path].blank? ? "./" : params[:path]
      # qa = QualityAnalysis.where(:project_id => @project.id, :author_login => user_name).first
      version = 1
      properties = "sonar.projectKey=#{sonar_name}
        sonar.projectName=#{sonar_name}
        sonar.projectVersion=#{version}
        sonar.sources=#{path}
        sonar.language=#{language.downcase}
        sonar.sourceEncoding=utf-8"
      git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"

      # 替换配置文件
      @doc = Nokogiri::XML(File.open(File.join(Rails.root, 'tmp', 'config.xml')))
      @doc.at_xpath("//hudson.plugins.git.UserRemoteConfig/url").content = git_url
      @doc.at_xpath("//hudson.plugins.git.BranchSpec/name").content = "*/#{branch}"
      @doc.at_xpath("//hudson.plugins.sonar.SonarRunnerBuilder/properties").content = properties # sonar-properties

      # jenkins job创建
      jenkins_job = @client_jenkins.job.create("#{job_name}", @doc.to_xml)

      # 将地址作为hook值添加到gitlab
      # @g.add_project_hook(@project.gpid, @jenkins_address + "/project/#{job_name}")
      # job创建完成后自动运行job,如果运行成功则返回‘200’
      code = @client_jenkins.job.build("#{job_name}")

      # 判断调用sonar分析是否成功
      # 等待启动时间处理, 最长时间为30分钟
      for i in 0..360 do
        sleep(5)
        @current_build_status = @client_jenkins.job.get_current_build_status("#{job_name}")
        if (@current_build_status == "success" || @current_build_status == "failure")
          break
          if i == 360
            @build_console_result = false
            break
          end
        end
      end

      # sonar 缓冲，sonar生成数据
      sleep(10)

      # 获取sonar output结果
      console_build = @client_jenkins.job.get_console_output("#{job_name}", build_num = 0, start = 0, mode = 'text')["output"]
      logger.info("@current_build_status is ==> #{@current_build_status}")

      # 两种情况需要删除job：
      # 1/创建成功但是build失败则删除job
      # 2/creat和build成功，调用sonar启动失败则删除job
      # 错误信息存储需存到Trustie数据库，否则一旦job删除则无法获取这些信息
      if jenkins_job == '200' && code != '201'
        @client_jenkins.job.delete("#{job_name}")
      else
        if @current_build_status == "failure"
          reg_console = /Exception:.*?\r/.match(console_build)
          output = reg_console[0].gsub("\r", "") unless reg_console.nil?
          se = SonarError.where(:jenkins_job_name => job_name).first
          se.nil? ? SonarError.create(:project_id => @project.id, :jenkins_job_name => job_name, :output => output) : se.update_column(:output, output)
          @client_jenkins.job.delete("#{job_name}")
        elsif @current_build_status == "success"
          if quality_an.blank?
            QualityAnalysis.create(:project_id => @project.id, :author_login => user_name, :rep_identifier => identifier,
                                   :sonar_version => version, :path => path, :branch => branch, :language => language, :sonar_name => "#{user_name}:#{rep_id}")
          end
        end
      end

      respond_to do |format|
        if @current_build_status == "success"
          format.html{redirect_to project_quality_analysis_path(:project_id => @project.id, :resource_id => sonar_name, :branch => branch, :current_build_status => @current_build_status, :job_name => job_name)}
        elsif @current_build_status == "failure"
          format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name)}
        end
      end

    rescue => e
      @message = e.message
      logger.error("######################====>#{e.message}")
      respond_to do |format|
        format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name, :message => @message)}
        # format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => identifier}
      end
    end

  end

  def error_list
    @error_list = SonarError.where(:jenkins_job_name => params[:job_name]).first
    respond_to do |format|
      format.html
    end
  end

  # get language type
  def swith_language_type language
    if language == "c#"
      "cs"
    elsif language == "python"
      "py"
    elsif language == "c"
      "c++"
    else
      language
    end
  end

  def edit
    @g = Gitlab.client
    gitlab_branches = @g.branches(@project.gpid)
    @branch_names = gitlab_branches.map{|b| b.name}
    @gitlab_default_branch = @g.project(@project.gpid).default_branch
  end

  # 删除的时候主要删除三方面数据：1/Trustie数据 2/jenkins数据 3/sonar数据
  # 如果只删除数据1，则新建的时候会有冲突
  def delete
    begin
      qa = QualityAnalysis.find(params[:id])
      rep_id = Repository.where(:project_id => @project.id, :identifier => qa.rep_identifier).first.try(:id)
      job_name = "#{qa.author_login}-#{rep_id}"
      logger.info("result: job_name  ###################==>#{job_name}")
      logger.info("result: @client_jenkins.job  ###################==>#{@client_jenkins.job}")

      d_job = @client_jenkins.job.delete(job_name)
      logger.info("result: delete job ###################==>#{d_job}")
      qa.delete
      respond_to do |format|
        format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => gitlab_repository(@project).identifier}
      end
    rescue Exception => e
      puts e
    end
  end

  # 更新Jenkins job，主要包括相关配置文件参数的更新，Trustie平台数据的更新
  def update_jenkins_job
    begin
      rep_id = Repository.where(:project_id => @project.id).first.try(:id)
      sonar_name = @quality_analysis.sonar_name
      job_name = "#{@quality_analysis.author_login}-#{rep_id}"
      version = @quality_analysis.sonar_version
      path = params[:path].blank? ? "./" : params[:path]
      language = swith_language_type(params[:language])
      branch = params[:branch]
      identifier = @quality_analysis.rep_identifier
      properties = "sonar.projectKey=#{sonar_name}
        sonar.projectName=#{sonar_name}
        sonar.projectVersion=#{version}
        sonar.sources=#{path}
        sonar.language=#{language.downcase}
        sonar.sourceEncoding=utf-8"
      git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"

      # modify config.yml
      @doc = Nokogiri::XML(File.open(File.join(Rails.root, 'tmp', 'config.xml')))
      @doc.at_xpath("//hudson.plugins.git.UserRemoteConfig/url").content = git_url
      @doc.at_xpath("//hudson.plugins.git.BranchSpec/name").content = "*/#{branch}"
      @doc.at_xpath("//hudson.plugins.sonar.SonarRunnerBuilder/properties").content = properties # sonar-properties

      # update成功则返回 ‘200’
      jenkins_job = @client_jenkins.job.update("#{job_name}", @doc.to_xml)
      # 数据更新到Trustie数据
      if jenkins_job == '200'
        logger.info("quality_ananlysis will be updated: ==> #{jenkins_job}")
        @quality_analysis.path = path
        @quality_analysis.language = language
        @quality_analysis.branch = branch
        @quality_analysis.save
      end
    rescue Exception => e
      logger.error("Update jenkins job: #{e}")
    end
    respond_to do |format|
      format.html{redirect_to project_quality_analysis_path(:project_id => @project.id)}
      format.js
    end
  end

  # resource_id: login + @repository.id
  def index
    begin
      @branch = params[:branch]
      @resource_id = params[:resource_id]
      @sonar_address = Redmine::Configuration['sonar_address']
      if params[:resource_id].nil?
        @name_flag = true
        projects_date = open(@sonar_address + "/api/projects/index").read
        arr = JSON.parse(projects_date).map {|m| m["nm"]} # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
        @quality_analyses = QualityAnalysis.where(:project_id => @project.id).select{|qa| arr.include?(qa.sonar_name)}
      else
        filter = "sqale_rating,function_complexity,duplicated_lines_density,comment_lines_density,sqale_index,lines,files,functions,classes,directories,blocker_violations,critical_violations,major_violations,minor_violations,info_violations,violations"
        complexity_date = open(@sonar_address + "/api/resources/index?resource=#{@resource_id}&depth=0&metrics=#{filter}").read
        @complexity =JSON.parse(complexity_date).first

        # 获取排名结果
        @g = Gitlab.client
        @author_infos = @g.rep_user_stats(@project.gpid, :rev => @branch)
        @user_quality_infos = []
        @author_infos.each do |author_info|
          email = author_info.email
          changes = author_info.changes.to_i
          unresolved_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}&resolved=false").read
          unresolved_issue_count = JSON.parse(unresolved_issues)["total"].to_i
          all_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}").read
          all_issue_count = JSON.parse(all_issues)["total"].to_i
          ratio = ((changes == 0 || all_issue_count == 0) ? 0 : format("%0.4f",all_issue_count.to_f/changes.to_f))
          @user_quality_infos << {:email => email, :changes => changes, :unresolved_issue_count => unresolved_issue_count, :ratio => ratio, :all_issue_count => all_issue_count}
        end

        # 按名称转换成hash键值对
        @ha = {}
        @complexity["msr"].each do |com|
          key = com["key"]
          if key == "sqale_index"
            value = com["frmt_val"]
          else
            value = com["val"]
          end
          @ha.store(key,value)
        end
      end
    rescue => e
      puts e
    end
  end

  # Find project of id params[:project_id]
  def find_project_by_project_id
    @project = Project.find(params[:project_id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def find_quality_analysis
    begin
      @quality_analysis = QualityAnalysis.find(params[:id])
    rescue
      render_404
    end
  end

  # Authorize the user for the requested action
  def authorize(ctrl = params[:controller], action = params[:action], global = false)
    unless @project.archived? && @project.gpid.nil?
      true
    else
      render_403 :message => :notice_not_authorized_archived_project
    end
  end

  def connect_jenkins
    @gitlab_address = Redmine::Configuration['gitlab_address']
    @jenkins_address = Redmine::Configuration['jenkins_address']
    jenkins_username = Redmine::Configuration['jenkins_username']
    jenkins_password = Redmine::Configuration['jenkins_password']
    # connect jenkins
    @client_jenkins = JenkinsApi::Client.new(:server_url => @jenkins_address, :username => jenkins_username, :password => jenkins_password)
  rescue => e
    logger.error("failed to connect Jenkins ==> #{e}")
  end

end
