class Play < ActiveRecord::Base
  belongs_to :game, foreign_key: 'game_id'
  belongs_to :player, foreign_key: 'player_id'
  has_many :player_cards, foreign_key: 'play_id'
  alias_attribute :hand, :player_cards

  validates :game, presence: true
  validates :player, presence: true
  validate :hand_valid?
  validate :player_has_control?

  after_save :check_for_winner
  after_save :update_play_to_beat
  after_save :set_controlling_player

  def check_for_winner
   if self.game.over?
     self.game.winner_player_id = self.player.id
     self.game.status = "Player #{self.player.display_name} wins!"
     self.game.save
   end
  end

  def update_play_to_beat
    if hand_valid?
      @pb = PlayToBeat.new
      @pb.game = self.game
      @pb.play = self
      @pb.save
      self.game.play_to_beat = @pb
      self.game.save!
    end
  end

  def set_controlling_player
    self.game.set_controlling_player 'next'
    self.game.save
  end

  # Validation methods

  def player_has_control?
    if self.player == self.game.controlling_player
      return true
    else
      errors.add(:player, "it's not your turn")
      return false
    end
  end

  def hand_valid?
    return false if self.hand_type == nil
    # Gather the truths about the hand
    @truths = []

    # Validations

    # Is there a hand_to_beat ?
    if !game.try(:hand_to_beat).blank?
      # current player is not the player to beat
      if (game.player_to_beat != player)
        # valid bomb?
        # if (self.hand_type =~ /bomb|double run/ && game.play_to_beat.try("play").try("hand_type") == 'single' && game.play_to_beat.try("play").try("player_cards")[0].try('rank') == '2')
        if (self.hand_type =~ /bomb|double run/)
          @truths << true
          return @truths
        else
          # hand_type match?
          @truths << (self.hand_type == game.play_to_beat.try("play").try("hand_type")) if game.play_to_beat.try("play").try(:hand_type)
          # beats?
          @truths << (self.beats? self.game.play_to_beat.try(:play))
        end
      end
    end

    if has_player_cards

      @truths << !hand_type.nil?

      if first_play?
        @truths << contains_lowest_card?
      end

    end
    if @truths.include? false
      errors.add(:player_cards, "invalid hand")
    end
    # Did any validations return false?
    return (!@truths.include? false)
  end

  # Check if the value of this hand is higher than the hand_to_beat
  def beats?(play_to_beat)
    if play_to_beat
      self.high_card_value > play_to_beat.high_card_value
    else
      false
    end
  end

  def high_card
    self.hand.last unless self.hand.empty?
  end

  def high_card_value
    return self.hand.last.value unless self.hand.empty?
    0
  end

  # TODO: Recognize face cards
  def hand_type
    case self.player_cards.length
    when 0
      @hand_type = 'empty'
    when 1
      @hand_type = 'single'
    when 2
      @hand_type = 'double'
      if (self.player_cards[0].rank != self.player_cards[1].rank)
        errors.add(:player_cards, "Invalid pair. Rank must match.")
      end
    when 3
      @hand_type = 'triple'
      @ranks = Array.new << self.player_cards.map {|pc| pc.rank}
      @ranks.flatten!
      @suits = Array.new << self.player_cards.map {|pc| pc.suit}
      @suits.flatten!
      # TODO: Does not work for face cards :(
      if (@ranks[0].to_i + 1 == @ranks[1].to_i) && (@ranks[1].to_i + 1 == @ranks[2].to_i)
        @hand_type += ' run'
        # Check if suited
        if (@suits.uniq.length == 1)
          @hand_type += ' suited'
        end
      elsif @ranks.uniq.length != 1
        errors.add(:player_cards, "Invalid triple. Rank must match.")
      end
    when 4
      @ranks = Array.new << self.player_cards.map {|pc| pc.rank}
      @ranks.flatten!
      @suits = Array.new << self.player_cards.map {|pc| pc.suit}
      @suits.flatten!

      if (@ranks.uniq.length == 1)
        @hand_type = 'bomb'
      end

      # TODO: Does not work for face cards :(
      if (@ranks[0].to_i + 1 == @ranks[1].to_i) && (@ranks[1].to_i + 1 == @ranks[2].to_i) & (@ranks[2].to_i + 1 == @ranks[3].to_i)
        @hand_type = 'run of 4'
      end

      if (@suits.uniq.length == 1)
        @hand_type += ' suited'
      end
    when 5..13
      @hand_type = "run of #{self.player_cards.length}"

      @ranks = [self.player_cards.map {|pc| pc.rank}].flatten!.sort_by(&:to_i)
      @suits = [self.player_cards.map {|pc| pc.suit}].flatten!

      # double? && length?
      if (@ranks.uniq.length == (@ranks.length/2)) && ([6,8,10].include? @ranks.length)
        @hand_type = "double " + @hand_type
        # run?
        # iterate over each unique rank; except for the last rank
        @ranks.uniq.sort_by(&:to_i)[0...-1].each.with_index do |rank, i|
          # each rank one away from each other?
          if (rank.to_i + 1) == @ranks.uniq.sort_by(&:to_i)[i+1].to_i
            # valid do nothing
          else
            @hand_type = "Invalid " + @hand_type
            errors.add(:player_cards, "Invalid double run of #{@ranks.length}")
          end
        end
      else
        # normal run?
        # For each card except for the last card
        @ranks.sort_by(&:to_i)[0...-1].each_with_index do |rank, i|
          # If cards are not a single run
          if (rank.to_i + 1) != @ranks[i+1].to_i
            # And if cards are not a run
            errors.add(:player_cards, "Invalid run of #{@ranks.length}")
          end
        end
      end

      # Check if suited
      if (@suits.uniq.length == 1)
        @hand_type += ' suited'
      end

    else
      errors.add(:player_cards, "Unrecognized hand type.")
    end
    return @hand_type
  end

  def first_play?
    # First play of the game
    if self.game.try(:plays).try(:empty?) || ( self.game.try(:plays).try(:length) == 1 && self.game.try(:plays).try(:first) == self )
      true
    else
      false
    end
  end

  def contains_lowest_card?
    # Cards being played must include the lowest card
    if (self.player_cards.first.value != self.game.lowest_card.value)
      errors.add(:player_cards, "First hand must contain the lowest card: #{game.lowest_card.to_s}")
      return false
    else
      return true
    end
  end

  def has_player_cards
    true unless self.player_cards.empty?
  end

  def to_s
    player_cards.join ' '
  end

end
