-module(server@serialisation).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).

-export([decode_signin/1, decode_signup/1, decode_save/1, decode_browse/1, decode_mine/1, decode_variants/1, encode_session_key/1, encode_template/1, encode_browse/1, encode_mine/2, encode_browse_err/1, encode_id/1, encode_id2/2, encode_save_err/1, encode_sign_up_err/1, encode_sign_in_err/1]).

-spec user_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {binary(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}).
user_decoder() ->
    gleam@dynamic:decode2(
        fun(Username, Password) -> {Username, Password} end,
        gleam@dynamic:field(<<"username"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"password"/utf8>>, fun gleam@dynamic:string/1)
    ).

-spec decode_signin(gleam@dynamic:dynamic_()) -> {ok, {binary(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_signin(Json) ->
    Decoder = user_decoder(),
    Decoder(Json).

-spec decode_signup(gleam@dynamic:dynamic_()) -> {ok, {binary(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_signup(Json) ->
    decode_signin(Json).

-spec save_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {binary(), binary(), gleam@dynamic:dynamic_()}} |
    {error, list(gleam@dynamic:decode_error())}).
save_decoder() ->
    gleam@dynamic:decode3(
        fun(Session_key, Username, Template) ->
            {Session_key, Username, Template}
        end,
        gleam@dynamic:field(<<"sessionKey"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"username"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"template"/utf8>>, fun gleam@dynamic:dynamic/1)
    ).

-spec template_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {binary(),
            binary(),
            integer(),
            integer(),
            list(binary()),
            binary(),
            binary()}} |
    {error, list(gleam@dynamic:decode_error())}).
template_decoder() ->
    gleam@dynamic:decode9(
        fun(
            Title,
            Description,
            Published_id,
            Version_id,
            Args,
            Language,
            Raw,
            _,
            _
        ) ->
            {Title, Description, Published_id, Version_id, Args, Language, Raw}
        end,
        gleam@dynamic:field(<<"title"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"description"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"publishedId"/utf8>>, fun gleam@dynamic:int/1),
        gleam@dynamic:field(<<"versionId"/utf8>>, fun gleam@dynamic:int/1),
        gleam@dynamic:field(
            <<"args"/utf8>>,
            gleam@dynamic:list(fun gleam@dynamic:string/1)
        ),
        gleam@dynamic:field(<<"language"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"raw"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"date"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"username"/utf8>>, fun gleam@dynamic:string/1)
    ).

-spec decode_save(gleam@dynamic:dynamic_()) -> {ok,
        {binary(), templates@template:template()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_save(Json) ->
    _pipe = <<"decoding save draft"/utf8>>,
    gleam@io:debug(_pipe),
    gleam@result:'try'(
        (save_decoder())(Json),
        fun(_use0) ->
            {Session_key, Username, Template_json} = _use0,
            gleam@result:'try'(
                (template_decoder())(Template_json),
                fun(_use0@1) ->
                    {Title,
                        Description,
                        Published_id_int,
                        Version_id_int,
                        Args,
                        Language,
                        Raw} = _use0@1,
                    Published_id = (case Published_id_int of
                        -1 ->
                            none;

                        Id ->
                            {some, Id}
                    end),
                    Version_id = (case Version_id_int of
                        -1 ->
                            none;

                        Id@1 ->
                            {some, Id@1}
                    end),
                    Template = templates@template:new(
                        Title,
                        Description,
                        Published_id,
                        Version_id,
                        Args,
                        Language,
                        Raw,
                        Username
                    ),
                    _pipe@2 = {Session_key,
                        begin
                            _pipe@1 = Template,
                            gleam@io:debug(_pipe@1)
                        end},
                    {ok, _pipe@2}
                end
            )
        end
    ).

-spec browse_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {integer(), binary(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}).
browse_decoder() ->
    gleam@dynamic:decode3(
        fun(Page, Language, Search) -> {Page, Language, Search} end,
        gleam@dynamic:field(<<"page"/utf8>>, fun gleam@dynamic:int/1),
        gleam@dynamic:field(<<"language"/utf8>>, fun gleam@dynamic:string/1),
        gleam@dynamic:field(<<"search"/utf8>>, fun gleam@dynamic:string/1)
    ).

-spec decode_browse(gleam@dynamic:dynamic_()) -> {ok,
        {integer(), binary(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_browse(Json) ->
    Decoder = browse_decoder(),
    Decoder(Json).

-spec mine_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {integer(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}).
mine_decoder() ->
    gleam@dynamic:decode2(
        fun(Page, Username) -> {Page, Username} end,
        gleam@dynamic:field(<<"page"/utf8>>, fun gleam@dynamic:int/1),
        gleam@dynamic:field(<<"username"/utf8>>, fun gleam@dynamic:string/1)
    ).

-spec decode_mine(gleam@dynamic:dynamic_()) -> {ok, {integer(), binary()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_mine(Json) ->
    Decoder = mine_decoder(),
    Decoder(Json).

-spec variants_decoder() -> fun((gleam@dynamic:dynamic_()) -> {ok,
        {integer(), integer()}} |
    {error, list(gleam@dynamic:decode_error())}).
variants_decoder() ->
    gleam@dynamic:decode2(
        fun(Page, Published_id) -> {Page, Published_id} end,
        gleam@dynamic:field(<<"page"/utf8>>, fun gleam@dynamic:int/1),
        gleam@dynamic:field(<<"publishedId"/utf8>>, fun gleam@dynamic:int/1)
    ).

-spec decode_variants(gleam@dynamic:dynamic_()) -> {ok, {integer(), integer()}} |
    {error, list(gleam@dynamic:decode_error())}.
decode_variants(Json) ->
    Decoder = variants_decoder(),
    Decoder(Json).

-spec encode_session_key(binary()) -> gleam@json:json().
encode_session_key(Session_key) ->
    gleam@json:object([{<<"sessionKey"/utf8>>, gleam@json:string(Session_key)}]).

-spec encode_template(templates@template:template()) -> gleam@json:json().
encode_template(T) ->
    V_id@1 = case erlang:element(5, T) of
        {some, V_id} ->
            V_id;

        none ->
            -1
    end,
    P_id@1 = case erlang:element(4, T) of
        {some, P_id} ->
            P_id;

        none ->
            -1
    end,
    gleam@json:object(
        [{<<"title"/utf8>>, gleam@json:string(erlang:element(2, T))},
            {<<"description"/utf8>>, gleam@json:string(erlang:element(3, T))},
            {<<"publishedId"/utf8>>, gleam@json:int(P_id@1)},
            {<<"versionId"/utf8>>, gleam@json:int(V_id@1)},
            {<<"args"/utf8>>,
                gleam@json:array(erlang:element(6, T), fun gleam@json:string/1)},
            {<<"language"/utf8>>, gleam@json:string(erlang:element(7, T))},
            {<<"raw"/utf8>>, gleam@json:string(erlang:element(8, T))},
            {<<"date"/utf8>>, gleam@json:string(erlang:element(9, T))},
            {<<"username"/utf8>>, gleam@json:string(erlang:element(10, T))}]
    ).

-spec encode_browse(list({templates@template:template(), binary()})) -> gleam@json:json().
encode_browse(Browse) ->
    _pipe = Browse,
    gleam@json:array(
        _pipe,
        fun(X) ->
            {Template, Creator} = X,
            gleam@json:object(
                [{<<"template"/utf8>>, encode_template(Template)},
                    {<<"creator"/utf8>>, gleam@json:string(Creator)}]
            )
        end
    ).

-spec encode_mine(
    list({templates@template:template(), binary()}),
    list(templates@template:template())
) -> gleam@json:json().
encode_mine(Drafts, Published) ->
    gleam@json:object(
        [{<<"published"/utf8>>,
                begin
                    _pipe = Drafts,
                    encode_browse(_pipe)
                end},
            {<<"drafts"/utf8>>,
                begin
                    _pipe@1 = Published,
                    gleam@json:array(_pipe@1, fun encode_template/1)
                end}]
    ).

-spec encode_browse_err(db@browse:get_err()) -> gleam@json:json().
encode_browse_err(Browse) ->
    Code = case Browse of
        {get_db_err, S} ->
            _pipe = S,
            gleam@io:debug(_pipe),
            <<"DB_ERR"/utf8>>
    end,
    gleam@json:string(Code).

-spec encode_id(integer()) -> gleam@json:json().
encode_id(Id) ->
    gleam@json:object([{<<"id"/utf8>>, gleam@json:int(Id)}]).

-spec encode_id2(integer(), integer()) -> gleam@json:json().
encode_id2(Published_id, Version_id) ->
    gleam@json:object(
        [{<<"published_id"/utf8>>, gleam@json:int(Published_id)},
            {<<"version_id"/utf8>>, gleam@json:int(Version_id)}]
    ).

-spec encode_save_err(templates@save_publish:save_err()) -> gleam@json:json().
encode_save_err(E) ->
    Code = case E of
        invalid_session_key ->
            <<"INVALID_SESSION_KEY"/utf8>>;

        {save_db_err, E@1} ->
            case E@1 of
                invalid_user ->
                    <<"INVALID_USER"/utf8>>;

                {add_db_err, _} ->
                    <<"DB_ERR"/utf8>>
            end
    end,
    gleam@json:string(Code).

-spec jsonify_err(binary(), binary(), binary()) -> gleam@json:json().
jsonify_err(Code, Field, Message) ->
    gleam@json:object(
        [{<<"code"/utf8>>, gleam@json:string(Code)},
            {<<"field"/utf8>>, gleam@json:string(Field)},
            {<<"message"/utf8>>, gleam@json:string(Message)}]
    ).

-spec encode_sign_up_err(users@signup:sign_up_err()) -> gleam@json:json().
encode_sign_up_err(E) ->
    {Code, Field, Message} = case E of
        username_taken ->
            {<<"USERNAME_TAKEN"/utf8>>,
                <<"username"/utf8>>,
                <<"This username is already being used. Please choose a different one."/utf8>>};

        username_too_short ->
            {<<"USERNAME_TOO_SHORT"/utf8>>,
                <<"username"/utf8>>,
                <<"Username is too short. A minimum of 5 characters is required."/utf8>>};

        password_too_short ->
            {<<"PASSWORD_TOO_SHORT"/utf8>>,
                <<"password"/utf8>>,
                <<"Password is too short. A minimum of 5 characters is required."/utf8>>};

        username_whitespace ->
            {<<"USERNAME_WHITESPACE"/utf8>>,
                <<"username"/utf8>>,
                <<"Username cannot contain whitespace. Remove any spaces or tabs from your username."/utf8>>};

        {sign_up_failed, S} ->
            {<<"SIGNUP_FAILED"/utf8>>, <<""/utf8>>, S}
    end,
    jsonify_err(Code, Field, Message).

-spec encode_sign_in_err(users@signin:sign_in_err()) -> gleam@json:json().
encode_sign_in_err(E) ->
    {Code, Field, Message} = case E of
        invalid_password ->
            {<<"INVALID_PASSWORD"/utf8>>,
                <<"password"/utf8>>,
                <<"Invalid password. Make sure to create an account (Sign Up) if you don't already have one."/utf8>>};

        invalid_username ->
            {<<"INVALID_USERNAME"/utf8>>,
                <<"password"/utf8>>,
                <<"Invalid username. Make sure to create an account (Sign Up) if you don't already have one. "/utf8>>};

        {sign_in_failed, S} ->
            {<<"SIGNUP_FAILED"/utf8>>, <<""/utf8>>, S}
    end,
    jsonify_err(Code, Field, Message).
