# frozen_string_literal: true

require 'set'
require 'optparse'

module JenkinsfileRunner
  class BaseConfiguration
    class << self
      def all_attributes
        @all_attributes ||= [].to_set
      end

      def config_attributes(attrs)
        attrs.each do |attr|
          all_attributes.add(attr)
          attr_accessor attr
        end
      end
    end

    attr_reader :errors

    def initialize(argv)
      parser.parse!(argv, into: self)
    end

    def validate
      @errors = []
    end

    def valid?
      validate
      @errors.empty?
    end

    def validate!
      return if valid?

      message = errors
        .map { |err| "#{key_for_user(err.first)} #{err.last}" }
        .join(",\n")

      abort("\nErrors:\n" + message + "\n\n" + parser.help)
    end

    def []=(key, value)
      attr = key_from_user(key)
      return unless valid_attr?(attr)

      self.public_send("#{attr}=", value)
    end

    def inspect
      all_attributes
        .map { |attr| "--#{key_for_user(attr)}=#{value_for(attr)}" }
        .join(' ')
    end

    private

    def parser
      raise NotImplementedError, 'define a parser in the concrete configuration class'
    end

    def validate_presence(attributes)
      attributes.inject([]) do |memo, attr|
        value = value_for(attr)
        memo << [attr, 'is missing'] if value.nil? || value.empty?
        memo
      end
    end

    def validate_paths(attributes)
      attributes.inject([]) do |memo, attr|
        value = value_for(attr)
        next memo unless value

        value = Pathname(value).expand_path
        memo << [attr, "#{value} does not exist"] unless value.exist?
        memo
      end
    end

    def all_attributes
      self.class.all_attributes
    end

    def value_for(attr)
      return unless valid_attr?(attr)

      public_send(attr)
    end

    def valid_attr?(attr)
      all_attributes.include?(attr)
    end

    def key_from_user(key)
      key.to_s.gsub('-', '_').to_sym
    end

    def key_for_user(key)
      key.to_s.gsub('_', '-' )
    end
  end
end
