class Tournament < ApplicationRecord

	belongs_to :user, optional: true
	has_many :schools, dependent: :destroy
	has_many :weights, dependent: :destroy
	has_many :mats, dependent: :destroy
	has_many :wrestlers, through: :weights
	has_many :matches, dependent: :destroy
	has_many :delegates, class_name: "TournamentDelegate", dependent: :destroy
	has_many :mat_assignment_rules, dependent: :destroy
	has_many :tournament_backups, dependent: :destroy
	has_many :tournament_job_statuses, dependent: :destroy
	
	validates :date, :name, :tournament_type, :address, :director, :director_email , presence: true

	attr_accessor :import_text

	def self.search_date_name(pattern)
		if pattern.blank?  # blank? covers both nil and empty string
			all
		else
			search_functions = []
			search_variables = []
			search_terms = pattern.split(' ').map{|word| "%#{word.downcase}%"}
			search_terms.each do |word|
				search_functions << '(LOWER(name) LIKE ? or LOWER(date) LIKE ?)'
				# add twice for both ?'s in the function above
				search_variables << word
				search_variables << word
			end
			like_patterns = search_functions.join(' and ')
			# puts "where(#{like_patterns})"
			# puts *search_variables
			# example: (LOWER(name LIKE ? or LOWER(date) LIKE ?) and (LOWER(name) LIKE ? or LOWER(date) LIKE ?), %test%, %test%, %2016%, %2016%
			where("#{like_patterns}", *search_variables)
		end
	end

	def days_until_start
		time = (Date.today - self.date).to_i
		if time < 0
			time = time * -1
		end
		time
	end

	def tournament_types
		["Pool to bracket","Modified 16 Man Double Elimination 1-6","Modified 16 Man Double Elimination 1-8","Regular Double Elimination 1-6","Regular Double Elimination 1-8"]
	end
	
	def number_of_placers
		if self.tournament_type.include? "1-8"
			return 8
		elsif self.tournament_type.include? "1-6"
		  return 6
		end
	end
	
	def calculate_all_team_scores
		self.schools.each do |school|
      school.calculate_score
    end
	end
	
	def create_pre_defined_weights(weight_classes)
		weights.destroy_all
		weight_classes.each do |w|
			weights.create(max: w)
		end
	end

	def up_matches_unassigned_matches
		matches
			.where("mat_id is NULL and (finished != 1 or finished is NULL)")
			.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
			.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
			.order("bout_number ASC")
			.limit(10)
			.includes({ wrestler1: :school }, { wrestler2: :school }, { weight: :matches })
	end

	def up_matches_mats
		mats.includes(:matches)
	end

	def self.broadcast_up_matches_board(tournament_id)
		tournament = find_by(id: tournament_id)
		return unless tournament

		Turbo::StreamsChannel.broadcast_replace_to(
			tournament,
			target: "up_matches_board",
			partial: "tournaments/up_matches_board",
			locals: { tournament: tournament }
		)
	end

	def destroy_all_matches
		matches.destroy_all
		mats.each(&:clear_queue!)
	end

	def matches_by_round(round)
		matches.joins(:weight).where(round: round).order("weights.max")
	end
	
	def total_rounds
		# Assuming this is line 147 that's causing the error
		matches.maximum(:round) || 0  # Return 0 if no matches or max round is nil
	end
	
	def reset_mats
		matches.reload
		mats.reload
		matches_to_reset = matches.select{|m| m.mat_id != nil}
		# matches_to_reset.update_all( {:mat_id => nil } )
		matches_to_reset.each do |m|
			m.mat_id = nil
			m.save
		end
		mats.each do |mat|
			mat.clear_queue!
		end
	end
	
	def pointAdjustments
	  school_scope = Teampointadjust.where(school_id: schools.select(:id))
	  wrestler_scope = Teampointadjust.where(wrestler_id: wrestlers.select(:id))

	  Teampointadjust.includes(:school, :wrestler)
	               .merge(school_scope.or(wrestler_scope))
    end
    
    def remove_school_delegations
	    self.schools.each do |s|
	      s.delegates.each do |d|
	        d.destroy
	      end
	    end
  	end
  	
  	def pool_to_bracket_number_of_wrestlers_error
  		error_string = ""
      if self.tournament_type.include? "Pool to bracket"
       	weights_with_too_many_wrestlers = weights.select{|w| w.wrestlers.size > 24}
       	weight_with_too_few_wrestlers = weights.select{|w| w.wrestlers.size < 2}
       	weights_with_too_many_wrestlers.each do |weight|
       		error_string = error_string + " The weight class #{weight.max} has more than 24 wrestlers."
       	end
       	weight_with_too_few_wrestlers.each do |weight|
       		error_string = error_string + " The weight class #{weight.max} has less than 2 wrestlers."
       	end
      end
      return error_string
  	end

  	def modified_sixteen_man_number_of_wrestlers_error
  		error_string = ""
        if self.tournament_type.include? "Modified 16 Man Double Elimination"
        	weights_with_too_many_wrestlers = weights.select{|w| w.wrestlers.size > 16}
        	weight_with_too_few_wrestlers = weights.select{|w| w.wrestlers.size < 12}
        	weights_with_too_many_wrestlers.each do |weight|
        		error_string = error_string + " The weight class #{weight.max} has more than 16 wrestlers."
        	end
        	weight_with_too_few_wrestlers.each do |weight|
        		error_string = error_string + " The weight class #{weight.max} has less than 12 wrestlers."
        	end
        end
        return error_string
  	end

  	def double_elim_number_of_wrestlers_error
  		error_string = ""
        if self.tournament_type == "Regular Double Elimination 1-6" or self.tournament_type == "Regular Double Elimination 1-8"
        	weights_with_too_many_wrestlers = weights.select{|w| w.wrestlers.size > 64}
        	weight_with_too_few_wrestlers = weights.select{|w| w.wrestlers.size < 2}
        	weights_with_too_many_wrestlers.each do |weight|
        		error_string = error_string + " The weight class #{weight.max} has more than 64 wrestlers."
        	end
        	weight_with_too_few_wrestlers.each do |weight|
        		error_string = error_string + " The weight class #{weight.max} has less than 2 wrestlers."
        	end
        end
        return error_string
    end

	def wrestlers_with_higher_seed_than_bracket_size_error
		error_string = ""
		weights.each do |weight|
			weight.wrestlers.each do |wrestler|
				if wrestler.original_seed != nil && wrestler.original_seed > weight.wrestlers.size
					error_string += "Wrestler: #{wrestler.name} has a seed of #{wrestler.original_seed} which is greater than the amount of wrestlers (#{weight.wrestlers.size}) in the weight class #{weight.max}."
				end
			end
		end
		return error_string
	end

	def wrestlers_with_duplicate_original_seed_error
		error_string = ""
		weights.each do |weight|
			weight.wrestlers.select{|wr| wr.original_seed != nil}.each do |wrestler|
				if weight.wrestlers.select{|wr| wr.original_seed == wrestler.original_seed}.size > 1
					error_string += "More than 1 wrestler in the #{weight.max} weight class is seeded #{wrestler.original_seed}."
				end
			end
		end
		return error_string
	end
  	
	def wrestlers_with_out_of_order_seed_error
		error_string = ""
		weights.each do |weight|
		  original_seeds = weight.wrestlers.map(&:original_seed).compact.sort
		  if original_seeds.any? && original_seeds != (original_seeds.first..original_seeds.last).to_a
			error_string += "The weight class #{weight.max} has wrestlers with out-of-order seeds: #{original_seeds}. There is a gap in the sequence."
		  end
		end
		return error_string
	end
	  
	def match_generation_error
		error_string = ""
		if pool_to_bracket_number_of_wrestlers_error.length > 0
		  error_string += pool_to_bracket_number_of_wrestlers_error
		elsif modified_sixteen_man_number_of_wrestlers_error.length > 0
		  error_string += modified_sixteen_man_number_of_wrestlers_error
		elsif double_elim_number_of_wrestlers_error.length > 0
		  error_string += double_elim_number_of_wrestlers_error
		elsif wrestlers_with_higher_seed_than_bracket_size_error.length > 0
		  error_string += wrestlers_with_higher_seed_than_bracket_size_error
		elsif wrestlers_with_duplicate_original_seed_error.length > 0
		  error_string += wrestlers_with_duplicate_original_seed_error
		elsif wrestlers_with_out_of_order_seed_error.length > 0
		  error_string += wrestlers_with_out_of_order_seed_error
		end
		if error_string.length > 0
		  return "There is a tournament error. #{error_string}"
		else
		  return nil
		end
	end	  

	def reset_and_fill_bout_board
		reset_mats
		matches.reload
		refill_open_bout_board_queues
	end

	def refill_open_bout_board_queues
		return unless mats.any?

		loop do
			assigned_any = false
			# Fill in round-robin order by queue depth:
			# all mats queue1 first, then queue2, then queue3, then queue4.
			(1..4).each do |slot|
				mats.reload.each do |mat|
					next unless mat.public_send("queue#{slot}").nil?
					assigned_any ||= mat.assign_next_match
				end
			end
			break unless assigned_any
		end
	end

	def create_backup()
		TournamentBackupService.new(self, "Manual backup").create_backup
	end	  

	def confirm_all_weights_have_original_seeds
	  error_string = wrestlers_with_higher_seed_than_bracket_size_error
	  error_string += wrestlers_with_duplicate_original_seed_error
	  error_string += wrestlers_with_out_of_order_seed_error
	  
	  return error_string.blank?
	end

	def confirm_each_weight_class_has_correct_number_of_wrestlers
	  error_string = pool_to_bracket_number_of_wrestlers_error
	  error_string += modified_sixteen_man_number_of_wrestlers_error
	  error_string += double_elim_number_of_wrestlers_error
	  
	  return error_string.blank?
	end
	  
	# Check if there are any active jobs for this tournament
	def has_active_jobs?
	  tournament_job_statuses.active.exists?
	end
	
	# Get all active jobs for this tournament
	def active_jobs
	  tournament_job_statuses.active
	end
	
	private
	
	def connection_adapter
	  ActiveRecord::Base.connection.adapter_name
	end
end
