# frozen_string_literal: true

module Decidim
  module Consultations
    # This module, when injected into a controller, ensures there's a
    # question available and deducts it from the context.
    module NeedsQuestion
      def self.enhance_controller(instance_or_module)
        instance_or_module.class_eval do
          helper_method :current_question, :previous_question, :next_question, :previous_published_question, :next_published_question,
                        :current_consultation, :current_participatory_space, :stats, :sorted_results

          helper Decidim::WidgetUrlsHelper
        end
      end

      def self.extended(base)
        base.extend Decidim::NeedsOrganization, InstanceMethods

        enhance_controller(base)
      end

      def self.included(base)
        base.include Decidim::NeedsOrganization, InstanceMethods

        enhance_controller(base)
      end

      module InstanceMethods
        # Public: Finds the current Question given this controller's
        # context.
        #
        # Returns the current Question.
        def current_question
          @current_question ||= detect_question
        end

        # Public: Finds the previous Question in the Array of questions
        # associated to the current_question's consultation.
        #
        # Returns the previous Question in the Array or nil if out of bounds.
        def previous_question
          return nil if (current_question_index - 1).negative?

          current_consultation_questions.at(current_question_index - 1)
        end

        # Public: Finds the next Question in the Array of questions
        # associated to the current_question's consultation.
        #
        # Returns the next Question in the Array or nil if out of bounds.
        def next_question
          return nil if current_question_index + 1 >= current_consultation_questions.size

          current_consultation_questions.at(current_question_index + 1)
        end

        # same as next_question but for published questions only
        def next_published_question
          return nil if current_published_question_index + 1 >= current_consultation_published_questions.size

          current_consultation_published_questions.at(current_published_question_index + 1)
        end

        # same as previous_question but for published questions only
        def previous_published_question
          return nil if (current_published_question_index - 1).negative?

          current_consultation_published_questions.at(current_published_question_index - 1)
        end

        # Public: Finds the current Consultation given this controller's
        # context.
        #
        # Returns the current Consultation.
        def current_consultation
          @current_consultation ||= current_question&.consultation || detect_consultation
        end

        alias current_participatory_space current_question

        private

        def detect_question
          request.env["current_question"] ||
            OrganizationQuestions.for(current_organization).find_by(slug: params[:question_slug] || params[:slug])
        end

        def detect_consultation
          request.env["current_consultation"] ||
            organization_consultations.find_by(slug: params[:consultation_slug])
        end

        def organization_consultations
          @organization_consultations ||= OrganizationConsultations.new(current_organization).query
        end

        def stats
          @stats ||= QuestionStatsPresenter.new(question: current_question)
        end

        def current_consultation_questions
          @current_consultation_questions ||= current_question.consultation.questions.to_a
        end

        def current_consultation_published_questions
          @current_consultation_published_questions ||= current_question.consultation.questions.published.to_a
        end

        def current_question_index
          current_consultation_questions.find_index(current_question)
        end

        def current_published_question_index
          current_consultation_published_questions.find_index(current_question) || -1
        end
      end
    end
  end
end