# frozen_string_literal: true

require 'open_food_network/permissions'

module Api
  module V1
    class CustomersController < Api::V1::BaseController
      include AddressTransformation
      include ExtraFields

      wrap_parameters :customer, include:
        Customer.attribute_names +
        [:billing_address, :shipping_address]

      skip_authorization_check only: :index

      before_action :authorize_action, only: [:show, :update, :destroy]

      # Query parameters
      before_action only: [:index] do
        @extra_customer_fields = extra_fields :customer, [:balance]
      end

      def index
        @pagy, customers = pagy(search_customers, **pagy_options)

        render json: Api::V1::CustomerSerializer.new(customers, pagination_options)
      end

      def show
        render json: Api::V1::CustomerSerializer.new(customer, include_options)
      end

      def create
        authorize! :update, Enterprise.find(customer_params[:enterprise_id])
        customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
        customer.assign_attributes(customer_params)

        if customer.save
          render json: Api::V1::CustomerSerializer.new(customer), status: :created
        else
          invalid_resource! customer
        end
      end

      def update
        if customer.update(customer_params)
          render json: Api::V1::CustomerSerializer.new(customer)
        else
          invalid_resource! customer
        end
      end

      def destroy
        if customer.destroy
          render json: Api::V1::CustomerSerializer.new(customer)
        else
          invalid_resource! customer
        end
      end

      private

      def customer
        @customer ||= if action_name == "show"
                        CustomersWithBalanceQuery.new(Customer.where(id: params[:id])).call.first!
                      else
                        Customer.find(params[:id])
                      end
      end

      def authorize_action
        authorize! action_name.to_sym, customer
      end

      def search_customers
        customers = visible_customers.includes(:bill_address, :ship_address)
        customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]

        if @extra_customer_fields.include?(:balance)
          customers = CustomersWithBalanceQuery.new(customers).call
        end

        customers.ransack(params[:q]).result.order(:id)
      end

      def visible_customers
        Customer.visible.managed_by(current_api_user)
      end

      def customer_params
        attributes = params.require(:customer).permit(
          :email, :enterprise_id,
          :code, :first_name, :last_name,
          :billing_address,
          shipping_address: [
            :phone, :latitude, :longitude,
            :first_name, :last_name,
            :street_address_1, :street_address_2,
            :postal_code, :locality,
            { region: [:name, :code], country: [:name, :code] },
          ]
        ).to_h

        attributes.merge!(created_manually: true)
        attributes.merge!(tag_list: params[:tags]) if params.key?(:tags)

        transform_address!(attributes, :billing_address, :bill_address)
        transform_address!(attributes, :shipping_address, :ship_address)

        attributes
      end

      def include_options
        fields = [params.fetch(:include, [])].flatten

        { include: fields.map(&:to_s) }
      end
    end
  end
end
