class MyshixunsController < ApplicationController
  before_action :require_login, :check_auth, :except => [:training_task_status, :code_runinng_message]
  before_action :find_myshixun, :except => [:training_task_status, :code_runinng_message]
  before_action :find_repo_name, :except => [:training_task_status, :code_runinng_message]
  skip_before_action :verify_authenticity_token, :only => [:html_content]

  ## TPI关卡列表
  def challenges
    # @challenges = Challenge.where(shixun_id: params[:shixun_id])
    @shixun = @myshixun.shixun
    @games = @myshixun.games.includes(:challenge).reorder("challenges.position")
    @identity = current_user.game_identity(@games.first)
  end


  # For Admin
  # 强制重置实训
  # 前段需要按照操作过程提示
  def reset_my_game
    course = Course.find_by(id: params[:course_id])
    unless (current_user.admin_or_business? || current_user.id == @myshixun.user_id) || (course.present? && current_user.course_identity(course) < Course::STUDENT)
      tip_exception("403", "")
    end
    begin
      @shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id)
      ActiveRecord::Base.transaction do
          @myshixun.destroy!
          # 目前考虑性能问题，所以只删除相关数据。可能会产生一些无用数据，整体来说，方案可行
          StudentWork.where(:myshixun_id => @myshixun.id).each do |work|
            work.delete
            StudentWork.create!(homework_common_id: work.homework_common_id, user_id: work.user_id, compelete_status: -1)
          end
      end
      # 删除版本库 TODO: 删除版本库比较慢
      # GitService.delete_repository(repo_path: @repo_path) unless @shixun.is_choice_type?
    rescue Exception => e
      raise "delete_repository_error:#{e.message}"
    end
  end

  # 代码运行中的信息接口
  # 这个方法是中间层主动调用的，点击评测后，中间层会发送参数过来，告诉目前Pod的启动情况，一次评测会调用两次请求
  def code_runinng_message
    begin
      jsonTestDetails = JSON.parse(params[:jsonTestDetails])
      game_id = jsonTestDetails['buildID']
      message = jsonTestDetails['textMsg']
      if game_id.present? && message.present?
        game = Game.find game_id
        msg = game.run_code_message
        # 只有评测中的game才会创建和更新代码评测中的信息
        if game.status == 1 || game.status == 2
          if msg.blank?
            RunCodeMessage.create!(:game_id => game_id, :status => 1, :message => message)
          else
            msg.update_attributes(:status => (msg.status + 1), :message => message)
          end
        end
        render :json => {:data => "success"}
      end
    rescue Exception => e
      render :json => {:data => "failed, exception_message: #{e}"}
    end
  end

  # 中间层评测接口
  # taskId 即返回的game id
  # 返回结果：params [:stauts] 0 表示成功，其它则失败
  # msg 错误信息
  # output 为测试用户编译输出结果
  # myshixun:status 1为完成实训
  # @jenkins: caseId对应test_set的position，passed: 1表示成功，0表示失败
  # resubmit 1：表示已通关后重新评测；0：表示非重新评测
  # retry_status 0：初始值；1：重新评测失败；2：重新评测成功
  # tpiRepoPath 中间层图片的workspace路径
  # params[:jsonTestDetails] = '{"buildID":"19284","compileSuccess":"1",
  # "msg":[{"caseId":"1","expectedOutput":"MSAyIDMNCg","input":"MiAzIDE","output":"MSAyIDMNCg","passed":"1"},
  # {"caseId":"2","expectedOutput":"LTMgMSA2DQo","input":"LTMgNiAx","output":"LTMgMSA2DQo","passed":"1"},
  # {"caseId":"3","expectedOutput":"LTcgLTUgLTMNCg","input":"LTcgLTMgLTU","output":"LTcgLTUgLTMNCg","passed":"1"}],
  # "outPut":"Y29tcGlsZSBzdWNjZXNzZnVsbHk","resubmit":"","status":"0"}'
  # params[:timeCost] = '{"evaluateEnd":"2017-11-24 11:04:37","pull":"0.086",
  # "createPod":"1.610","evaluateAllTime":2820,"evaluateStart":"2017-11-24 11:04:35","execute":"0.294"}'
  # params[:pics] = "a.png,b.png,c.png"
  def training_task_status

    ActiveRecord::Base.transaction do
      begin
        t1 = Time.now
        #uid_logger_dubug("@@@222222#{params[:jsonTestDetails]}")
        jsonTestDetails = JSON.parse(params[:jsonTestDetails])
        timeCost = JSON.parse(params[:timeCost])
        brige_end_time = Time.parse(timeCost['evaluateEnd']) if timeCost['evaluateEnd'].present?
        return_back_time = format("%.3f",  ( t1.to_f - brige_end_time.to_f)).to_f
        status = jsonTestDetails['status']
        game_id = jsonTestDetails['buildID']
        sec_key = jsonTestDetails['sec_key']
        server_url = jsonTestDetails['showServer']

        #uid_logger_dubug("training_task_status start-#{game_id}-1#{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
        resubmit = jsonTestDetails['resubmit']
        outPut = tran_base64_decode64(jsonTestDetails['outPut'])

        jenkins_testsets = jsonTestDetails['msg']
        compile_success  = jsonTestDetails['compileSuccess']
        # message = Base64.decode64(params[:msg]) unless params[:msg].blank?

        game = Game.find(game_id)
        myshixun = game.myshixun
        challenge = game.challenge
        # test_sets = challenge.test_sets
        if challenge.picture_path.present?
          #pics = params[:files]
          pics = params[:tpiRepoPath]
          game.update_column(:picture_path, pics)
        end
        # 如果启动了服务，则存在redis中，供前端访问
        if server_url.present?
          game.set_server_key(server_url)
        end
        max_query_index = game.outputs ? (game.outputs.first.try(:query_index).to_i + 1) : 1
        test_set_score = 0
        unless jenkins_testsets.blank?
          jenkins_testsets.each_with_index do |j_test_set, i|
            actual_output = tran_base64_decode64(j_test_set['output'])
            #ts_time += j_test_set['testSetTime'].to_i

            # is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public)
            ts_time = format("%.2f",  j_test_set['testSetTime'].to_f/1000000000).to_f if j_test_set['testSetTime']
            ts_mem = format("%.2f",  j_test_set['testSetMem'].to_f/1024/1024).to_f if j_test_set['testSetMem']

            Output.create!(:code => status, :game_id => game_id, :out_put => outPut, :test_set_position => j_test_set['caseId'],
                           :actual_output => actual_output, :result => j_test_set['passed'].to_i, :query_index => max_query_index,
                           :compile_success => compile_success.to_i, :sec_key => sec_key, :ts_time => ts_time, :ts_mem => ts_mem)
            # 如果设置了按测试集给分,则需要统计测试集的分值
            if challenge.test_set_score && j_test_set['passed'].to_i == 1
              test_set_score += challenge.test_sets.where(:position => j_test_set['caseId']).pluck(:score).first
            end
          end
        end
        record = EvaluateRecord.where(:identifier => sec_key).first
        answer_deduction_percentage = (100 - game.answer_deduction) / 100.to_f # 查看答案后剩余分数的百分比.
        # answer_deduction是查看答案的扣分比例
        # status：0表示评测成功
        if status == "0"
          if resubmit.present?
            game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit)
            challenge.path.split("；").each do |path|
              game_passed_code(path.try(:strip), myshixun, game_id)
            end
          else
            game.update_attributes!(:status => 2,
                                    :end_time => Time.now,
                                    :accuracy => format("%.4f", 1.0 / game.query_index))
            myshixun.update_attributes!(:status => 1) if game.had_done == 1
            challenge.path.split("；").each do |path|
              game_passed_code(path.try(:strip), myshixun, game_id)
            end
            # 如果是已经发布的实训,则需要给出相应的奖励
            if challenge.shixun.try(:status) > 1
              score = (challenge.score * answer_deduction_percentage).to_i
              if score > 0
                reward_attrs = { container_id: game.id, container_type: 'Game', score: score }
                RewardGradeService.call(game.user, reward_attrs)
                RewardExperienceService.call(game.user, reward_attrs)
              end
              # 需要扣除查看答案的分数
              game.update_attributes!(:final_score => score)
            end

            # 更新实训关联的作品分数 TODO: 更新作品分数
            # HomeworksService.new.update_myshixun_work_score myshixun
          end
          # 如果过关了，下一关的状态是3（为开启），则需要把状态改成1（已开启）
          # next_game = game.next_game
          next_game = game.next_game(myshixun.shixun_id, game.myshixun_id, challenge.position)
          next_game.update_column(:status, 0) if next_game.present? && next_game.status == 3
          # status == "-1" 表示返回结果错误
        else
          if resubmit.present?
            game.update_attributes!(:retry_status => 1, :resubmit_identifier => resubmit)
          else
            # 评测没通关则,测试集对的个数给分,并且还要扣除用户是否查看答案的值
            test_set_percentage = test_set_score / 100.to_f # 测试集得分比
            score = (challenge.score * test_set_percentage * answer_deduction_percentage).to_i
            # 如果分数比上次多，则更新成绩
            game_update =
                if game.final_score < score
                  {final_score: score, status: 0}
                else
                  {status: 0}
                end
            game.update_attributes!(game_update)
          end
        end
        test_cases_time = format("%.3f",  (Time.now.to_f - t1.to_f)).to_f
        if record.present?
          consume_time = format("%.3f",  (Time.now - record.created_at)).to_f

          record.update_attributes!(:consume_time => consume_time, :git_pull => timeCost['pull'] , :create_pod => timeCost['createPod'],
                                    :pod_execute => timeCost['execute'], :test_cases => test_cases_time,
                                    :brige => timeCost['evaluateAllTime'], :return_back => return_back_time)
        end
        sucess_status
      # rescue Exception => e
      #   tip_exception(e.message)
      #   uid_logger_error("training_task_status error: #{e}")
      #   raise ActiveRecord::Rollback
      end
    end
  end

  # 连接webssh
  def open_webssh
    username = edu_setting('webssh_username')
    password = edu_setting('webssh_password')
    old_time = Time.now.to_i
    begin
      shixun_tomcat = edu_setting('tomcat_webssh')
      tpiGitURL = "#{edu_setting('git_address_domain')}/#{@myshixun.repo_path}"
      uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
      # 由于中间层采用混合云的方式，因为local参数表示在有文件生成的实训是在本地生成，还是在其他云端生成评测文件
      local = @myshixun.shixun.challenges.where.not(show_type: -1).count == 0
      params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: local, tpiGitURL: tpiGitURL,
                containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
      res = uri_post uri, params
      if res && res['code'].to_i != 0
        tip_exception("实训云平台繁忙（繁忙等级：92）")
      end
      render :json => {:host => res['address'],
                       :port => res['port'],
                       :ws_url => res['ws_address'],
                       :username => username,
                       :password => password,
                       :game_id => @myshixun.id,
                       :webssh_url => "#{shixun_tomcat}/bridge"}
    rescue Exception => e
      logger.error(e)
      render :json => {:error => e.try(:message)}
    ensure
      use_time = Time.now.to_i - old_time
      logger.info "open_webssh tpiID #{@myshixun.id} use time #{use_time}"
    end
  end

  include GitCommon

  # -----Repository
  # TODO: 之类需要一个resubmit参数,但是是关于games.
  def update_file
    begin
      @hide_code = Shixun.where(id: @myshixun.shixun_id).pluck(:hide_code).first
      tip_exception("实验环境不能为空，请查看实训模板的环境配置项是否正确!") if (@myshixun.mirror_name.blank? || @myshixun.mirror_name.first.to_s == "-1")
      path = params[:path].strip unless params[:path].blank?
      game_id = params[:game_id]
      game = Game.find(game_id)
      @content_modified = 0

      # params[:evaluate] 实训评测时更新必须给的参数，需要依据该参数做性能统计，其它类型的更新可以跳过
      # 自动保存的时候evaluate为0；点评测的时候为1
      if params[:evaluate] == 1
        exec_time = game.challenge.try(:exec_time)
        @sec_key = generate_identifier(EvaluateRecord, 12)
        record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id,
                                        :identifier => @sec_key, :exec_time => exec_time)
        #uid_logger_dubug("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
      end
      # 隐藏代码文件 和  VNC的都不需要走版本库
      vnc =  @myshixun.shixun&.vnc
      unless @hide_code || (vnc && params[:evaluate].present?)

        # 远程版本库文件内容
        last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]

        content =
            if python_file?(path)
              params[:content].gsub(/\t/, '    ').gsub(/ /, ' ')
            else
              params[:content]
            end

        if content != last_content
          @content_modified = 1

          author_name = current_user.real_name
          author_email = current_user.git_mail
          message = params[:evaluate] == 0 ? "System automatically submitted" : "User submitted"
          @content = GitService.update_file(repo_path: @repo_path,
                                            file_path: path,
                                            message: message,
                                            content: content.gsub("\r\n", "\n"),
                                            author_name: author_name,
                                            author_email: author_email)
        end
      end

      if game.status == 2
        @resubmit = Time.now.to_i
      end

      # 评测时间记录
      if record.present?
        consume_time = format("%.3f",  (Time.now.to_f - record.created_at.to_f)).to_f
        record.update_attributes!(:file_update => consume_time)
      end
    rescue Exception => e
      uid_logger_error(e.message)
      tip_exception("文件内容更新异常，请稍后重试")
    end
  end

  # 渲染实训代码
  # educodercss: 字符串以 ‘，’分隔，存储的是版本库css的路径
  # educoderscript: 字符串以 ‘，’分隔，存储的是版本库js的路径
  # contents： html实训的整体内容
  def html_content
    @contents = params[:contents] || ""
    edu_css = params[:educodercss]
    edu_js = params[:educoderscript]
    if @contents.present?
      @contents = @contents.gsub("w3equalsign", "=").gsub("w3scrw3ipttag", "script").gsub("edulink", "link").html_safe
    end
    # css
    if edu_css.present?
      css_path = edu_css.split(",")
      css_path.each do |path|
        file_content = git_fle_content(@repo_path, path)["content"]
        file_content = tran_base64_decode64(file_content) unless file_content.blank?
        @contents = @contents.sub(/EDUCODERCSS/, "<style>#{file_content}</style>")
      end
    end
    # js
    if edu_js.present?
      js_path = edu_js.split(",")
      js_path.each do |path|
        file_content = git_fle_content(@repo_path, path)["content"]
        file_content = tran_base64_decode64(file_content) unless file_content.blank?
        @contents = @contents.sub(/EDUCODERJS/, "<script>#{file_content}</script>")
      end
    end
    respond_to do |format|
      format.json
      format.html{render :layout => false}
    end
  end

  # 最新可以用的并发测试接口
  def sigle_mul_test
    codes = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
    begin
      identifiers = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).pluck(:identifier)
      ide = identifiers[rand(identifiers.length)]
      myshixun = Myshixun.where(:identifier => ide).first

      game = myshixun.games.last
      logger.warn("###2mul test game_build start ")
      identifier = game.try(:identifier)
      if game.status == 2
        code = codes.sample(8).join
        resubmit = "#{code}_#{myshixun.id}"
      end
      logger.warn("###3mul test game_build start ...")
      EvaluateRecord.create!(:user_id => myshixun.user_id, :shixun_id => myshixun.shixun.id, :game_id => game.id)
      redirect_to "/api/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
    rescue Exception => e
      logger.error("mul test failed ===> #{e.message}")
    end
  end

  def sync_code
    shixun_tomcat = edu_setting('cloud_bridge')
    begin
      git_myshixun_url = repo_ip_url @myshixun.repo_path
      git_shixun_url = repo_ip_url @myshixun.shixun.try(:repo_path)
      git_myshixun_url = Base64.urlsafe_encode64(git_myshixun_url)
      git_shixun_url = Base64.urlsafe_encode64(git_shixun_url)
      # todo: identifier 是以前的密码,用来验证的,新版如果不需要,和中间层协调更改.
      params = {tpiID: "#{@myshixun.try(:id)}", tpiGitURL: "#{git_myshixun_url}", tpmGitURL: "#{git_shixun_url}",
                identifier: "xinhu1ji2qu3"}
      uri = "#{shixun_tomcat}/bridge/game/resetJupyterTpm"
      res = uri_post uri, params
      if (res && res['code'] != 0)
        tip_exception("实训云平台繁忙（繁忙等级：95）")
      end
      shixun_new_commit = GitService.commits(repo_path: @myshixun.shixun.repo_path).first["id"]
      @myshixun.update_attributes!(commit_id: shixun_new_commit, reset_time: @myshixun.shixun.try(:reset_time))
      # 更新完成后，弹框则隐藏不再提示
      @myshixun.update_column(:system_tip, false)
      render_ok
    rescue Exception => e
      tip_exception("立即更新代码失败!#{e.message}")
    end
  end


  # -----End

  private
  def find_myshixun
    @myshixun = Myshixun.find_by!(identifier: params[:identifier])
  end

  def find_repo_name
    @repo_path = @myshixun.try(:repo_path)
    @path = params[:path]
  end

  def python_file?(path)
    false if path.blank?
    path.to_s.split(".").last.downcase == "py"
  end
end
