-module(pprint@decoder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).

-export([classify/1]).
-export_type([type/0, field/0]).

-type type() :: {t_string, binary()} |
    {t_int, integer()} |
    {t_float, float()} |
    {t_bool, boolean()} |
    t_nil |
    {t_bit_array, bitstring()} |
    {t_list, list(gleam@dynamic:dynamic_())} |
    {t_dict, gleam@dict:dict(type(), type())} |
    {t_tuple, list(gleam@dynamic:dynamic_())} |
    {t_custom, binary(), list(field())} |
    {t_foreign, binary()}.

-type field() :: {labelled, binary(), gleam@dynamic:dynamic_()} |
    {positional, gleam@dynamic:dynamic_()}.

-spec decode_type(gleam@dynamic:dynamic_()) -> {ok, type()} |
    {error, list(gleam@dynamic:decode_error())}.
decode_type(Value) ->
    gleam@result:lazy_or(
        gleam@result:map(
            gleam@dynamic:int(Value),
            fun(Field@0) -> {t_int, Field@0} end
        ),
        fun() ->
            gleam@result:lazy_or(
                gleam@result:map(
                    gleam@dynamic:float(Value),
                    fun(Field@0) -> {t_float, Field@0} end
                ),
                fun() ->
                    gleam@result:lazy_or(
                        gleam@result:map(
                            gleam@dynamic:string(Value),
                            fun(Field@0) -> {t_string, Field@0} end
                        ),
                        fun() ->
                            gleam@result:lazy_or(
                                gleam@result:map(
                                    gleam@dynamic:bool(Value),
                                    fun(Field@0) -> {t_bool, Field@0} end
                                ),
                                fun() ->
                                    gleam@result:lazy_or(
                                        gleam@result:map(
                                            pprint_ffi:decode_nil(Value),
                                            fun(_) -> t_nil end
                                        ),
                                        fun() ->
                                            gleam@result:lazy_or(
                                                gleam@result:map(
                                                    gleam@dynamic:bit_array(
                                                        Value
                                                    ),
                                                    fun(Field@0) -> {t_bit_array, Field@0} end
                                                ),
                                                fun() ->
                                                    gleam@result:lazy_or(
                                                        pprint_ffi:decode_custom_type(
                                                            Value
                                                        ),
                                                        fun() ->
                                                            gleam@result:lazy_or(
                                                                gleam@result:map(
                                                                    pprint_ffi:decode_tuple(
                                                                        Value
                                                                    ),
                                                                    fun(Field@0) -> {t_tuple, Field@0} end
                                                                ),
                                                                fun() ->
                                                                    gleam@result:lazy_or(
                                                                        gleam@result:map(
                                                                            gleam@dynamic:shallow_list(
                                                                                Value
                                                                            ),
                                                                            fun(Field@0) -> {t_list, Field@0} end
                                                                        ),
                                                                        fun() ->
                                                                            gleam@result:lazy_or(
                                                                                gleam@result:map(
                                                                                    (gleam@dynamic:dict(
                                                                                        fun decode_type/1,
                                                                                        fun decode_type/1
                                                                                    ))(
                                                                                        Value
                                                                                    ),
                                                                                    fun(Field@0) -> {t_dict, Field@0} end
                                                                                ),
                                                                                fun(
                                                                                    
                                                                                ) ->
                                                                                    {ok,
                                                                                        {t_foreign,
                                                                                            gleam@string:inspect(
                                                                                                Value
                                                                                            )}}
                                                                                end
                                                                            )
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-spec classify(gleam@dynamic:dynamic_()) -> type().
classify(Value) ->
    _assert_subject = decode_type(Value),
    {ok, T} = case _assert_subject of
        {ok, _} -> _assert_subject;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Assertion pattern match failed"/utf8>>,
                        value => _assert_fail,
                        module => <<"pprint/decoder"/utf8>>,
                        function => <<"classify"/utf8>>,
                        line => 30})
    end,
    T.
