role Perl6::Metamodel::Nominalizable {
    method nominalizable_kind($obj) {
        Perl6::Metamodel::Configuration.throw_or_die(
            'X::Nominalizable::NoKind',
            self.HOW.name(self) ~ " doesn't declare method 'nominalizable_kind'",
            :nominalizable(self)
        );
    }

    method !find_wrappee($obj, %kind_of, :$lookup = 0) {
        unless %kind_of{self.nominalizable_kind} {
            my $my_wrappee := self."!wrappee"($obj);

            # .^wrappee without a named parameter returns our immediate wrappee
            return $my_wrappee
                unless nqp::elems(%kind_of);

            # If the immediate wrappee is a nominalizable then bypass the request
            return $my_wrappee.HOW."!find_wrappee"($my_wrappee, %kind_of, :$lookup)
                if $my_wrappee.HOW.archetypes($my_wrappee).nominalizable;

            # Don't be aggressive if it's about introspection purposes
            return nqp::null() if $lookup;

            # Otherwise the request cannot be completed
            Perl6::Metamodel::Configuration.throw_or_die(
                'X::Nominalizable::NoWrappee',
                "Can't find requested wrappee on "
                    ~ $*ORIG-NOMINALIZABLE
                    ~ ": reached a nominal type "
                    ~ $my_wrappee.HOW.name($my_wrappee),
                :nominalizable($*ORIG-NOMINALIZABLE),
                :kinds(%kind_of),
            )
        }
        $obj
    }

    method wrappee($obj, *%kind_of) {
        my $*ORIG-NOMINALIZABLE := $obj;
        self."!find_wrappee"($obj, %kind_of)
    }

    method wrappee-lookup($obj, *%kind_of) {
        self."!find_wrappee"($obj, %kind_of, :lookup)
    }

    method coerce($obj, $value) {
        # In general, this method should be invoked via $type.^wrappee(:coercion). But this would complicate QAST
        # generated by parameter binding implementation in Actions. So, let it be here. Hopefully, it'd be possible to
        # remove it either with RakuAST or by implementing corresponding helper nqp:: Raku op for coercive parameter
        # binding.
        my $coercion_type := self.wrappee($obj, :coercion);
        $coercion_type.HOW.coerce($coercion_type, $value)
    }
}
