# frozen_string_literal: true

Given /^I create the gift exchange "([^\"]*)" with the following options$/ do |name, table|
  # Set up the gift exchange with the correct owner.
  step %{the user "moderator" exists and is activated}
  user = User.find_by(login: "moderator")
  collection = Collection.new(
    name: name,
    title: name,
    challenge_type: "GiftExchange"
  )

  collection.collection_participants.build(
    pseud_id: user.default_pseud.id,
    participant_role: "Owner"
  )

  # Create the new collection.
  collection.challenge = GiftExchange.new
  collection.challenge.request_restriction = PromptRestriction.new
  collection.challenge.offer_restriction = PromptRestriction.new
  collection.challenge.potential_match_settings = PotentialMatchSettings.new

  potential_match_settings = collection.challenge.potential_match_settings

  # Go through each of the types, and set up the request/offer/matching
  # requirements accordingly.
  table.hashes.each do |hash|
    # Get the type (prompt/fandom/character/etc.) that we're restricting.
    type = hash["value"].downcase.singularize

    # Constraints on the prompts.
    required = (hash["minimum"] || "0").to_i
    allowed = (hash["maximum"] || "0").to_i
    any = %w(y t yes true).include?(hash["any"] || "yes")
    unique = %w(y t yes true).include?(hash["unique"] || "yes")
    optional = %w(y t yes true).include?(hash["optional"] || "no")

    # Constraints on the matching.
    match = (hash["match"] || hash["minimum"] || "0").to_i

    if type == "prompt"
      # Prompts aren't a type of tag, so we have to set them up differently.
      collection.challenge.requests_num_required = required
      collection.challenge.requests_num_allowed = allowed
      collection.challenge.offers_num_required = required
      collection.challenge.offers_num_allowed = allowed
      potential_match_settings.num_required_prompts = match
    else
      attributes = {
        "#{type}_num_required" => required,
        "#{type}_num_allowed" => allowed,
        "allow_any_#{type}" => any,
        "require_unique_#{type}" => unique
      }

      attributes["optional_tags_allowed"] = true if optional

      # We treat requests and offers identically, in order to make the
      # constraints simpler to express. (The tests are designed to verify
      # potential match generation, not challenge signups, so we don't need
      # that kind of fine-grained control.)
      collection.challenge.request_restriction.update!(attributes)
      collection.challenge.offer_restriction.update!(attributes)

      potential_match_settings.update_attribute(
        "num_required_#{type.pluralize}", match
      )

      potential_match_settings.update_attribute(
        "include_optional_#{type.pluralize}", optional
      )
    end
  end

  # Save all the things!
  collection.save!
  collection.challenge.save!
  collection.challenge.request_restriction.save!
  collection.challenge.offer_restriction.save!
  collection.challenge.potential_match_settings.save!
end

Given /^the user "([^\"]*)" signs up for "([^\"]*)" with the following prompts$/ do |user_name, collection_name, table|
  # Set up the username.
  step %{the user "#{user_name}" exists and is activated}
  user = User.find_by(login: user_name).default_pseud

  # Set up the basics of the signup.
  collection = Collection.find_by(name: collection_name)
  signup = ChallengeSignup.new(pseud: user, collection: collection)
  offers = []
  requests = []

  table.hashes.each do |hash|
    # Each row of the table is a prompt -- either a request, or an offer.
    prompt_type = hash["type"].downcase
    prompt = prompt_type.classify.constantize.new(
      collection: collection,
      pseud: user
    )

    tagset = TagSet.new
    optional_tag_set = nil
    TagSet::TAG_TYPES.each do |type|
      optional = hash["optional #{type}"] || hash["optional #{type.pluralize}"]
      unless optional.nil?
        optional_tag_set ||= TagSet.new

        tag_names = optional.split(/ *, */)
        tag_names.each do |tag_name|
          tag = type.classify.constantize.create_canonical(tag_name)
          optional_tag_set.tags << tag
        end
      end

      value = hash[type] || hash[type.pluralize]
      next if value.nil?
      value = value.downcase

      # Allow the test to specify "any" for the type.
      if value == "any"
        prompt.update_attribute("any_#{type}", true)
      else
        tag_names = value.split(/ *, */)
        tag_names.each do |tag_name|
          tag = type.classify.constantize.create_canonical(tag_name)
          tagset.tags << tag
        end
      end
    end

    tagset.save!
    prompt.tag_set = tagset

    optional_tag_set.save! if optional_tag_set
    prompt.optional_tag_set = optional_tag_set

    requests << prompt if prompt_type == "request"
    offers << prompt if prompt_type == "offer"
  end

  # Save the final signup.
  signup.requests = requests
  signup.offers = offers
  signup.save!
end

When /^potential matches are generated for "([^\"]*)"$/ do |name|
  collection = Collection.find_by(name: name)
  PotentialMatch.generate_in_background collection.id
end

Then /^there should be no potential matches for "([^\"]*)"$/ do |name|
  collection = Collection.find_by(name: name)
  collection.potential_matches.count.should == 0
end

Then /^the potential matches for "([^\"]*)" should be$/ do |name, table|
  # First extract the set of potential matches for the given challenge.
  collection = Collection.find_by(name: name)
  matches = collection.potential_matches.includes(
    request_signup: :pseud,
    offer_signup: :pseud
  )

  match_names = matches.map do |pm|
    [pm.request_signup.pseud.name, pm.offer_signup.pseud.name]
  end

  match_names.sort!

  # Next extract the set of potential matches from the table.
  desired_names = []
  table.hashes.each do |hash|
    desired_names << [hash["request"], hash["offer"]]
  end

  desired_names.sort!

  match_names.should == desired_names
end
