# frozen_string_literal: true

require 'skittlize'
require 'terminal-table'

require 'motoko/utils/snake_to_camel'

module Motoko
  class Formatter
    attr_reader :options, :columns_spec, :shortcuts
    attr_accessor :columns, :nodes, :mono, :wide, :count, :sort_by

    include Motoko::Utils::SnakeToCamel

    def initialize
      config = Config.new
      @nodes = []
      @columns = config.columns
      @mono = false
      @wide = false
      @count = false
      @sort_by = config.sort_by
      @columns_spec = config.columns_spec
      @shortcuts = config.shortcuts
    end

    def to_s
      return '' if nodes.empty?

      @rows = nil
      @column_resolvers = nil

      columns.uniq!

      table = ::Terminal::Table.new headings: headings, rows: data
      column_resolvers.each_with_index do |column, idx|
        table.align_column(idx, column.align) if column.align
      end
      table.to_s
    end

    def column_resolvers
      @column_resolvers ||= columns.map do |column|
        klass = columns_spec[column].delete('resolver') || 'Fact'

        Object.const_get("Motoko::Resolvers::#{snake_to_camel_case(klass)}").new(column, columns_spec[column])
      end
    end

    def data
      return @data if @data

      @data = sorted_nodes.map! do |node|
        column_resolvers.map do |column|
          column.value(node)
        end
      end

      @data.skittlize!(split: "\n", join: ', ') unless mono

      @data
    end

    def headings
      column_resolvers.each_with_index.map do |column, idx|
        name = column.human_name
        if count
          different_values = data.map { |line| line[idx] }.uniq.compact.count
          name += " (#{different_values})" if different_values > 1
        end
        {
          value: mono ? name : "\e[1m#{name}\e[0m",
          alignment: :center,
        }
      end
    end

    def sorted_nodes
      nodes.sort_by { |a| sort_by.map { |c| a.fact(c) || '' } + [a.identity] }
    end
  end
end
