# frozen_string_literal: true

module RSpec
  module XlsxMatchers
    # have_excel_columns / have_excel_column
    class Columns < BaseSheet
      include ExactMatch
      attr_reader :extra_columns, :missing_columns, :expected_columns, :actual_columns

      def initialize(expected_columns)
        super()
        @expected_columns = force_array(expected_columns)
        @missing_columns = []
        @extra_columns = []
        @actual_columns = []
      end

      def failure_message
        return sheet_failure_message if sheet.nil?

        msg = failure_message_in_sheet("Columns mismatch")

        "#{msg}:\n\t#{column_failure_message}"
      end

      private

      def process_axlsx_sheet
        @actual_columns = sheet.rows[0]&.map(&:value)
        if exact_match
          perform_exact_match
        else
          perform_loose_match
        end
      end

      def process_roo_sheet
        @actual_columns = sheet.row(1)
        if exact_match
          perform_exact_match
        else
          perform_loose_match
        end
      end

      def perform_exact_match
        return true if expected_columns == actual_columns

        (expected_columns - actual_columns).each do |column|
          extra_columns << column
        end

        (actual_columns - expected_columns).each do |column|
          missing_columns << column
        end
        false
      end

      def perform_loose_match
        expected_columns.each do |column|
          missing_columns << column unless actual_columns&.include?(column)
        end
        missing_columns.empty?
      end

      def column_failure_message
        column_mismatch_failure_message || column_order_failure_message
      end

      def column_order_failure_message
        return unless exact_match

        "Expected: #{map_output(expected_columns)}\n\t" \
          "Received: #{map_output(actual_columns)}"
      end

      def column_mismatch_failure_message
        msg = [missing_column_failure_message, extra_columns_failure_message].compact.join("\n\t")
        if msg.empty?
          nil
        else
          msg
        end
      end

      def missing_column_failure_message
        return if missing_columns.empty?

        "Missing columns: #{map_output(missing_columns)}"
      end

      def extra_columns_failure_message
        return if extra_columns.empty?

        "Extra columns: #{map_output(extra_columns)}"
      end
    end
  end
end