require 'dry/types/hash/schema'
require 'dry/types/fn_container'

module Dry
  module Types
    class Hash < Definition
      # A bulder for legacy schemas
      # @api private
      class SchemaBuilder
        NIL_TO_UNDEFINED = -> v { v.nil? ? Undefined : v }
        OMITTABLE_KEYS = %i(schema weak symbolized).freeze
        STRICT = %i(strict strict_with_defaults).freeze

        # @param primitive [Type]
        # @option options [Hash{Symbol => Definition}] :member_types
        # @option options [Symbol] :hash_type
        def call(primitive, **options)
          hash_type = options.fetch(:hash_type)
          member_types = {}

          options.fetch(:member_types).each do |k, t|
            member_types[k] = build_type(hash_type, t)
          end

          instantiate(primitive, **options, member_types: member_types)
        end

        def instantiate(primitive, hash_type: :base, meta: EMPTY_HASH, **options)
          meta = meta.dup

          meta[:strict] = true if strict?(hash_type)
          meta[:key_transform_fn] = Schema::SYMBOLIZE_KEY if hash_type == :symbolized

          Schema.new(primitive, **options, meta: meta)
        end

        private

        def omittable?(constructor)
          OMITTABLE_KEYS.include?(constructor)
        end

        def strict?(constructor)
          STRICT.include?(constructor)
        end

        def build_type(constructor, type)
          type = safe(constructor, type)
          type = default(constructor, type) if type.default?
          type = type.meta(omittable: true) if omittable?(constructor)
          type
        end

        def safe(constructor, type)
          if constructor == :weak || constructor == :symbolized
            type.safe
          else
            type
          end
        end

        def default(constructor, type)
          case constructor
          when :strict_with_defaults
            type
          when :strict
            type.type
          else
            type.constructor(NIL_TO_UNDEFINED)
          end
        end
      end
    end
  end
end