# frozen_string_literal: true
# typed: true

module T::Types
  class TypedHash < TypedEnumerable
    def underlying_class
      Hash
    end

    def initialize(keys:, values:)
      @inner_keys = keys
      @inner_values = values
    end

    # Technically we don't need this, but it is a nice api
    def keys
      @keys ||= T::Utils.coerce(@inner_keys)
    end

    # Technically we don't need this, but it is a nice api
    def values
      @values ||= T::Utils.coerce(@inner_values)
    end

    def type
      @type ||= T::Utils.coerce([keys, values])
    end

    # overrides Base
    def name
      "T::Hash[#{keys.name}, #{values.name}]"
    end

    # overrides Base
    def recursively_valid?(obj)
      obj.is_a?(Hash) && super
    end

    # overrides Base
    def valid?(obj)
      obj.is_a?(Hash)
    end

    def new(...)
      Hash.new(...)
    end

    class Untyped < TypedHash
      def initialize
        # Use the INSTANCE constant directly (rather than `T.untyped`) so this
        # can be built at load time, mirroring TypedArray::Untyped.
        super(keys: T::Types::Untyped::Private::INSTANCE, values: T::Types::Untyped::Private::INSTANCE)
      end

      def valid?(obj)
        obj.is_a?(Hash)
      end

      def freeze
        build_type # force lazy initialization before freezing the object
        super
      end

      module Private
        INSTANCE = Untyped.new.freeze
      end
    end
  end
end
