class ShixunsController < ApplicationController
	include ShixunsHelper
	include ApplicationHelper
	include ElasticsearchAble
	include CoursesHelper

	before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list,
																											:discusses, :collaborators, :fork_list, :propaedeutics]
	before_action :check_account, only: [:new, :create, :shixun_exec]

	before_action :find_shixun,  except: [:index, :new, :create, :menus, :get_recommend_shixuns,
																				:propaedeutics, :departments, :apply_shixun_mirror,
																				:get_mirror_script, :download_file, :shixun_list, :batch_send_to_course]

	before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
																								 :propaedeutics, :departments, :apply_shixun_mirror,
																								 :get_mirror_script, :download_file, :shixun_list, :batch_send_to_course]
	before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, :add_file]

	before_action :allowed, only:  [:update, :close, :update_propaedeutics, :settings, :publish,
																  :shixun_members_added, :change_manager, :collaborators_delete,
																	:cancel_publish, :add_collaborators, :add_file]
	before_action :portion_allowed, only: [:copy]

	before_action :special_allowed, only: [:send_to_course, :search_user_courses]

	## 获取课程列表
  def index
		@shixuns = current_laboratory.shixuns.unhidden

		## 方向
		if params[:tag_level].present? && params[:tag_id].present?
      @shixuns = @shixuns.filter_tag(params[:tag_level].to_i, params[:tag_id].to_i)
			case params[:tag_level].to_i
				when 1 #大类
					@search_tags = Repertoire.find(params[:tag_id].to_i).name
				when 2 #子类
					@search_tags =  SubRepertoire.find(params[:tag_id].to_i).name
				when 3 #tag
					tag = TagRepertoire.find(params[:tag_id].to_i)
					@search_tags = "#{tag.sub_repertoire.name} / #{tag.name}"
			end
		end

		## 搜索关键字 匹配关卡名, 用户名, 实训名 和 空格多搜索
		if params[:keyword].present?
			keyword = params[:keyword].strip
			@shixuns = @shixuns.joins(:user, challenges: :challenge_tags).
					where("challenge_tags.name like :keyword
                  or challenges.subject like :keyword
                  or concat(lastname, firstname) like :keyword
                  or shixuns.name like :name",
								keyword: "%#{keyword}%", name: "%#{keyword.split(" ").join("%")}%").distinct
		end

		## 筛选 状态
    if params[:status].present? && params[:status].to_i != 0
			params[:status] = [0, 1] if params[:status].to_i == 1
      @shixuns = @shixuns.where(status: params[:status])
    end

		## 筛选 难度
    if params[:diff].present? && params[:diff].to_i != 0
      @shixuns = @shixuns.where(trainee: params[:diff])
    end

		## 排序参数
		bsort = params[:sort] || 'desc'
		case params[:order_by] || 'publish_time'
			when 'new'
				@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.created_at #{bsort}")
			when 'hot'
				@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.myshixuns_count #{bsort}")
      when 'mine'
        @shixuns = @shixuns.order("shixuns.created_at #{bsort}")
      else
				@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.publish_time #{bsort}")
		end

		# 用id计数会快10+MS左右,对于搜索的内容随着数据的增加,性能会提升一些。
		@total_count = @shixuns.count("shixuns.id")

		## 分页参数
		page  = params[:page]  || 1
		limit = params[:limit] || 16

		@shixuns = @shixuns.includes(:tag_repertoires, :challenges).page(page).per(limit)

		@tag_name_map = TagRepertoire.joins(:shixun_tag_repertoires)
											.where(shixun_tag_repertoires: { shixun_id: @shixuns.map(&:id) })
											.group('shixun_tag_repertoires.shixun_id')
											.select('shixun_id, tag_repertoires.name')
											.each_with_object({}) { |r, obj| obj[r.shixun_id] = r.name }
  end

	def shixun_list
		# 全部实训/我的实训
		type = params[:type] || "all"
		# 状态:已发布/未发布
		status = params[:status] || "all"

		# 超级管理员用户显示所有未隐藏的实训、非管理员显示所有已发布的实训(对本单位公开且未隐藏未关闭)
		if type == "mine"
			@shixuns = current_user.shixuns.none_closed
		else
			if current_user.admin?
				@shixuns = Shixun.none_closed.where(hidden: 0)
			else
				none_shixun_ids = ShixunSchool.where("school_id != #{current_user.school_id}").pluck(:shixun_id)

				@shixuns = Shixun.where.not(id: none_shixun_ids).none_closed.where(hidden: 0)
			end
		end

		unless status == "all"
			@shixuns = status == "published" ? @shixuns.where(status: 2) : @shixuns.where(status: [0, 1])
		end

		## 筛选 难度
		if params[:diff].present? && params[:diff].to_i != 0
			@shixuns = @shixuns.where(trainee: params[:diff])
		end

		page  = params[:page]  || 1
		limit = params[:limit] || 10
		offset = (page.to_i - 1) * (limit.to_i)
		order = params[:order] || "desc"
		## 搜索关键字创建者、实训名称、院校名称
		keyword = params[:keyword].to_s.strip.presence || '*'

		model_options = {
			index_name: [Shixun],
			model_includes: Shixun.searchable_includes
		}
		model_options.merge(where: { id: @shixuns.pluck(:id) }).merge(order: {"myshixuns_count" => order}).merge(limit: limit, offset: offset)
		model_options.merge(default_options)
		@shixuns = Searchkick.search(keyword, model_options)
		# @shixuns = Shixun.search keyword, where: {id: @shixuns.pluck(:id)}, order: {"myshixuns_count" => order}, limit: limit, offset: offset

		@total_count = @shixuns.total_count
	end

  ## 获取顶部菜单
	def menus
		@repertoires = current_laboratory.shixun_repertoires
	end

	## 实训详情
	def show
		# 当前用户开启的实训
		can_fork = current_user.is_certification_teacher || current_user.admin?
		unless can_fork
			@can_fork = {can_fork: "已经职业认证的教师才能fork实训",
									 certi_url: "/account/certification"}
    end
		@current_myshixun = @shixun.current_myshixun(current_user)
		if @shixun.fork_from
			fork_shixun = Shixun.select(:id, :user_id, :name, :identifier).where(id: @shixun.fork_from).first
			@fork_from = {name: fork_shixun.name, username: fork_shixun.owner.try(:full_name),
										fork_identifier: fork_shixun.identifier} if fork_shixun
		end
		@power = current_user.manager_of_shixun?(@shixun)
		# 更新是为了首页的排序,myshixun的动静需要在实训中展现出来
		if @current_myshixun && params[:exit] && !current_user.admin?
			@current_myshixun.update_column(:updated_at, Time.now)
		end
	end

	def show_right
		owner = @shixun.owner
		#@fans_count = owner.fan_count
		#@followed_count = owner.follow_count
		@user_own_shixuns = owner.shixuns.published.count
	end

	# 排行榜
	def ranking_list
		if @shixun.status == 2
			sql = "
				select m.user_id, u.login, u.lastname, m.updated_at,
					(select sum(cost_time) from games g where g.myshixun_id = m.id) as time,
					(select sum(final_score) from games g where g.myshixun_id = m.id) as score
				from (users u left join myshixuns m on m.user_id = u.id) where m.shixun_id = #{@shixun.id} and m.status = 1
				order by score desc, time asc limit 10
			"
			@myshixuns = Myshixun.find_by_sql(sql)

		end
	end

	#评论
	def discusses
		new_params = params.merge(container_id: @shixun.id, container_type: 'Shixun')
		discusses = ShixunsService.new.shixun_discuss new_params, current_user

		if discusses.present?
			@children_list = discusses[:children_list]
		else
			@children_list = []
		end
	end

	def copy
		ActiveRecord::Base.transaction do
			begin
				@new_shixun = Shixun.new
				@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
																															 "homepage_show","repo_name", "myshixuns_count", "challenges_count",
																															 "can_copy", "created_at", "updated_at")
				@new_shixun.user_id = User.current.id
				@new_shixun.averge_star = 5
				@new_shixun.identifier = generate_identifier Shixun, 8
				@new_shixun.fork_from = @shixun.id
				@new_shixun.save!

				# 同步shixun_info的信息
				if @shixun.shixun_info.present?
					ShixunInfo.create!(shixun_id: @new_shixun.id,
														 description: @shixun.description,
														 evaluate_script: @shixun.evaluate_script)
				end

				# 同步私密版本库
				if @shixun.shixun_secret_repository
					# 源仓库的的私密版本库地址
					repo_name = @shixun.shixun_secret_repository.repo_name
					# 新生成的地址
					fork_repository_name = "#{current_user.login}/secret_#{@new_shixun.identifier}"
					ShixunSecretRepository.create!(shixun_id: @new_shixun.id,
																				 repo_name: "#{fork_repository_name}",
																				 secret_dir_path: @shixun.shixun_secret_repository.secret_dir_path)
					GitService.fork_repository(repo_path: "#{repo_name}.git", fork_repository_path: (fork_repository_name + ".git"))
				end

				# 同步镜像
				if @shixun.mirror_repositories.present?
					@shixun.mirror_repositories.each do |mirror|
						ShixunMirrorRepository.create!(:shixun_id => @new_shixun.id, :mirror_repository_id => mirror.id)
					end
				end
				# 同步技术标签
				@shixun.shixun_tag_repertoires.each do |str|
					ShixunTagRepertoire.create!(:tag_repertoire_id => str.tag_repertoire_id, :shixun_id => @new_shixun.id)
				end

				# 同步配置
				logger.info("########-shixun_service_configs_count: #{@shixun.shixun_service_configs.pluck(:id, :shixun_id)}")
				@shixun.shixun_service_configs.each do |config|
					ShixunServiceConfig.create!(:shixun_id => @new_shixun.id,
																			:cpu_limit => config.cpu_limit,
																			:lower_cpu_limit => config.lower_cpu_limit,
																			:memory_limit => config.memory_limit,
																			:request_limit => config.request_limit,
																			:mirror_repository_id => config.mirror_repository_id)
				end

				# 同步高校限制
				@shixun.shixun_schools.each do |school|
					ShixunSchool.create!(shixun_id: @new_shixun.id, school_id: school.school_id)
				end

				# fork版本库
				logger.info("###########fork_repo_path: ######{@repo_path}")
				project_fork(@new_shixun, @repo_path, current_user.login)

				ShixunMember.create!(:user_id => User.current.id, :shixun_id => @new_shixun.try(:id), :role => 1)

				# 同步复制关卡
				if @shixun.challenges.present?
					@shixun.challenges.each do |challenge|
						new_challenge = Challenge.new
						new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id", "challenge_tags_count")
						new_challenge.user_id = User.current.id
						new_challenge.shixun_id = @new_shixun.id
						new_challenge.save!
						# 同步参考答案
						challenge.challenge_answers.each do |answer|
							new_answer = ChallengeAnswer.new
							new_answer.attributes = answer.attributes.dup.except("id","challenge_id")
							new_answer.challenge_id = new_challenge.id
							new_answer.save!
						end
						if challenge.st == 0  # 评测题
							# 同步测试集
							if challenge.test_sets.present?
								challenge.test_sets.each do |test_set|
									new_test_set = TestSet.new
									new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id")
									new_test_set.challenge_id = new_challenge.id
									new_test_set.save!
								end
							end
							# 同步关卡标签
							challenge_tags = ChallengeTag.where("challenge_id =? and challenge_choose_id is null", challenge.id)
							if challenge_tags.present?
								challenge_tags.each do |challenge_tag|
									ChallengeTag.create!(:challenge_id => new_challenge.id, :name => challenge_tag.try(:name))
								end
							end
						elsif challenge.st == 1  # 选择题
							if challenge.challenge_chooses.present?
								challenge.challenge_chooses.each do |challenge_choose|
									new_challenge_choose = ChallengeChoose.new
									new_challenge_choose.attributes = challenge_choose.attributes.dup.except("id","challenge_id")
									new_challenge_choose.challenge_id = new_challenge.id
									new_challenge_choose.save!
									# 每一题的选项
									if challenge_choose.challenge_questions.present?
										challenge_choose.challenge_questions.each do |challenge_question|
											new_challenge_question = ChallengeQuestion.new
											new_challenge_question.attributes = challenge_question.attributes.dup.except("id","challenge_choose_id")
											new_challenge_question.challenge_choose_id = new_challenge_choose.id
											new_challenge_question.save!
										end
									end
									# 每一题的知识标签
									st_challenge_tags = ChallengeTag.where(:challenge_id => challenge.id, :challenge_choose_id => challenge_choose.id)
									if st_challenge_tags.present?
										st_challenge_tags.each do |st_challenge_tag|
											ChallengeTag.create!(:challenge_id => new_challenge.id, :name => st_challenge_tag.try(:name), :challenge_choose_id => new_challenge_choose.id)
										end
									end
								end
							end
						end
					end
				end

				# 将实训标志为该云上实验室建立
				Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true)
			rescue Exception => e
				uid_logger_error("copy shixun failed ##{e.message}")
				# 删除版本库
				# 删除私密版本库
				GitService.delete_repository(repo_path: "#{fork_repository_name}.git") if @new_shixun.shixun_secret_repository&.repo_name
				GitService.delete_repository(repo_path: @new_shixun.repo_path) if @new_shixun.repo_path
				tip_exception("实训Fork失败")
				raise ActiveRecord::Rollback
			end
		end
	end

	#合作者
	def collaborators
		@user = current_user
		@members = @shixun.shixun_members.includes(:user)
	end

	def fork_list
		@shixuns = Shixun.where(:fork_from => @shixun.id)
		@shixuns_count = @shixuns.count

		## 分页参数
		page  = params[:page]  || 1
		limit = params[:limit] || 20

		@shixuns = @shixuns.page(page).per(limit)
	end

	def new
		@introduction_sample = PlatformSample.where(samples_type: ['introduction', 'knowledge']).pluck([:samples_type, :contents])
		@main_type = shixun_main_type
		@small_type = shixun_small_type
	end

	def create
		# 评测脚本的一些操作
		main_type, sub_type = params[:main_type], params[:small_type]
		mirror =  MirrorScript.where(:mirror_repository_id => main_type)

		identifier = generate_identifier Shixun, 8
		@shixun = Shixun.new(shixun_params)
		@shixun.identifier = identifier
		@shixun.user_id = current_user.id
		@shixun.reset_time, @shixun.modify_time = Time.now, Time.now

		if sub_type.blank?
			shixun_script = mirror.first.try(:script)
		else
			main_mirror = MirrorRepository.find(main_type).type_name
			sub_mirror = MirrorRepository.where(id: sub_type).pluck(:type_name)
			if main_mirror == "Java" && sub_mirror.include?("Mysql")
				shixun_script = mirror.last.try(:script)
			else
				shixun_script = mirror.first.try(:script)
				shixun_script = modify_shixun_script @shixun, shixun_script
			end
		end

		ActiveRecord::Base.transaction do
			begin
				@shixun.save!
				# shixun_info关联ß
				ShixunInfo.create!(shixun_id: @shixun.id, evaluate_script: shixun_script, description: params[:description])
				# 实训的公开范围
				if params[:scope_partment].present?
					arr = []
					ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq
					ids.each do |id|
						arr << { :school_id => id, :shixun_id => @shixun.id }
					end
					ShixunSchool.create!(arr)
				end

				# 实训合作者
				@shixun.shixun_members.create!(user_id: current_user.id, role: 1)

				# 镜像-实训关联表
				ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present?
				# 实训主镜像服务配置
				ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id =>  main_type.to_i)
				if sub_type.present?
					sub_type.each do |mirror|
						ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
						# 实训子镜像服务配置
						name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置
						ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id =>  mirror) if name.present?
					end
				end

				# 创建版本库
				repo_path = repo_namespace(User.current.login, @shixun.identifier)
				GitService.add_repository(repo_path: repo_path)
				# todo: 为什么保存的时候要去除后面的.git呢??
				@shixun.update_column(:repo_name, repo_path.split(".")[0])

				# 将实训标志为该云上实验室建立
				Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true)
			rescue Exception => e
				uid_logger_error(e.message)
				tip_exception("实训创建失败")
				raise ActiveRecord::Rollback
			end
		end
	end

	def apply_shixun_mirror
		form_params = params.permit(*%i[language runtime run_method attachment_id])
		form = ApplyShixunMirrorForm.new(form_params)
		form.validate!

		tiding = Tiding.new(
			user_id: 1,
			trigger_user_id: current_user.id,
			container_type: 'SendMessage',
			viewed: 0,
			tiding_type: 'Apply',
			extra: form.to_json
		)
		ActiveRecord::Base.transaction do
			# TODO: 由于tiding是多态,而SendMessage却没有对应的model,因此,如果不跳过验证会报 container must exist的错.
			tiding.save(validate: false)

			form.attachment.update!(container: tiding)
		end

		sucess_status
	rescue ActiveModel::ValidationError => ex
		tip_exception(ex.message)
	rescue Exception => e
		uid_logger_error(e.message)
		tip_exception("申请失败")
	end

	def update
		ActiveRecord::Base.transaction do
			begin
				@shixun.shixun_mirror_repositories.destroy_all
				if params[:main_type].present?
					ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => params[:main_type].to_i)
				end
				if params[:small_type].present?
					params[:small_type].each do |mirror|
						ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
					end
				end
				@shixun.update_attributes(shixun_params)
				@shixun.shixun_info.update_attributes(shixun_info_params)
				@shixun.shixun_schools.delete_all
				# scope_partment: 高校的名称
				if params[:scope_partment].present?
					arr = []
					ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq
					ids.each do |id|
						arr << { :school_id => id, :shixun_id => @shixun.id }
					end
					ShixunSchool.create!(arr)
				end
				# 超级管理员和运营人员才能保存 中间层服务器pod信息的配置
				# 如果镜像改动了,则也需要更改
				mirror = @shixun.shixun_service_configs.map(&:mirror_repository_id).sort
				new_mirror = params[:shixun_service_configs].map{|c| c[:mirror_repository_id]}.sort
				if current_user.admin? || current_user.business? || (mirror != new_mirror)
					@shixun.shixun_service_configs.destroy_all
					service_config_params[:shixun_service_configs].each do |config|
						name = MirrorRepository.find_by_id(config[:mirror_repository_id])&.name
						# 不保存没有镜像的配置
						@shixun.shixun_service_configs.create!(config) if name.present?
					end
				end
				# 添加第二仓库
				if params[:is_secret_repository]
					add_secret_repository if @shixun.shixun_secret_repository.blank?
				else
					# 如果有仓库,就要删
					if @shixun.shixun_secret_repository&.repo_name
						@shixun.shixun_secret_repository.lock!
						GitService.delete_repository(repo_path: @shixun.shixun_secret_repository.repo_path)
						@shixun.shixun_secret_repository.destroy
					end
				end

			rescue Exception => e
				uid_logger_error("实训保存失败--------#{e.message}")
				tip_exception("实训保存失败")
				raise ActiveRecord::Rollback
			end
		end
	end

	# 永久关闭实训
	def close
		@shixun.update_attributes(status: 3, closer_id: current_user.id, end_time: Time.now)
		sucess_status
	end

	def propaedeutics
		@content = Shixun.find_by_identifier!(params[:identifier]).propaedeutics
	end

	# 更新背景知识
	def update_propaedeutics
		@shixun.shixun_info.update_column(:propaedeutics, params[:content])
	end

	# 获取推荐实训接口 2个热门实训 + 2个最新实训
	def get_recommend_shixuns
		hot_shixuns = Shixun.field_for_recommend.published.order("myshixuns_count desc").limit(2)
		newest_shixuns = Shixun.field_for_recommend.published.order("created_at desc").limit(2)
		@recommend_shixuns = hot_shixuns + newest_shixuns
	end

	def settings
		@choice_main_type =  @shixun.main_mirror_id
		@choice_small_type = @shixun.small_mirror_id
		@main_type = shixun_main_type
		@small_type = shixun_small_type
		@configs = @shixun.shixun_service_configs
		#@mirror_script = MirrorScript.select([:id, :script_type]).find(@shixun.mirror_script_id).attributes if @shixun.mirror_script_id && @shixun.mirror_script_id != 0
		# @shixun_main_mirror = @shixun.show_shixun_mirror
		# @script_type = @shixun.script_tag.try(:script_type) || "无"
		# @evaluate_scirpt = @shixun.evaluate_script || "无"
	end

	# 获取脚本内容
	def get_script_contents
		mirrir_script =  MirrorScript.find(params[:script_id])
		script = mirrir_script.try(:script)
		@description = mirrir_script.try(:description)
		@script = modify_shixun_script @shixun, script
	end

	def get_custom_script
		shixun_script = PlatformSample.where(:samples_type => "script").first.try(:contents)
		compile = params[:compile]
		execute = params[:executive]
		if shixun_script
			shixun_script = compile.blank? ? shixun_script.gsub("COMPILEFUNCTION", "").gsub("CHALLENGEFIELPATH", "") :
											shixun_script.gsub("COMPILEFUNCTION", "#{compile_command}").gsub("COMPILECOMMAND", "#{compile}")
			shixun_script = execute.blank? ? shixun_script.gsub("EXECUTEFUNCTION", "") : shixun_script.gsub("EXECUTECOMMAND", "#{execute}")
			shixun_script = modify_shixun_script @shixun, shixun_script
		end
		@shixun_script = shixun_script
	end

	def departments
		@scope = []
		q = params[:q]
		if q && q.strip.present?
			@scope = School.where("name like ?", "%#{q.strip}%").pluck(:name)
		end

	end

  def get_mirror_script
    mirror = MirrorRepository.find(params[:mirror_id])
    @script = mirror.mirror_scripts
  end

	# TODO: 目前实训只做软删除.
	def destroy
		apply_records = ApplyAction.where(container_id: @shixun.id, container_type: "ApplyShixun")
		apply_records.delete_all if apply_records
		# HomeworkCommonShixuns.where(shixun_id: @shixun).delete_all
		# @shixun.destroy
		@shixun.update_column(:status, -1)
	end

	# 开启挑战
	# 以前在开启挑战的时候检测实训是否更新,更新则重置,觉得应该放在TPI更好
	# 中间需要一个过渡动画
	# TODO: 第一次开启实训都会去判断是否是纯选择题类型,感觉做成在创建关卡的时候就判断该实训是否是纯选择题更加合适
	def shixun_exec
		if is_shixun_opening?
			tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
		end
		current_myshixun = @shixun.current_myshixun(current_user.id)

		min_challenges = @shixun.challenges.pluck(:id , :st)

		Rails.logger.info("11111111112#{current_myshixun.try(:id)}")
		Rails.logger.info("111111111102#{params[:reset] != 1}")

		# 因为读写分离有延迟,所以如果是重置来的请求可以先跳过,重置过来的params[:reset]为1
		if current_myshixun && params[:reset] != "1"
			games = current_myshixun.games
			# 如果TPM和TPI的管卡数不相等或者关卡顺序错了,说明实训被极大的改动,需要重置,实训发布前打过的实训都需要重置
			if is_shixun_reset?(games, min_challenges, current_myshixun)
				# 这里页面弹框要收到 当前用户myshixun的identifier.
				tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
			end

			# 如果存在实训,则直接进入实训
			# 如果实训允许跳关,传参params[:challenge_id]跳入具体的关卡
			@current_task =
					if params[:challenge_id]
						game = games.where(challenge_id: params[:challenge_id]).take
						if @shixun.task_pass || game.status != 3
							game
						else
							current_myshixun.current_task(games)
						end
					else
						current_myshixun.current_task(games)
					end
		else
			# 如果未创建关卡一定不能开启实训,否则TPI没法找到当前的关卡
			if @shixun.challenges_count == 0
				tip_exception("开启实战前请先创建实训关卡")
			end

			# 判断实训是否全为选择题
			is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
			if !is_choice_type
				commit = GitService.commits(repo_path: @repo_path).try(:first)
				uid_logger("First comit########{commit}")
				tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
				commit_id = commit["id"]
			end

			# 如果该实训是金课中的实训,则将当前用户加入到当期开课的课堂
			if StageShixun.exists?(shixun_id: @shixun.id, subject_id: Subject.where(excellent: 1))
				subject = Subject.find_by(id: StageShixun.where(shixun_id: @shixun.id).pluck(:subject_id), excellent: 1)
				course = subject.courses.where("start_date is not null and start_date <= '#{Date.today}' and end_date is not null and end_date >= '#{Date.today}'").take
				if course.present? && !CourseMember.exists?(course_id: course.id, user_id: current_user.id)
					# 为了不影响后续操作,用create而不是create!
					CourseMember.create(course_id: course.id, user_id: current_user.id, role: 4)
					CourseAddStudentCreateWorksJob.perform_later(course.id, [current_user.id])
				end
			end

			ActiveRecord::Base.transaction do
				begin
					cloud_bridge = edu_setting('cloud_bridge')
					myshixun_identifier = generate_identifier Myshixun, 10
					myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
																							 modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
																							 onclick_time: Time.now, commit_id: commit_id, repo_name: (is_choice_type ? "-1" : nil))
					uid_logger("myshixun_id is #{myshixun.id}")


					# 其它创建关卡等操作
					challenges = @shixun.challenges
					# 之所以增加user_id是为了方便统计查询性能
					game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
					Game.bulk_insert(*game_attrs) do |worker|
						base_attr = { myshixun_id: myshixun.id, user_id: myshixun.user_id }
						challenges.each_with_index do |challenge, index|
							status = (index == 0 ? 0 : 3)
							game_identifier = generate_identifier(Game, 12)
							worker.add(base_attr.merge(challenge_id: challenge.id, status: status,
																				 identifier: game_identifier, modify_time: challenge.modify_time))
						end
					end

					# 如果实训是纯选择题,则不需要去fork仓库以及中间层的相关操作了
					unless is_choice_type
						# fork仓库
						project_fork(myshixun, @repo_path, current_user.login)

						rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path )
						uid_logger("start openGameInstance")
						uri = "#{cloud_bridge}/bridge/game/openGameInstance"
						logger.info("end openGameInstance")
						params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: myshixun.repo_name.split("/").last}
						uid_logger("openGameInstance params is #{params}")
						interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)"
					end

					@current_task = myshixun.current_task(myshixun.games)
					uid_logger("## shixun exec: myshixun id is #{myshixun.id}")
				rescue Exception => e
					uid_logger_error(e.message)
					tip_exception("实训云平台繁忙(繁忙等级:81)")
					raise ActiveRecord::Rollback
				end
			end
		end
	end

	# def shixun_exec
	# 	if is_shixun_opening?
	# 		tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
	# 	end
	# 	current_myshixun = @shixun.current_myshixun(current_user.id)
	#
	# 	min_challenges = @shixun.challenges.pluck(:id , :st)
	# 	# 因为读写分离有延迟,所以如果是重置来的请求可以先跳过,重置过来的params[:reset]为1
	# 	if current_myshixun && params[:reset] != "1"
	# 		games = current_myshixun.games
	# 		# 如果TPM和TPI的管卡数不相等或者关卡顺序错了,说明实训被极大的改动,需要重置,实训发布前打过的实训都需要重置
	# 		if is_shixun_reset?(games, min_challenges, current_myshixun)
	# 			# 这里页面弹框要收到 当前用户myshixun的identifier.
	# 			tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
	# 		end
	#
	#
	# 		if current_myshixun.repo_name.nil?
	# 			g = Gitlab.client
	# 			repo_name = g.project(current_myshixun.gpid).try(:path_with_namespace)
	# 			current_myshixun.update_column(:repo_name, repo_name)
	# 		end
	#
	# 		# 如果存在实训,则直接进入实训
	# 		# 如果实训允许跳关,传参params[:challenge_id]跳入具体的关卡
	# 		@current_task =
	# 				if params[:challenge_id]
	# 					game = games.where(challenge_id: params[:challenge_id]).take
	# 					if @shixun.task_pass || game.status != 3
	# 						game
	# 					else
	# 						current_myshixun.current_task(games)
	# 					end
	# 				else
	# 					current_myshixun.current_task(games)
	# 				end
	# 	else
	# 		# 如果未创建关卡一定不能开启实训,否则TPI没法找到当前的关卡
	# 		if @shixun.challenges_count == 0
	# 			tip_exception("开启实战前请先创建实训关卡")
	# 		end
	#
	# 		# 判断实训是否全为选择题
	# 		is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
	# 		if !is_choice_type
	# 			commit = GitService.commits(repo_path: @repo_path).try(:first)
	# 			uid_logger("First comit########{commit}")
	# 			tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
	# 			commit_id = commit["id"]
	# 		end
	#
	# 		begin
	# 			ActiveRecord::Base.transaction do
	# 				begin
	# 					myshixun_identifier = generate_identifier Myshixun, 10
	# 					myshixun_params = {user_id: current_user.id, identifier: myshixun_identifier,
	# 														 modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
	# 														 onclick_time: Time.now, commit_id: commit_id}
	# 					@myshixun = @shixun.myshixuns.create!(myshixun_params)
	# 					# 其它创建关卡等操作
	# 					challenges = @shixun.challenges
	# 					# 之所以增加user_id是为了方便统计查询性能
	# 					game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
	# 					Game.bulk_insert(*game_attrs) do |worker|
	# 						base_attr = {myshixun_id: @myshixun.id, user_id: @myshixun.user_id}
	# 						challenges.each_with_index do |challenge, index|
	# 							status = (index == 0 ? 0 : 3)
	# 							game_identifier = generate_identifier(Game, 12)
	# 							worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
	# 																				 identifier: game_identifier, modify_time: challenge.modify_time))
	# 						end
	# 					end
	# 					@current_task = @myshixun.current_task(@myshixun.games)
	# 				rescue Exception => e
	# 					logger.error("------ActiveRecord::RecordInvalid: #{e.message}")
	# 					raise("ActiveRecord::RecordInvalid")
	# 				end
	# 			end
	# 			# 如果实训是纯选择题,则不需要去fork仓库以及中间层的相关操作了
	# 			ActiveRecord::Base.transaction do
	# 				unless is_choice_type
	# 					# fork仓库
	# 					cloud_bridge = edu_setting('cloud_bridge')
	# 					project_fork(@myshixun, @repo_path, current_user.login)
	# 					rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path)
	# 					uid_logger("start openGameInstance")
	# 					uri = "#{cloud_bridge}/bridge/game/openGameInstance"
	# 					logger.info("end openGameInstance")
	# 					params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last}
	# 					uid_logger("openGameInstance params is #{params}")
	# 					interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)"
	# 				end
	# 			end
	# 		rescue Exception => e
	# 			logger.info("shixun_exec error: #{e.message}")
	# 			if e.message != "ActiveRecord::RecordInvalid"
	# 				logger.error("##########project_fork error #{e.message}")
	# 				@myshixun.destroy!
	# 			end
	# 			raise "实训云平台繁忙(繁忙等级:81)"
	# 		end
	# 	end
	# end

	# gameID 及实训ID
	# status:  0 , 1 申请过, 2,实训关卡路径未填, 3 实训标签未填, 4 实训未创建关卡
	def publish
		@status = 0
		@position = []
		begin
			if @shixun.challenges.count == 0
				@status = 4
			else
				@shixun.challenges.each do |challenge|
					if challenge.challenge_tags.count == 0
						@status = 3
						@position << challenge.position
					end
				end
				unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil)
				if unfinish_challenge.count > 0 && !@shixun.is_choice_type?
					@status = 2
					@pos = []
					unfinish_challenge.each do |challenge|
						@pos << challenge.position
					end
				end
			end
			if @status == 0
				@shixun.update_attributes!(:status => 1)
				apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
				if apply && apply.status == 0
					@status = 0
				else
					ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0)
					#begin
					#	status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员')
					#rescue => e
					#	Rails.logger.error "发送验证码出错: #{e}"
					#end
					@status = 1
				end
			end
		rescue Exception => e
			logger.error("pushlish game #{e}")
		end
	end

	# 设置私密版本库的在tpm中的目录
	def set_secret_dir
		raise("设置路径不能为空") if params[:secret_dir_path].blank?
		raise("请先配置私密版本库") if @shixun.shixun_secret_repository.blank?
		@shixun.shixun_secret_repository.update_attributes(:secret_dir_path => params[:secret_dir_path])
		normal_status("设置成功")
	end

	def secret_repository
		begin
			@repo_path = @shixun.shixun_secret_repository&.repo_path
			@repo_url = repo_url @repo_path
			@trees = GitService.file_tree(repo_path: @repo_path, path: params[:path])
			logger.info("#11@@#@#@#@111#@@@@###{@trees}")
			if @trees
				logger.info("#@@#@#@#@#@@@@###{@trees.try(:count)}")
				@latest_commit = [GitService.commits(repo_path: @repo_path).first]
				Rails.logger.info("########## #{@latest_commit}")
			end
		rescue Exception => e
			logger.error(e.message)
		end
	end

	include GitCommon

	def update_file
		content = params[:content]
		author_name = current_user.real_name
		author_email = current_user.git_mail
		@content = update_file_content content, @repo_path, @path, author_email, author_name, "Edit by browser"
  end

  def add_collaborators
		member_ids = "(" + @shixun.shixun_members.map(&:user_id).join(',') + ")"
		user_name = "%#{params[:user_name].to_s.strip}%"
		school_name = "%#{params[:school_name].to_s.strip}%"
		if user_name.present? || school_name.present?
			@users = User.where("users.id not in #{member_ids} AND users.status = 1 AND
																								 (LOWER(concat(users.lastname, users.firstname)) LIKE ? or users.phone like ?)",
													user_name, user_name)
			@users = @users.joins(user_extension: :school).where("schools.name like '%#{school_name}%'") if params[:school_name].present?
		else
			@users = User.none
		end
		@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
		page = params[:page] || 1
		limit = params[:limit] || 20
		@user_count = @users.count
		@users = @users.page(page).per(limit)

	end

  def shixun_members_added
    raise("user_ids 不能为空!") if params[:user_ids].blank?
    memberships = params[:user_ids]
    memberships.each do |member|
      ShixunMember.create!(:user_id => member, :shixun_id => @shixun.id, :role => 2)
    end
  end

  def change_manager
    # 搜索成员
    if request.get?
      @collaborators = @shixun.shixun_members.where("user_id != #{@shixun.user_id}")
    else
      if params[:user_id]
        man_member = ShixunMember.where(:shixun_id => @shixun.id, :user_id => @shixun.user_id).first
        cha_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id).first
        if man_member && cha_member
          man_member.update_attribute(:role, 2)
          cha_member.update_attribute(:role, 1)
          @shixun.update_attribute(:user_id, cha_member.user_id)
        end
      end
    end
  end

	# 刪除合作者
	def collaborators_delete
		raise("user_id不能为空") if params[:user_id].blank?
		shixun_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id, :role => 2).first
		shixun_member.delete
	end

	# 实训的发送至课堂:搜索课堂
	def search_user_courses
		## 分页参数
		page  = params[:page]  || 1
		limit = params[:limit] || 20
		if params[:search]
			search = "%#{params[:search].to_s.strip.downcase}%"
			course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m
                                      WHERE m.course_id = c.id AND m.role in (1,2,3)
                                      AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end  = 0
                                      AND c.name like '#{search}' ").map(&:id)

		else
			course_ids = Course.find_by_sql("SELECT c.id, c.name FROM courses c, course_members m
                                      WHERE m.course_id = c.id AND m.role in (1,2,3)
                                      AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end  = 0").map(&:id)

		end

		@courses = Course.where(:id => course_ids)
		## 云上实验室过滤
		@courses = @courses.where(id: current_laboratory.all_courses)

		@course_count = @courses.count
		@courses = @courses.page(page).per(limit)
	end

	# 将实训发送到课程
  def send_to_course
    @course = Course.find(params[:course_id])
		homework = HomeworksService.new.create_homework @shixun, @course, nil, current_user
		CreateStudentWorkJob.perform_later(homework.id)
	end

	# 批量发送
	def batch_send_to_course
		@course = Course.find_by!(id: params[:course_id])
		shixuns = Shixun.where(id: params[:shixun_ids]).unhidden
		shixuns.each do |shixun|
			homework = HomeworksService.new.create_homework shixun, @course, nil, current_user
			CreateStudentWorkJob.perform_later(homework.id)
		end
	end

	# 二维码扫描下载
	def download_file
		file_path = params[:file_name]
		send_file "#{Rails.root}/#{file_path}", :filename => "#{file_path}",
							:type => 'shixun',
							:disposition => 'attachment'    #inline can open in browser
	end

	# 撤销发布
	def cancel_publish
		tip_exception("实训已经发布,无法撤销") if @shixun.status == 2
		apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
		if apply && apply.status == 0
			apply.update_attribute(:status, 3)
			apply.tidings.destroy_all
		end
		@shixun.update_column(:status, 0)
	end

	# 创建实训审核
	def review_shixun
		validate_review_shixun_params
		# 没有记录就创建记录, 如果有记录就
		@shixun.shixun_reviews.create!(user_id: current_user.id, status: params[:status],
																	 review_type: params[:review_type], evaluate_content: params[:evaluate_content])
		normal_status("审核完成")
	end

	# 实训审核最新记录
	def review_newest_record
		@content_record = @shixun.shixun_reviews.where(review_type: "Content").first
		@perfer_record = @shixun.shixun_reviews.where(review_type: "Performance").first
	end

private
	def shixun_params
		raise("实训名称不能为空") if params[:shixun][:name].blank?
		params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
																	 :task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
																	 :hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission)
	end

	def validate_review_shixun_params
		tip_exception("只有平台管理员或运营人员才能审核") if !admin_or_business?
		tip_exception("审核类型参数不对") unless ["Content", "Performance"].include?(params[:review_type])
	end

	def shixun_info_params
		raise("实训描述不能为空") if params[:shixun_info][:description].blank?
		raise("评测脚本不能为空") if params[:shixun_info][:evaluate_script].blank?
		params.require(:shixun_info).permit(:description, :evaluate_script)
	end


	def service_config_params
		params.permit(shixun_service_configs: [:cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id])
	end

	def find_shixun
		@shixun = Shixun.find_by_identifier(params[:identifier])
		if @shixun.blank?
			normal_status(404, "...")
			return
		end
	end

	def find_repo_name
		# 有私密版本库的参数时,需要拿私密仓库
		@repo_path = if params[:secret_repository]
									@shixun.shixun_secret_repository&.repo_path
								else
									@shixun.try(:repo_path)
								 end
		logger.info("######{@repo_path}")
		@path = params[:path]
	end

	def allowed
		unless current_user.manager_of_shixun?(@shixun)
			tip_exception(403, "..")
		end
	end

	def portion_allowed
		if current_user.shixun_identity(@shixun) > User::EDU_CERTIFICATION_TEACHER
      raise Educoder::TipException.new(403, "..")
		end
	end

	def special_allowed
		if @shixun.status != 2
			tip_exception(403, "..")
		end
	end

	# 实训是否需要开启
	def is_shixun_opening?
		@shixun.opening_time.present? &&
				@shixun.opening_time > Time.now &&
				current_user.shixun_identity(@shixun) > User::EDU_SHIXUN_MEMBER
	end

	# 实训是否需要重置
	def is_shixun_reset?(games, min_challenges, current_myshixun)
		# 用户在申请发布之前,是否玩过实训 TODO: 重置的字段应该迁移到myshixuns表比较合适
		modify_shixun = ShixunModify.exists?(:myshixun_id => current_myshixun.id, :shixun_id => @shixun.id, :status => 1)
		games.size != min_challenges.size || modify_shixun
	end

	# 添加私密仓库
	def add_secret_repository
		# 防止跟tpm版本库重名,加了前缀secret
		repo_path = repo_namespace(current_user.login, "secret_#{@shixun.identifier}")
		GitService.add_repository(repo_path: repo_path)
		ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id)
	end

end