module NotificationsConcern
  extend ActiveSupport::Concern

  def load_and_count_notifications(scope = notifications_for_presentation.newest)
    count_notifications(scope)
    @unread_count = user_unread_count
    load_notifications(scope)
  end

  def load_notifications(scope = notifications_for_presentation.newest)
    scope = current_notifications(scope)
    check_out_of_bounds(scope)


    @pagy, @notifications = pagy(scope, limit: per_page, page: page_param)
    @total = @pagy.count

    @cur_selected = [per_page, @total].min
    return scope
  end

  def count_notifications(scope)
    @types                 = scope.reorder(nil).distinct.group(:subject_type).count
    @unread_notifications  = scope.reorder(nil).distinct.group(:unread).count
    @reasons               = scope.reorder(nil).distinct.group(:reason).count
    @unread_repositories   = scope.reorder(nil).distinct.group(:repository_full_name).count

    @states                = scope.reorder(nil).distinct.joins(:subject).group('subjects.state').count
    @statuses              = scope.reorder(nil).distinct.joins(:subject).group('subjects.status').count
    @unlabelled            = scope.reorder(nil).unlabelled.count
    @bot_notifications     = scope.reorder(nil).bot_author.count
    @draft                 = scope.reorder(nil).draft.count
    @assigned              = scope.reorder(nil).assigned(current_user.github_login).count
    @visiblity             = scope.reorder(nil).distinct.joins(:repository).group('repositories.private').count
    @repositories          = Repository.where(full_name: scope.reorder(nil).distinct.pluck(:repository_full_name)).select('full_name,private')
  end

  def user_unread_count
    current_user.notifications.inbox.distinct.group(:unread).count.fetch(true){ 0 }
  end

  def selected_notifications
    if params[:id] == ['all']
      current_notifications
    else
      current_user.notifications.where(id: params[:id])
    end
  end

  def current_notifications(scope = notifications_for_presentation)
    [:repo, :reason, :type, :unread, :owner, :state, :number, :author, :is_private, :status, :draft].each do |sub_scope|
      next unless params[sub_scope].present?
      # This cast is required due to a bug in type casting
      # TODO: Rails 5.2 was supposed to fix this:
      # https://github.com/rails/rails/commit/68fe6b08ee72cc47263e0d2c9ff07f75c4b42761
      # but it seems that the issue persists when using MySQL
      # https://github.com/rails/rails/issues/32624
      if sub_scope == :reason
        val = params[sub_scope].split(',')
      else
        val = scope.klass.type_for_attribute(sub_scope.to_s).cast(params[sub_scope])
      end
      scope = scope.send(sub_scope, val)
    end
    scope = scope.unlabelled if params[:unlabelled].present?
    scope = scope.bot_author(params[:bot]) if params[:bot].present?
    scope = scope.label(params[:label]) if params[:label].present?
    scope = scope.assigned(params[:assigned]) if params[:assigned].present?
    scope
  end

  def notifications_for_presentation
    @search = Search.initialize_for_saved_search(query: params[:q], user: current_user, params: params)

    if params[:q].present?
      @search.results
    elsif params[:starred].present?
      @search.scope.starred
    elsif params[:archive].present?
      @search.scope.archived
    else
      @search.scope.inbox
    end
  end

  def check_out_of_bounds(scope)
    return unless page > 1
    total_pages = (scope.count / per_page.to_f).ceil
    page_num = [page, total_pages].min
    redirect_params = params.permit!.merge(page: page_num)
    redirect_to url_for(redirect_params) if page_num != page
  end

  def find_notification
    @notification = current_user.notifications.find(params[:id])
  end

  def authenticate_web_or_api!
    return if logged_in?
    respond_to do |format|
      format.html { render 'pages/home' }
      format.json { authenticate_user! }
    end
  end

  def page
    @page ||= page_param
  end

  def per_page
    @per_page ||= restrict_per_page
  end

  DEFAULT_PER_PAGE = 20

  def restrict_per_page
    per_page = per_page_param || per_page_cookie || DEFAULT_PER_PAGE
    
    return DEFAULT_PER_PAGE if per_page < 1
    raise ActiveRecord::RecordNotFound if per_page > 100
    cookies[:per_page] = per_page

    per_page
  end

  def page_param
    pge = Integer(params[:page]) rescue 1
    pge < 1 ? 1 : pge
  end

  def per_page_param
    Integer(params[:per_page]) rescue nil
  end

  def per_page_cookie
    Integer(cookies[:per_page]) rescue nil
  end
end
