namespace :polls do
  desc "Removes duplicate poll voters"
  task remove_duplicate_voters: :environment do
    logger = ApplicationLogger.new
    duplicate_records_logger = DuplicateRecordsLogger.new

    logger.info "Removing duplicate voters in polls"

    Tenant.run_on_each do
      duplicate_ids = Poll::Voter.select(:user_id, :poll_id)
                                 .group(:user_id, :poll_id)
                                 .having("count(*) > 1")
                                 .pluck(:user_id, :poll_id)

      duplicate_ids.each do |user_id, poll_id|
        voters = Poll::Voter.where(user_id: user_id, poll_id: poll_id)
        voters.excluding(voters.first).each do |voter|
          voter.delete

          tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?
          log_message = "Deleted duplicate record with ID #{voter.id} " \
                        "from the #{Poll::Voter.table_name} table " \
                        "with user_id #{user_id} " \
                        "and poll_id #{poll_id}" + tenant_info.to_s
          logger.info(log_message)
          duplicate_records_logger.info(log_message)
        end
      end
    end
  end

  desc "Removes duplicate poll answers"
  task remove_duplicate_answers: :environment do
    logger = ApplicationLogger.new
    duplicate_records_logger = DuplicateRecordsLogger.new

    logger.info "Removing duplicate answers in polls"

    Tenant.run_on_each do
      Poll::Question.find_each do |question|
        manageable_titles = PollOptionFinder.new(question).manageable_choices.keys

        question.question_options.each do |option|
          titles = option.translations.where(title: manageable_titles).select(:title).distinct

          author_ids = question.answers
                               .where(answer: titles)
                               .select(:author_id)
                               .group(:author_id)
                               .having("count(*) > 1")
                               .pluck(:author_id)

          author_ids.each do |author_id|
            poll_answers = question.answers.where(option_id: nil, answer: titles, author_id: author_id)

            poll_answers.excluding(poll_answers.first).each do |poll_answer|
              poll_answer.delete

              tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?
              log_message = "Deleted duplicate record with ID #{poll_answer.id} " \
                            "from the #{Poll::Answer.table_name} table " \
                            "with question_id #{question.id}, " \
                            "author_id #{author_id} " \
                            "and answer #{poll_answer.answer}" + tenant_info.to_s
              logger.info(log_message)
              duplicate_records_logger.info(log_message)
            end
          end
        end
      end
    end
  end

  desc "populates the poll answers option_id column"
  task populate_option_id: :remove_duplicate_answers do
    logger = ApplicationLogger.new
    logger.info "Updating option_id column in poll_answers"

    Tenant.run_on_each do
      questions = Poll::Question.select do |question|
        # Change this if you've added a custom votation type
        # to your Consul Democracy installation that implies
        # choosing among a few given options
        question.unique? || question.multiple?
      end

      questions.each do |question|
        option_finder = PollOptionFinder.new(question)

        option_finder.manageable_choices.each do |choice, ids|
          question.answers.where(option_id: nil, answer: choice).update_all(option_id: ids.first)
        end

        option_finder.unmanageable_choices.each do |choice, ids|
          tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?

          if ids.count == 0
            logger.warn "Skipping poll_answers with the text \"#{choice}\" for the poll_question " \
                        "with ID #{question.id}. This question has no poll_question_answers " \
                        "containing the text \"#{choice}\"" + tenant_info.to_s
          else
            logger.warn "Skipping poll_answers with the text \"#{choice}\" for the poll_question " \
                        "with ID #{question.id}. The text \"#{choice}\" could refer to any of these " \
                        "IDs in the poll_question_answers table: #{ids.join(", ")}" + tenant_info.to_s
          end
        end
      end
    end
  end

  desc "Removes duplicate poll partial results"
  task remove_duplicate_partial_results: :environment do
    logger = ApplicationLogger.new
    duplicate_records_logger = DuplicateRecordsLogger.new

    logger.info "Removing duplicate partial results in polls"

    Tenant.run_on_each do
      Poll::Question.find_each do |question|
        manageable_titles = PollPartialResultOptionFinder.new(question).manageable_choices.keys

        question.question_options.each do |option|
          titles = option.translations.where(title: manageable_titles).select(:title).distinct

          groups = question.partial_results.where(option_id: nil, answer: titles)
                           .select(:booth_assignment_id, :date)
                           .group(:booth_assignment_id, :date)
                           .having("count(*) > 1")
                           .pluck(:booth_assignment_id, :date)

          groups.each do |booth_assignment_id, date|
            partial_results = question.partial_results.where(
              option_id: nil,
              booth_assignment_id: booth_assignment_id,
              date: date,
              answer: titles
            )

            tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?

            amounts_by_id = partial_results.pluck(:id, :amount).to_h
            if amounts_by_id.values.uniq.size > 1
              log_message = "Found duplicate partial results with different amounts " \
                            "for question_id #{question.id}, " \
                            "booth_assignment_id #{booth_assignment_id} " \
                            "and date #{date}. " \
                            "Keeping ID #{partial_results.first.id} " \
                            "with amount #{partial_results.first.amount}. " \
                            "Deleting partial results with these IDs and amounts: " \
                            "#{amounts_by_id.except(partial_results.first.id)}" + tenant_info.to_s
              logger.info(log_message)
              duplicate_records_logger.info(log_message)
            end

            partial_results.excluding(partial_results.first).each do |partial_result|
              partial_result.delete

              log_message = "Deleted duplicate record with ID #{partial_result.id} " \
                            "from the #{Poll::PartialResult.table_name} table " \
                            "with question_id #{question.id}, " \
                            "booth_assignment_id #{booth_assignment_id} " \
                            "and date #{date}" + tenant_info.to_s
              logger.info(log_message)
              duplicate_records_logger.info(log_message)
            end
          end
        end
      end
    end
  end

  desc "populates the poll_partial_results option_id column"
  task populate_partial_results_option_id: :remove_duplicate_partial_results do
    logger = ApplicationLogger.new
    logger.info "Updating option_id column in poll_partial_results"

    Tenant.run_on_each do
      Poll::Question.find_each do |question|
        option_finder = PollPartialResultOptionFinder.new(question)

        option_finder.manageable_choices.each do |choice, ids|
          question.partial_results.where(option_id: nil, answer: choice).update_all(option_id: ids.first)
        end

        option_finder.unmanageable_choices.each do |choice, ids|
          tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?

          if ids.count == 0
            logger.warn "Skipping poll_partial_results with the text \"#{choice}\" for the poll_question " \
                        "with ID #{question.id}. This question has no poll_question_answers " \
                        "containing the text \"#{choice}\"" + tenant_info.to_s
          else
            logger.warn "Skipping poll_partial_results with the text \"#{choice}\" for the poll_question " \
                        "with ID #{question.id}. The text \"#{choice}\" could refer to any of these " \
                        "IDs in the poll_question_answers table: #{ids.join(", ")}" + tenant_info.to_s
          end
        end
      end
    end
  end
end
