# frozen_string_literal: true

# Provides methods for Order
class Orders::OrderService
  class << self
    include ApplicationHelper

    ORDERS_PER_PAGE = 20

    def list(filters, current_user)
      orders = Order.includes(statistic_profile: [user: [:profile]]).where(nil)
      orders = filter_by_user(orders, filters, current_user)
      orders = filter_by_reference(orders, filters, current_user)
      orders = filter_by_state(orders, filters)
      orders = filter_by_period(orders, filters)

      orders = orders.where.not(state: 'cart') if current_user.member?
      orders = orders_ordering(orders, filters)
      total_count = orders.count
      orders = orders.page(filters[:page] || 1).per(ORDERS_PER_PAGE)
      {
        data: orders,
        page: filters[:page]&.to_i || 1,
        total_pages: orders.page(1).per(ORDERS_PER_PAGE).total_pages,
        page_size: ORDERS_PER_PAGE,
        total_count: total_count
      }
    end

    def update_state(order, current_user, state, note = nil)
      case state
      when 'in_progress'
        ::Orders::SetInProgressService.new.call(order, current_user)
      when 'ready'
        ::Orders::OrderReadyService.new.call(order, current_user, note)
      when 'canceled'
        ::Orders::OrderCanceledService.new.call(order, current_user)
      when 'delivered'
        ::Orders::OrderDeliveredService.new.call(order, current_user)
      when 'refunded'
        ::Orders::OrderRefundedService.new.call(order, current_user)
      else
        nil
      end

      # update in elasticsearch (statistics)
      stat_order = Stats::Order.search(query: { term: { orderId: order.id } })
      sub_type = if state.in?(%w[paid in_progress ready delivered])
                   'paid-processed'
                 elsif state.in?(%w[payment_failed refunded canceled])
                   'aborted'
                 end

      stat_order.map { |s| s.update(subType: sub_type, state: state) } if sub_type.present?

      order
    end

    def in_stock?(order, stock_type = 'external')
      order.order_items.each do |item|
        return false if item.orderable.stock[stock_type] < item.quantity || item.orderable.stock[stock_type] < item.orderable.quantity_min
      end
      true
    end

    def greater_than_quantity_min?(order)
      order.order_items.each do |item|
        return false if item.quantity < item.orderable.quantity_min
      end
      true
    end

    def item_amount_not_equal?(order)
      order.order_items.each do |item|
        orderable_amount = item.orderable.amount || 0
        return false if item.amount != orderable_amount
      end
      true
    end

    def all_products_is_active?(order)
      order.order_items.each do |item|
        return false unless item.orderable.is_active
      end
      true
    end

    def withdrawal_instructions(order)
      res = order&.order_activities&.find_by(activity_type: 'ready')&.note.presence ||
            Setting.get('store_withdrawal_instructions').presence ||
            _t('order.please_contact_FABLAB', FABLAB: Setting.get('fablab_name').presence || 'empty')

      ActionController::Base.helpers.sanitize(res, tags: %w[p ul li h3 u em strong a], attributes: %w[target rel href])
    end

    private

    def filter_by_user(orders, filters, current_user)
      if filters[:user_id]
        statistic_profile_id = current_user.statistic_profile.id
        if (current_user.member? && current_user.id == filters[:user_id].to_i) || current_user.privileged?
          user = User.find(filters[:user_id])
          statistic_profile_id = user.statistic_profile.id
        end
        orders = orders.where(statistic_profile_id: statistic_profile_id)
      elsif current_user.member?
        orders = orders.where(statistic_profile_id: current_user.statistic_profile.id)
      else
        orders = orders.where.not(statistic_profile_id: nil)
      end
      orders
    end

    def filter_by_reference(orders, filters, current_user)
      return orders unless filters[:reference].present? && current_user.privileged?

      orders.where(reference: filters[:reference])
    end

    def filter_by_state(orders, filters)
      return orders if filters[:states].blank?

      state = filters[:states].split(',')
      orders.where(state: state) unless state.empty?
    end

    def filter_by_period(orders, filters)
      return orders unless filters[:period_from].present? && filters[:period_to].present?

      orders.where(created_at: Time.zone.parse(filters[:period_from])..Time.zone.parse(filters[:period_to]).end_of_day)
    end

    def orders_ordering(orders, filters)
      key, order = filters[:sort]&.split('-')
      key ||= 'created_at'
      order ||= 'desc'

      orders.order(key => order)
    end
  end
end
