# Copyright (c) 2020 Jerome Arbez-Gindre
# frozen_string_literal: true

require('csv')
require('defmastership/export/body_formatter')
require('defmastership/export/header_formatter')

module Defmastership
  # Module to host export classes
  module Export
    # Module to host CSV export classes
    module CSV
      # to export a CSV file
      class Formatter
        # @param doc [Document] the document to export
        # @param sep [String] the CSV separator
        def initialize(doc, sep)
          @doc = doc
          @sep = sep
        end

        # Export the document to a CSV file
        #
        # @param output_file [Strinf] filename for the export
        def export_to(output_file)
          column_list = build_column_list
          ::CSV.open(output_file, 'w:ISO-8859-1', col_sep: @sep) do |csv|
            csv << header(column_list)
            export_definitions_in(csv, column_list)
          end
        end

        private

        # Helper functions
        module Helper
          # @return [Array<Symbol>] the array of methods for columns inclusion
          #
          # @param column_spec [MethodSpec] the method to include or not with its guard
          # @param doc [Document] the Document to export
          def self.column_method(column_spec, doc)
            column_spec.guard.call(doc) ? [column_spec.column_method] : []
          end

          # 2-uplet with  one method to include or not depending of a lambda guard
          MethodSpec = Struct.new(:column_method, :guard)
          public_constant :MethodSpec
        end
        private_constant :Helper

        COLUMNS_METHODS_SPECIFICATIONS =
          [
            Helper::MethodSpec.new(:type,                    ->(_) { true }),
            Helper::MethodSpec.new(:reference,               ->(_) { true }),
            Helper::MethodSpec.new(:summary,                 lambda(&:summaries?)),
            Helper::MethodSpec.new(:value,                   ->(_) { true }),
            Helper::MethodSpec.new(:checksum,                ->(_) { true }),
            Helper::MethodSpec.new(:wrong_explicit_checksum, lambda(&:wrong_explicit_checksum?)),
            Helper::MethodSpec.new(:explicit_version,        lambda(&:explicit_version?)),
            Helper::MethodSpec.new(:labels,                  ->(doc) { !doc.labels.empty? }),
            Helper::MethodSpec.new(:eref,                    ->(doc) { !doc.eref.empty? }),
            Helper::MethodSpec.new(:iref,                    lambda(&:iref)),
            Helper::MethodSpec.new(:attributes,              ->(doc) { !doc.attributes.empty? })
          ].freeze
        private_constant :COLUMNS_METHODS_SPECIFICATIONS

        def export_definitions_in(csv, column_list)
          @doc.definitions.each { |definition| csv << body(definition, column_list) }
        end

        def header(column_list)
          header_formatter = HeaderFormatter.new(@doc)
          header_line = column_list.map { |part| header_formatter.public_send(part) }
          header_line.flatten
        end

        def body(definition, column_list)
          body_formatter = BodyFormatter.new(@doc, definition)
          body_line = column_list.map { |part| body_formatter.public_send(part) }
          body_line.flatten
        end

        def build_column_list
          COLUMNS_METHODS_SPECIFICATIONS.reduce([]) do |acc, column|
            acc + Helper.column_method(column, @doc)
          end
        end
      end
    end
  end
end