# typed: strict
module HasAttrJsonEnum
  extend T::Sig
  extend T::Helpers

  module ClassMethods
    extend T::Sig

    sig do
      params(
        name: Symbol,
        type: Symbol,
        values: T::Hash[Symbol, Integer],
        options: T.untyped,
      ).returns(T.untyped)
    end
    def attr_json_enum(name, type, values, options = {})
      if type != :integer
        Kernel.raise("attr_json_enum only supports :integer type, got: #{type}")
      end

      enum_active_model_type =
        Class
          .new(ActiveModel::Type::Integer) do
            define_method(:cast) do |value|
              ret =
                case value
                when Symbol
                  values[value]
                when String
                  values[value.to_sym]
                when Integer
                  value
                else
                  raise(
                    "invalid value: #{value}, expected one of: #{values.keys}",
                  )
                end
              ret
            end

            define_method(:serialize) do |value|
              if value.is_a?(Integer)
                value
              elsif value
                values[value.to_sym] ||
                  raise(
                    "invalid value: #{value}, expected one of: #{values.keys}",
                  )
              end
            end
          end
          .new

      T.unsafe(self).attr_json(name, enum_active_model_type)
      T.unsafe(self).enum(name, values, **options)
      T.unsafe(self).validates_inclusion_of(
        name,
        in: values.keys.map(&:to_s),
        if: -> { T.unsafe(self).send(name).present? },
      )
    end
  end

  mixes_in_class_methods(ClassMethods)
end
