src/gleeam_code@internal@leetcode.erl

-module(gleeam_code@internal@leetcode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeam_code/internal/leetcode.gleam").
-export([describe_error/1, fetch_problem/2, resolve_slug/2]).
-export_type([problem/0, fetch_error/0, snippet/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-type problem() :: {problem,
        binary(),
        binary(),
        binary(),
        binary(),
        binary(),
        boolean(),
        binary(),
        list(binary())}.

-type fetch_error() :: {http_error, binary()} |
    {json_error, binary()} |
    premium_no_auth |
    premium_no_access |
    no_content |
    no_erlang_snippet |
    problem_not_found.

-type snippet() :: {snippet, binary(), binary()}.

-file("src/gleeam_code/internal/leetcode.gleam", 35).
?DOC(false).
-spec describe_error(fetch_error()) -> binary().
describe_error(Err) ->
    case Err of
        {http_error, Msg} ->
            <<"HTTP error: "/utf8, Msg/binary>>;

        {json_error, Msg@1} ->
            <<"JSON parse error: "/utf8, Msg@1/binary>>;

        premium_no_auth ->
            <<"Premium problem. Run 'glc auth' to authenticate."/utf8>>;

        premium_no_access ->
            <<"Premium problem. Your account may not have Premium access."/utf8>>;

        no_content ->
            <<"Failed to fetch problem content."/utf8>>;

        no_erlang_snippet ->
            <<"No Erlang code snippet available for this problem."/utf8>>;

        problem_not_found ->
            <<"Problem not found."/utf8>>
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 84).
?DOC(false).
-spec send_graphql(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        binary()} |
    {error, fetch_error()}.
send_graphql(Body, Session) ->
    Base_req = begin
        _pipe = gleam@http@request:new(),
        _pipe@1 = gleam@http@request:set_method(_pipe, post),
        _pipe@2 = gleam@http@request:set_host(_pipe@1, <<"leetcode.com"/utf8>>),
        _pipe@3 = gleam@http@request:set_path(_pipe@2, <<"/graphql"/utf8>>),
        _pipe@4 = gleam@http@request:set_scheme(_pipe@3, https),
        _pipe@5 = gleam@http@request:set_body(_pipe@4, Body),
        gleam@http@request:prepend_header(
            _pipe@5,
            <<"content-type"/utf8>>,
            <<"application/json"/utf8>>
        )
    end,
    Req = case Session of
        {ok, Cookie} ->
            _pipe@6 = Base_req,
            gleam@http@request:prepend_header(
                _pipe@6,
                <<"cookie"/utf8>>,
                <<"LEETCODE_SESSION="/utf8, Cookie/binary>>
            );

        {error, _} ->
            Base_req
    end,
    case gleam@httpc:send(Req) of
        {ok, Resp} ->
            case erlang:element(2, Resp) of
                200 ->
                    {ok, erlang:element(4, Resp)};

                Status ->
                    {error,
                        {http_error,
                            <<"status "/utf8,
                                (gleam@string:inspect(Status))/binary>>}}
            end;

        {error, _} ->
            {error, {http_error, <<"failed to connect"/utf8>>}}
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 208).
?DOC(false).
-spec snippet_decoder() -> gleam@dynamic@decode:decoder(snippet()).
snippet_decoder() ->
    gleam@dynamic@decode:field(
        <<"langSlug"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Lang_slug) ->
            gleam@dynamic@decode:field(
                <<"code"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_string/1},
                fun(Code) ->
                    gleam@dynamic@decode:success({snippet, Lang_slug, Code})
                end
            )
        end
    ).

-file("src/gleeam_code/internal/leetcode.gleam", 214).
?DOC(false).
-spec find_erlang_snippet(list(snippet())) -> {ok, binary()} |
    {error, fetch_error()}.
find_erlang_snippet(Snippets) ->
    case gleam@list:find(
        Snippets,
        fun(S) -> erlang:element(2, S) =:= <<"erlang"/utf8>> end
    ) of
        {ok, Snippet} ->
            {ok, erlang:element(3, Snippet)};

        {error, _} ->
            {error, no_erlang_snippet}
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 155).
?DOC(false).
-spec parse_full_problem(binary(), binary()) -> {ok, problem()} |
    {error, fetch_error()}.
parse_full_problem(Body, Content) ->
    Decoder = gleam@dynamic@decode:at(
        [<<"data"/utf8>>, <<"question"/utf8>>],
        begin
            gleam@dynamic@decode:field(
                <<"questionFrontendId"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_string/1},
                fun(Frontend_id) ->
                    gleam@dynamic@decode:field(
                        <<"titleSlug"/utf8>>,
                        {decoder, fun gleam@dynamic@decode:decode_string/1},
                        fun(Title_slug) ->
                            gleam@dynamic@decode:field(
                                <<"title"/utf8>>,
                                {decoder,
                                    fun gleam@dynamic@decode:decode_string/1},
                                fun(Title) ->
                                    gleam@dynamic@decode:field(
                                        <<"difficulty"/utf8>>,
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_string/1},
                                        fun(Difficulty) ->
                                            gleam@dynamic@decode:field(
                                                <<"codeSnippets"/utf8>>,
                                                gleam@dynamic@decode:list(
                                                    snippet_decoder()
                                                ),
                                                fun(Snippets) ->
                                                    gleam@dynamic@decode:field(
                                                        <<"exampleTestcaseList"/utf8>>,
                                                        gleam@dynamic@decode:list(
                                                            {decoder,
                                                                fun gleam@dynamic@decode:decode_string/1}
                                                        ),
                                                        fun(Testcases) ->
                                                            gleam@dynamic@decode:success(
                                                                {Frontend_id,
                                                                    Title_slug,
                                                                    Title,
                                                                    Difficulty,
                                                                    Snippets,
                                                                    Testcases}
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ),
    case gleam@json:parse(Body, Decoder) of
        {error, _} ->
            {error, {json_error, <<"failed to parse problem fields"/utf8>>}};

        {ok,
            {Frontend_id@1,
                Title_slug@1,
                Title@1,
                Difficulty@1,
                Snippets@1,
                Testcases@1}} ->
            case find_erlang_snippet(Snippets@1) of
                {error, Err} ->
                    {error, Err};

                {ok, Erlang_code} ->
                    {ok,
                        {problem,
                            Frontend_id@1,
                            Title_slug@1,
                            Title@1,
                            Difficulty@1,
                            Content,
                            false,
                            Erlang_code,
                            Testcases@1}}
            end
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 131).
?DOC(false).
-spec parse_question_fields(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        problem()} |
    {error, fetch_error()}.
parse_question_fields(Body, Session) ->
    Content_decoder = gleam@dynamic@decode:at(
        [<<"data"/utf8>>, <<"question"/utf8>>, <<"content"/utf8>>],
        gleam@dynamic@decode:optional(
            {decoder, fun gleam@dynamic@decode:decode_string/1}
        )
    ),
    Is_paid_decoder = gleam@dynamic@decode:at(
        [<<"data"/utf8>>, <<"question"/utf8>>, <<"isPaidOnly"/utf8>>],
        {decoder, fun gleam@dynamic@decode:decode_bool/1}
    ),
    Content_result = gleam@json:parse(Body, Content_decoder),
    Is_paid_result = gleam@json:parse(Body, Is_paid_decoder),
    case {Content_result, Is_paid_result} of
        {{ok, none}, {ok, true}} ->
            case Session of
                {ok, _} ->
                    {error, premium_no_access};

                {error, _} ->
                    {error, premium_no_auth}
            end;

        {{ok, none}, _} ->
            {error, no_content};

        {{ok, {some, Content}}, _} ->
            parse_full_problem(Body, Content);

        {_, _} ->
            {error, {json_error, <<"failed to parse content fields"/utf8>>}}
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 114).
?DOC(false).
-spec parse_problem_response(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        problem()} |
    {error, fetch_error()}.
parse_problem_response(Body, Session) ->
    Question_decoder = gleam@dynamic@decode:at(
        [<<"data"/utf8>>, <<"question"/utf8>>],
        gleam@dynamic@decode:optional(gleam@dynamic@decode:success(nil))
    ),
    case gleam@json:parse(Body, Question_decoder) of
        {error, _} ->
            {error, {json_error, <<"invalid response structure"/utf8>>}};

        {ok, Option} ->
            case Option of
                none ->
                    {error, problem_not_found};

                {some, _} ->
                    parse_question_fields(Body, Session)
            end
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 48).
?DOC(false).
-spec fetch_problem(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        problem()} |
    {error, fetch_error()}.
fetch_problem(Slug, Session) ->
    Query = <<<<"{\"query\":\"query questionContent($titleSlug: String!) { question(titleSlug: $titleSlug) { questionFrontendId titleSlug title difficulty content isPaidOnly codeSnippets { lang langSlug code } exampleTestcaseList }}\",\"variables\":{\"titleSlug\":\""/utf8,
            Slug/binary>>/binary,
        "\"}}"/utf8>>,
    gleam@result:'try'(
        send_graphql(Query, Session),
        fun(Resp) -> parse_problem_response(Resp, Session) end
    ).

-file("src/gleeam_code/internal/leetcode.gleam", 221).
?DOC(false).
-spec parse_slug_response(binary(), binary()) -> {ok, binary()} |
    {error, fetch_error()}.
parse_slug_response(Body, Number) ->
    Decoder = gleam@dynamic@decode:at(
        [<<"data"/utf8>>, <<"questionList"/utf8>>, <<"data"/utf8>>],
        gleam@dynamic@decode:list(
            begin
                gleam@dynamic@decode:field(
                    <<"questionFrontendId"/utf8>>,
                    {decoder, fun gleam@dynamic@decode:decode_string/1},
                    fun(Fid) ->
                        gleam@dynamic@decode:field(
                            <<"titleSlug"/utf8>>,
                            {decoder, fun gleam@dynamic@decode:decode_string/1},
                            fun(Slug) ->
                                gleam@dynamic@decode:success({Fid, Slug})
                            end
                        )
                    end
                )
            end
        )
    ),
    case gleam@json:parse(Body, Decoder) of
        {error, _} ->
            {error, {json_error, <<"failed to parse question list"/utf8>>}};

        {ok, Items} ->
            case gleam@list:find(
                Items,
                fun(Item) -> erlang:element(1, Item) =:= Number end
            ) of
                {ok, {_, Slug@1}} ->
                    {ok, Slug@1};

                {error, _} ->
                    {error, problem_not_found}
            end
    end.

-file("src/gleeam_code/internal/leetcode.gleam", 71).
?DOC(false).
-spec lookup_slug_by_number(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        binary()} |
    {error, fetch_error()}.
lookup_slug_by_number(Number, Session) ->
    Query = <<<<"{\"query\":\"query questionByNumber { questionList: questionList(categorySlug: \\\"\\\" limit: 1 skip: 0 filters: { searchKeywords: \\\""/utf8,
            Number/binary>>/binary,
        "\\\" }) { data { questionFrontendId titleSlug } }}\"}"/utf8>>,
    gleam@result:'try'(
        send_graphql(Query, Session),
        fun(Resp) -> parse_slug_response(Resp, Number) end
    ).

-file("src/gleeam_code/internal/leetcode.gleam", 61).
?DOC(false).
-spec resolve_slug(binary(), {ok, binary()} | {error, binary()}) -> {ok,
        binary()} |
    {error, fetch_error()}.
resolve_slug(Input, Session) ->
    case gleeam_code@internal@resolver:is_numeric(Input) of
        false ->
            {ok, Input};

        true ->
            lookup_slug_by_number(Input, Session)
    end.