# frozen_string_literal: true

module Reporting
  class ReportTemplate
    include ReportsHelper

    attr_accessor :user, :params, :ransack_params

    delegate :render_as, :as_json, :to_html, :to_csv, :to_xlsx, :to_pdf, :to_json, to: :renderer
    delegate :unformatted_render?, :html_render?, :display_header_row?, :display_summary_row?,
             to: :renderer

    delegate :rows, :table_rows, :grouped_data, to: :rows_builder
    delegate :available_headers, :table_headers, :fields_to_hide, :fields_to_show,
             to: :headers_builder

    delegate :formatted_rules, :header_option?, :summary_row_option?, :metadata_option?, to: :ruler

    def initialize(user, params = {}, render: false)
      unless render
        params.reverse_merge!(default_params)
        params[:q] ||= {}
        params[:q].reverse_merge!(default_params[:q]) if default_params[:q].present?
      end
      @user = user
      @params = params
      @params = @params.permit!.to_h unless @params.is_a? Hash
      @ransack_params = @params[:q] || {}
    end

    # Message to be displayed at the top of rendered table
    def message
      ""
    end

    # Ransack search to get base ActiveRelation
    # If the report object do not use ransack search, create a fake one just for the form_for
    # in reports/show.haml
    def search
      Ransack::Search.new(Spree::Order)
    end

    # The search result, usually an ActiveRecord Array
    def query_result
      raise NotImplementedError
    end

    # Convert the query_result into expected row result (which will be displayed)
    # Example
    # {
    #   name: proc { |model| model.display_name },
    #   best_friend: proc { |model| model.friends.first.first_name }
    # }
    def columns
      raise NotImplementedError
    end

    def table_columns
      columns
    end

    # Exple { total_price: :currency }
    def columns_format
      {}
    end

    # Headers are automatically translated with table_headers method
    # You can customize some header name if needed
    def custom_headers
      {}
    end

    # Rules for grouping, ordering, and summary rows
    # Rule Full Example. In the following item reference the query_result item and
    #                    row the transformation of this item into the expected result
    # {
    #   group_by: proc { |item, row| row.last_name },
    #   group_by: :last_name, # same that previous line,
    #   group_by: proc { |line_item, row| line_item.product },
    #   sort_by: proc { |product| product.name },
    #   header: proc { |group_key, items, rows| items.first.display_name },
    #   header: true, # shortcut to use group_key as header
    #   header: :supplier, # shortcut to use supplier column as header
    #   header: [:last_name, :first_name], # shortcut to use last_name & first_name as header
    #   header_class: "h1 h2 h3 h4 text-center background", # class applies to the header row
    #   # Those fields will be hidden when the header_row is activated
    #   fields_used_in_header: [:first_name, :last_name],
    #   summary_row: proc do |group_key, items, rows|
    #     {
    #       quantity: rows.map(&:quantity).sum(&:to_i),
    #       price: "#{rows.map(&:price).compact.sum} #{currency_symbol}"
    #     }
    #   end,
    #   summary_row_class: "", # by default 'text-bold'
    #   summary_row_label: "Total by Customer" # by default 'TOTAL'
    #   summary_row_label: proc { |group_key, items, rows| "Total for #{group_key}" }
    # }
    def rules
      []
    end

    # Default filters/search params to be used
    def default_params
      {}
    end

    def skip_duplicate_rows?
      false
    end

    private

    def renderer
      @renderer ||= ReportRenderer.new(self)
    end

    def rows_builder
      @rows_builder ||= ReportRowsBuilder.new(self)
    end

    def headers_builder
      @headers_builder ||= ReportHeadersBuilder.new(self)
    end

    def ruler
      @ruler ||= ReportRuler.new(self)
    end
  end
end
