%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 14. 8月 2025 17:32
%%%-------------------------------------------------------------------
-module(line_format).
-author("anlicheng").

%% API
-export([parse/1]).

%% <<"cpu,name=xyz,uuid=\"this,=isx=test,\" key=345,key1=23.5,key2=yes 123457">>,
-spec parse(Metric :: binary()) -> error | {ok, map()}.
parse(Metric) when is_binary(Metric) ->
    case catch lexer(Metric) of
        [[Measurement|Tags], Fields, [Timestamp|_]] ->
            {ok, #{
                <<"measurement">> => Measurement,
                <<"tags">> => parser_keys(Tags),
                <<"fields">> => parser_keys(Fields),
                <<"timestamp">> => Timestamp
            }};
        _ ->
            error
    end.

%% 词法分析：将输入文本转换为标记序列
lexer(Input) ->
    lexer(Input, false, [], [], []).
lexer(<<>>, _Quotes, Current, Acc, Groups) ->
    Last = list_to_binary(lists:reverse(Current)),
    NAcc = lists:reverse([Last|Acc]),
    lists:reverse([NAcc|Groups]);
%% 遇到逗号
lexer(<<$,, Rest/binary>>, Quotes, Current, Acc, Groups) ->
    case Quotes of
        true ->
            lexer(Rest, Quotes, [","|Current], Acc, Groups);
        false ->
            Part = list_to_binary(lists:reverse(Current)),
            lexer(Rest, Quotes, [], [Part|Acc], Groups)
    end;
lexer(<<$\s, Rest/binary>>, Quotes, Current, Acc, Groups) ->
    case Quotes of
        true ->
            lexer(Rest, Quotes, [$\s|Current], Acc, Groups);
        false ->
            Part = list_to_binary(lists:reverse(Current)),
            NAcc = lists:reverse([Part|Acc]),
            lexer(Rest, Quotes, [], [], [NAcc|Groups])
    end;
lexer(<<$\", Rest/binary>>, Quotes, Current, Acc, Groups) ->
    lexer(Rest, not Quotes, [$\"|Current], Acc, Groups);
lexer(<<Char/utf8, Rest/binary>>, Quotes, Current, Acc, Groups) ->
    lexer(Rest, Quotes, [Char|Current], Acc, Groups).

parser_keys(Tokens) ->
    maps:from_list(lists:flatmap(fun(Item) ->
        case binary:split(Item, <<"=">>) of
            [Key, Val] ->
                [{Key, Val}];
            _ ->
                []
        end
                                 end, Tokens)).