# frozen_string_literal: true

#  Copyright (c) 2012-2024, Jungwacht Blauring Schweiz. This file is part of
#  hitobito and licensed under the Affero General Public License version 3
#  or later. See the COPYING file at the top-level directory or at
#  https://github.com/hitobito/hitobito.

class Event::ApplicationMarketController < ApplicationController
  before_action :authorize

  decorates :event, :participants, :participation, :group

  helper_method :event

  def index
    @participants = load_participants
    @applications = sort_and_decorate(load_applications)
  end

  def add_participant
    return render "maximum_participations_reached_error" if event.maximum_participants_reached?

    if assigner.createable?
      assigner_add_participant
    else
      render "participation_exists_error", status: :unprocessable_entity
    end
  end

  delegate :remove_participant, to: :assigner

  def put_on_waiting_list
    application.update!(waiting_list: true,
      waiting_list_comment: params[:event_application][:waiting_list_comment])
    render "waiting_list"
  end

  def remove_from_waiting_list
    application.update_column(:waiting_list, false) # rubocop:disable Rails/SkipsModelValidations
    render "waiting_list"
  end

  private

  def assigner_add_participant
    assigner.add_participant
  end

  def load_participants
    event.participations_for(*event.participant_types)
      .includes(:application)
      .select(*Event::Participation.column_names)
  end

  def load_applications
    applications = Event::Participation
      .includes(:application, :event)
      .references(:application)
      .where(filter_applications)
      .merge(Event::Participation.pending)
      .distinct

    Event::Participation::PreloadParticipations.preload(applications)

    applications
  end

  def sort_and_decorate(applications)
    sort_applications(applications)
    Event::ParticipationDecorator.decorate_collection(applications)
  end

  def sort_applications(applications)
    # do not include nil values in arrays returned by #sort_by
    applications.to_a.sort_by! do |p|
      [p.application.priority(event) || 99,
        p.participant.last_name || "",
        p.participant.first_name || ""]
    end
  end

  def filter_applications
    if params[:prio].nil? && params[:waiting_list].nil?
      params[:prio] = %w[1] # default filter
    end

    conditions = []
    args = []
    filter_by_prio(conditions, args) if params[:prio]
    filter_by_waiting_list(conditions, args) if params[:waiting_list]

    [conditions.join(" OR "), *args] if conditions.present?
  end

  def filter_by_prio(conditions, args)
    ([1, 2, 3] & params[:prio].collect(&:to_i)).each do |i|
      conditions << "event_applications.priority_#{i}_id = ?"
      args << event.id
    end
  end

  def filter_by_waiting_list(conditions, args)
    if event.kind_id
      conditions << "(event_applications.waiting_list = ? AND events.kind_id = ?)"
      args << true << event.kind_id
    else
      conditions << "(event_applications.waiting_list = ? AND events.kind_id IS NULL)"
      args << true
    end
  end

  def assigner
    @assigner ||= Event::ParticipantAssigner.new(event, participation, send_email?, current_user)
  end

  def event
    @event ||= group.events.find(params[:event_id])
  end

  def group
    @group ||= Group.find(params[:group_id])
  end

  def participation
    @participation ||= Event::Participation.find(params[:id])
  end

  def application
    participation.application
  end

  def authorize
    not_found unless event.supports_applications?
    authorize!(:application_market, event)
  end

  def send_email?
    true?(params[:send_email])
  end
end
