%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2024, <COMPANY>
%%% @doc
%%% 中电的签名逻辑
%%% @end
%%% Created : 02. 9月 2024 11:28
%%%-------------------------------------------------------------------
-module(iot_jinzhi_signer).
-author("anlicheng").

%% API
-export([sign/2]).

%% 数据签名
-spec sign(M :: #{}, PrivateKey :: public_key:private_key()) -> binary().
sign(M, PrivateKey) when is_map(M) ->
    Json = serialize(M),
    Hash = iolist_to_binary(io_lib:format("~64.16.0b", [binary:decode_unsigned(crypto:hash(sha256, Json))])),
    RsaEncoded = public_key:encrypt_private(Hash, PrivateKey),

    base64:encode(RsaEncoded).

%% 简单的序列号，sign签名
-spec serialize(M :: map()) -> JsonString :: binary().
serialize(M) when is_map(M) ->
    L = maps:to_list(M),
    L1 = lists:sort(fun({K, _}, {K1, _}) -> K < K1 end, L),
    serialize(L1, []).
serialize([], Target) ->
    B = iolist_to_binary(lists:join(<<$,>>, lists:reverse(Target))),
    <<${, B/binary, $}>>;
serialize([{K, V}|T], Target) ->
    V1 = if
             is_integer(V) ->
                 integer_to_binary(V);
             is_float(V) ->
                 float_to_binary(V);
             is_binary(V) ->
                 <<$", V/binary, $">>;
             is_boolean(V) andalso V ->
                 <<"true">>;
             is_boolean(V) andalso not V ->
                 <<"false">>;
             is_list(V) ->
                 Items = lists:map(fun(E) -> serialize(E) end, V),
                 V0 = iolist_to_binary(lists:join(<<$,>>, Items)),
                 <<$[, V0/binary, $]>>
         end,
    Item = <<$", K/binary, $", $:, V1/binary>>,
    serialize(T, [Item|Target]).