module JSONAPIonify::Api
  class ParamOptions
    extend JSONAPIonify::Structure::Helpers::MemberNames

    def self.reserved?(value)
      %w{sort include}.include? value
    end

    def self.valid?(value)
      super(value) && value =~ /[^\u0061-\u007A]/
    end

    def self.hash_to_keypaths(hash)
      hash.each_with_object([]) do |(k, v), key_paths|
        pather = lambda do |v, current_path|
          if v.is_a? Hash
            v.each do |k, v|
              pather.call(v, [*current_path, k])
            end
          else
            key_paths << current_path.map(&:to_sym)
          end
        end
        pather.call(v, [k])
      end
    end

    def self.keypath_to_string(*paths)
      first_path, *rest = paths
      "#{first_path}#{rest.map { |path| "[#{path}]" }.join}"
    end

    def self.invalid_parameters(hash, keypaths)
      invalid_key_paths = hash_to_keypaths(hash) - keypaths
      invalid_key_paths.map do |paths|
        keypath_to_string(*paths)
      end
    end

    def self.missing_parameters(hash, keypaths)
      missing_key_paths = keypaths - hash_to_keypaths(hash)
      missing_key_paths.map do |paths|
        keypath_to_string(*paths)
      end
    end

    attr_reader :keypath, :actions, :required, :sticky

    def initialize(*keys, default: nil, actions: nil, required: false, sticky: false)
      @keypath  = keys
      @sticky   = sticky
      @actions  = Array.wrap(actions)
      @default  = default.to_s
      @required = required
    end

    def with_value(value)
      Hash.new.tap do |hash|
        keypath[0..-2].reduce(hash) do |current_hash, key|
          current_hash[key.to_s] = {}
        end[keypath.last.to_s] = value
      end
    end

    def default
      with_value @default
    end

    def extract_value(params)
      keypath.reduce(params) do |p, key|
        p[key.to_s]
      end
    end

    def has_default?
      @default.present?
    end

    def default_value?(value)
      @default == value
    end

    def string
      self.class.keypath_to_string(*@keypath)
    end

  end
end