# frozen_string_literal: true

# Finds an address based on the data provided
# Can take any combination of an email String, Customer or Spree::User as args
# The #bill_address and #ship_address methods automatically return matched addresses
# according to this order: customer addresses, user addresses, addresses from
# completed orders with an email that matches the email string provided.

module OpenFoodNetwork
  class AddressFinder
    attr_reader :email, :user, :customer

    def initialize(email: nil, user: nil, customer: nil)
      @email = email
      @user = user
      @customer = customer
    end

    def bill_address
      customer_preferred_bill_address || user_preferred_bill_address || fallback_bill_address
    end

    def ship_address
      customer_preferred_ship_address || user_preferred_ship_address || fallback_ship_address
    end

    private

    def customer_preferred_bill_address
      customer&.bill_address
    end

    def customer_preferred_ship_address
      customer&.ship_address
    end

    def user_preferred_bill_address
      user&.bill_address
    end

    def user_preferred_ship_address
      user&.ship_address
    end

    def fallback_bill_address
      last_used_bill_address&.clone || Spree::Address.default
    end

    def fallback_ship_address
      last_used_ship_address&.clone || Spree::Address.default
    end

    def last_used_bill_address
      return nil unless allow_search_by_email?

      Spree::Order.joins(:bill_address).order('id DESC')
        .complete.where(email:)
        .first&.bill_address
    end

    def last_used_ship_address
      return nil unless allow_search_by_email?

      Spree::Order.complete.joins(:ship_address, shipments: :shipping_methods).order('id DESC')
        .where(email:, spree_shipping_methods: { require_ship_address: true })
        .first&.ship_address
    end

    # Only allow search for address by email if a customer or user with the
    # same address has been provided, otherwise we are providing access to
    # addresses with only an email address, which could be problematic.
    # Assumption: front-end users can't ask this library for an address using
    # a customer or user other than themselves...
    def allow_search_by_email?
      email.present? && email_matches_customer_or_user?
    end

    def email_matches_customer_or_user?
      email == customer&.email || email == user&.email
    end
  end
end
