%%-----------------------------------------------------------------------------
%% @Module  :       lib_red_envelopes_mod
%% @Author  :       Czc
%% @Email   :       389853407@qq.com
%% @Created :       2017-09-20
%% @Description:    红包(通用框架)
%%-----------------------------------------------------------------------------
-module(lib_red_envelopes_mod).

-include("red_envelopes.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("def_fun.hrl").
-include("def_module.hrl").
-include("def_id_create.hrl").
-include("predefine.hrl").
-include("goods.hrl").
-include("role.hrl").
-include("def_goods.hrl").
-export([
    init/0
    , make_record/2
    % , send_red_envelopes/6
    , add_red_envelopes/1
    , open_red_envelopes/5
    , daily_clear_red_envelopes/0
    , add_expired_log/2
    ,handout_red_envelopes/4
    ]).

make_record(sql_red_envelopes, [Id, Type, OwnershipType, OwnershipId, OwnerId, Status, Money, MoneyList, ShareList, SplitNum, PacketId, Greet, Stime, Ctime]) ->
    TermMoneyList = util:bitstring_to_term(MoneyList),
    TermShareList = util:bitstring_to_term(ShareList),
    #red_envelopes{
        id = Id, type = Type, ownership_type = OwnershipType, ownership_id = OwnershipId,
        owner_id = OwnerId, status = Status, money = Money, money_list = TermMoneyList, share_list = TermShareList,
        split_num = SplitNum, packet_id = PacketId, greet = Greet, stime = Stime, ctime = Ctime
    };
make_record(red_envelopes, [Id, Type, OwnershipType, OwnershipId, OwnerId, Status, Money, MoneyList, ShareList, SplitNum, PacketId, Greet, Stime, Ctime]) ->
    #red_envelopes{
        id = Id, type = Type, ownership_type = OwnershipType, ownership_id = OwnershipId,
        owner_id = OwnerId, status = Status, money = Money, money_list = MoneyList, share_list=ShareList,
        split_num = SplitNum, packet_id = PacketId, greet = Greet, stime = Stime, ctime = Ctime
    };
make_record(sql_recipients, [RoleId, RedEnvelopesId, Money, Time]) ->
    #recipients_record{
        role_id = RoleId, red_envelopes_id = RedEnvelopesId,
        money = Money, time = Time
    }.

init() ->
    RedEnvelopesList = db:get_all(io_lib:format(?sql_select_red_envelopes, [])),
    RecipientsList = db:get_all(io_lib:format(?sql_select_recipients_record, [])),
    NowTime = utime:unixtime(),
    RecipientsMap = init_recipients_map(RecipientsList, #{}),
    RedEnvelopesMap = init_red_envelopes_map(RedEnvelopesList, NowTime, RecipientsMap, #{}),
    lib_red_envelopes_data:save_red_envelopes_map(RedEnvelopesMap).

init_red_envelopes_map([], _NowTime, _RecipientsMap, RedEnvelopesMap) -> RedEnvelopesMap;
init_red_envelopes_map([T|RList], NowTime, RecipientsMap, RedEnvelopesMap) ->
    RedEnvelopes = make_record(sql_red_envelopes, T),
    #red_envelopes{
        id = RedEnvelopesId,
        ownership_type = OwnershipType,
        ownership_id = OwnershipId,
        type = Type
    } = RedEnvelopes,
    IsValid = lib_red_envelopes:is_vaild_red_envelopes(RedEnvelopes, NowTime),
    IsVip = Type == ?RED_ENVELOPES_TYPE_VIP_PLAYER_SEND,
    case IsValid orelse IsVip of
        true ->
            OneTypeMap = maps:get(OwnershipType, RedEnvelopesMap, #{}),
            List = maps:get(OwnershipId, OneTypeMap, []),
            RecipientsList = maps:get(RedEnvelopesId, RecipientsMap, []),
            NewRedEnvelopes = RedEnvelopes#red_envelopes{recipients_num = length(RecipientsList), recipients_lists = RecipientsList},
            NewList = lists:reverse([NewRedEnvelopes|List]),
            NewOneTypeMap = maps:put(OwnershipId, NewList, OneTypeMap),
            NewRedEnvelopesMap = maps:put(OwnershipType, NewOneTypeMap, RedEnvelopesMap),
            init_red_envelopes_map(RList, NowTime, RecipientsMap, NewRedEnvelopesMap);
        false -> %% 已经过期的不加载
            init_red_envelopes_map(RList, NowTime, RecipientsMap, RedEnvelopesMap)
    end.

%% 初始化红包领取Map
init_recipients_map([], RecipientsMap) -> RecipientsMap;
init_recipients_map([T|List], RecipientsMap) ->
    Recipients = make_record(sql_recipients, T),
    #recipients_record{red_envelopes_id = RedEnvelopesId} = Recipients,
    RecipientsList = maps:get(RedEnvelopesId, RecipientsMap, []),
    NewRecipientsMap = maps:put(RedEnvelopesId, [Recipients|RecipientsList], RecipientsMap),
    init_recipients_map(List, NewRecipientsMap).

%% 增加新的红包
add_red_envelopes(Args) ->
    [Type, OwnershipType, OwnershipId, OwnerId, Status, Money, MoneyList, SplitNum, PacketId, _GuildLv, Greet, ShareList] = Args,
    NowTime = utime:unixtime(),
    Stime = case Status of
        ?NO_SEND -> 0;
        _ -> NowTime
    end,
    AutoId = mod_id_create:get_new_id(?RED_ENVELOPES_ID_CREATE),
    RedEnvelopes = lib_red_envelopes_mod:make_record(red_envelopes,
        [AutoId, Type, OwnershipType, OwnershipId, OwnerId, Status, Money, MoneyList, ShareList, SplitNum, PacketId, Greet, Stime, NowTime]),
    lib_red_envelopes_data:db_insert_red_envelopes(RedEnvelopes),

    %% 日志
    % LogExtraArgs = ?IF(Status == ?HAS_SEND, [{money, Money}, {num, SplitNum}], [{money, Money}]),
    LogExtraArgs = [{money, Money}, {num, SplitNum}],
    lib_log_api:log_red_envelopes(AutoId, OwnershipType, OwnershipId, OwnerId, Status, Type, LogExtraArgs),

    lib_red_envelopes_data:add_red_envelopes(RedEnvelopes),
    %% 推送新的红包给客户端
    PackList = lib_red_envelopes:pack_red_envelopes_list(OwnerId, [RedEnvelopes]),
    % NewRedEnvelopes = lib_red_envelopes_data:get_red_envelopes(OwnershipType, OwnershipId, AutoId),
    %% 通知客户端有新的红包
    lib_red_envelopes:notify_client(?NEW_RED_ENVELOPES, RedEnvelopes),
    {ok, BinData} = pt_339:write(33904, [PackList]),
    lib_server_send:send_to_guild(OwnershipId, BinData).

%%红包发送
handout_red_envelopes(OwnershipType,OwnershipId,RoleID, RedEnvelopesId)->
    NowTime = utime:unixtime(),
    TmpRedEnvelopes = lib_red_envelopes_data:get_red_envelopes(OwnershipType, OwnershipId, RedEnvelopesId),
    RedEnvelopes = ?IF(is_record(TmpRedEnvelopes, red_envelopes), TmpRedEnvelopes, #red_envelopes{}),
    IsVaildRedEnvelopes = lib_red_envelopes:is_vaild_red_envelopes(RedEnvelopes, NowTime),
    #red_envelopes{status = Status,owner_id=OwnID} = RedEnvelopes,
    if
        is_record(TmpRedEnvelopes, red_envelopes) == false ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_not_exist));
        IsVaildRedEnvelopes == false ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_not_exist));
        OwnID /= RoleID ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_not_owner));
        Status == ?HAS_SEND ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_is_limit));
        Status == ?EXPIRED ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_has_expired));
        Status == ?END ->
            lib_red_envelopes:send_error_code_by_uid(RoleID, ?ERRCODE(err339_not_exist));
        true ->
            LastRedEnvelopes = RedEnvelopes#red_envelopes{status=?HAS_SEND,stime=NowTime},
            lib_red_envelopes_data:add_red_envelopes(LastRedEnvelopes),
            db:execute(io_lib:format(?sql_update_red_envelopes_status1, [?HAS_SEND,NowTime,RedEnvelopesId])),
            %% 推送新的红包给客户端
            PackList = lib_red_envelopes:pack_red_envelopes_list(RoleID, [LastRedEnvelopes]),
            %% 通知客户端有新的红包
            lib_red_envelopes:notify_client(?NEW_RED_ENVELOPES, LastRedEnvelopes),
            {ok, BinData} = pt_339:write(33904, [PackList]),
            lib_server_send:send_to_guild(OwnershipId, BinData)
    end.

%% 打开红包
open_red_envelopes(OwnershipType, OwnershipId, RoleId, RedEnvelopesId, Args) ->
    NowTime = utime:unixtime(),
    TmpRedEnvelopes = lib_red_envelopes_data:get_red_envelopes(OwnershipType, OwnershipId, RedEnvelopesId),
    RedEnvelopes = ?IF(is_record(TmpRedEnvelopes, red_envelopes), TmpRedEnvelopes, #red_envelopes{}),
    IsVaildRedEnvelopes = lib_red_envelopes:is_vaild_red_envelopes(RedEnvelopes, NowTime),
    #red_envelopes{packet_id=CfgId,status = Status, stime = Stime, recipients_lists = RecipientsList} = RedEnvelopes,
    %%每日领取上限
    {BgoldPacket, GoldPacket} = Args,
    case data_red_envelopes:get_envelope(CfgId) of
        #base_red_envelopes_cfg{kind=Kind}->
            skip;
        _->
            Kind = 0
    end,
    RecipientsRecord = lists:keyfind(RoleId, #recipients_record.role_id, RecipientsList),
    HasReceive = is_record(RecipientsRecord, recipients_record),
    BgoldLimit = data_guild:get(red_packet_limit),
    GoldLimit = data_guild:get(red_gold_packet_limit),
    if
        is_record(TmpRedEnvelopes, red_envelopes) == false ->
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_not_exist));
        IsVaildRedEnvelopes == false ->
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_not_exist));
        Status == ?NO_SEND ->
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_not_send));
        Status == ?EXPIRED orelse (Status == ?HAS_SEND andalso NowTime >= Stime + ?DEL_AF_SEND_TIME) ->
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_has_expired));
        Status == ?HAS_SEND andalso HasReceive == false andalso Kind == ?TYPE_GOLD andalso GoldPacket>= GoldLimit ->
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_is_limit));
        Status == ?HAS_SEND andalso HasReceive == false andalso Kind == ?TYPE_BGOLD andalso BgoldPacket >= BgoldLimit -> %%其它类型都是绑钻 201912 支持coin
            lib_red_envelopes:send_error_code_by_uid(RoleId, ?ERRCODE(err339_is_limit));
        true ->
            case receive_red_envelopes(RoleId, RedEnvelopes, NowTime, RecipientsRecord, Args) of
                {ok, ObtainMoney, NewRedEnvelopes} -> do_open_red_envelopes(RoleId, NewRedEnvelopes, ObtainMoney);
                {false, ErrCode} -> lib_red_envelopes:send_error_code_by_uid(RoleId, ErrCode);
                _ -> skip
            end
    end.

do_open_red_envelopes(RoleId, NewRedEnvelopes, ObtainMoney) ->
    #red_envelopes{owner_id = OwnerId, greet = Greet, type = Type, packet_id = PacketId, id = RedEnvelopesId} = NewRedEnvelopes,
    #figure{
        name = OwnerName, career = OwnerCareer, sex = OwnerSex, turn = OwnerTurn,
        picture = OwnerPic, picture_ver = OwnerPicVer, profile_photo_id = Photo
    } = Figure = lib_role:get_role_figure(OwnerId),
    FrameId = lib_dress_up:get_role_frame_dress(Figure),
    LastRedEnvelopes = add_look_role(NewRedEnvelopes, RoleId),
    lib_red_envelopes_data:add_red_envelopes(LastRedEnvelopes),
    PackList = lib_red_envelopes:pack_recipients_record(LastRedEnvelopes#red_envelopes.recipients_lists),
    {ok, BinData} = pt_339:write(33902, [RedEnvelopesId, PacketId, OwnerId, OwnerName, OwnerCareer, OwnerSex, OwnerTurn, OwnerPic, OwnerPicVer,
        Photo, FrameId, LastRedEnvelopes#red_envelopes.status, ObtainMoney, LastRedEnvelopes#red_envelopes.split_num,
    LastRedEnvelopes#red_envelopes.recipients_num, LastRedEnvelopes#red_envelopes.money, Type, Greet, PackList]),
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, lib_red_envelopes, send_got_num, []),
    lib_server_send:send_to_uid(RoleId, BinData).

%% 领取红包
receive_red_envelopes(_RoleId, RedEnvelopes, _NowTime, #recipients_record{money = Money}, _Args) ->
    {ok, Money, RedEnvelopes};
receive_red_envelopes(_RoleId, #red_envelopes{status = ?END} = RedEnvelopes, _NowTime, _HasReceive, _Args) ->
    {ok, 0, RedEnvelopes};
receive_red_envelopes(RoleId, #red_envelopes{type = ?RED_ENVELOPES_TYPE_BONFIRE} = RedEnvelopes, NowTime, _HasReceive, _Args) ->
    case lists:member(RoleId, RedEnvelopes#red_envelopes.share_list) of
        true-> do_receive_red_envelopes(RoleId, RedEnvelopes, NowTime, normal);
        false->
            {ok, 0, RedEnvelopes}
    end;
receive_red_envelopes(RoleId, #red_envelopes{packet_id=CfgId} = RedEnvelopes, NowTime, _HasReceive, Args) ->
    {BgoldPacket, GoldPacket} = Args,
    BgoldLimit = data_guild:get(red_packet_limit),
    GoldLimit = data_guild:get(red_gold_packet_limit),
    #base_red_envelopes_cfg{kind=Kind} = data_red_envelopes:get_envelope(CfgId),
    case Kind of
        ?TYPE_GOLD->
            case GoldPacket>= GoldLimit of
                true -> {ok, 0, RedEnvelopes};
                false -> do_receive_red_envelopes(RoleId, RedEnvelopes, NowTime, normal)
            end;
        ?TYPE_BGOLD->
            case BgoldPacket>= BgoldLimit of
                true -> {ok, 0, RedEnvelopes};
                false -> do_receive_red_envelopes(RoleId, RedEnvelopes, NowTime, normal)
            end;
        _->
           do_receive_red_envelopes(RoleId, RedEnvelopes, NowTime, normal)
    end.

do_receive_red_envelopes(RoleId, RedEnvelopes, NowTime, normal) ->
    #red_envelopes{
        id = RedEnvelopesId,
        type = Type,
        ownership_type = OwnershipType,
        ownership_id = OwnershipId,
        owner_id = OwnerId,
        status = ?HAS_SEND,
        split_num = SplitNum,
        recipients_num = RecipientsNum,
        recipients_lists = RecipientsList,
        packet_id = PacketId,
        stime = Stime
    } = RedEnvelopes,
    Classify = lib_red_envelopes:get_red_envelopes_classify(Type),
    MoneyType = lib_red_envelopes:count_money_type(Classify, PacketId),
    {ObtainMoney, LeftMoneyList} = lib_red_envelopes:count_obtain_money(RedEnvelopes),
    NowTimes = lib_daily:get_count_offline(RoleId, ?MOD_RED_ENVELOPES, 1),
    Limit = lib_daily:get_count_limit(?MOD_RED_ENVELOPES, 1),
    case NowTimes<Limit of
        true->
            F = fun() ->
                db:execute(io_lib:format(?sql_insert_recipients_record,
                    [RoleId, RedEnvelopesId, OwnershipId, ObtainMoney, Stime, NowTime])),
                %% 如果红包领完了要更新状态
                NewStatus = case RecipientsNum + 1 == SplitNum of
                    true ->
                        db:execute(io_lib:format(?sql_update_red_envelopes_status, [?END, Stime, util:term_to_bitstring(LeftMoneyList), RedEnvelopesId])),
                        %% 日志
                        lib_log_api:log_red_envelopes(RedEnvelopesId, OwnershipType, OwnershipId, OwnerId, ?END, Type, []),
                        ?END;
                    false ->
                        db:execute(io_lib:format(?sql_update_red_envelopes_money_list,[util:term_to_bitstring(LeftMoneyList), RedEnvelopesId])),
                        ?HAS_SEND
                end,
                {ok, NewStatus}
            end,
            case catch db:transaction(F) of
                {ok, NewStatus} ->
                    AddRecipientsRecord = make_record(sql_recipients,
                        [RoleId, RedEnvelopesId, ObtainMoney, NowTime]),
                    NewRecipientsList = [AddRecipientsRecord|RecipientsList],
                    %% 红包领取日志
                    lib_log_api:log_red_envelopes_recipients(RoleId, RedEnvelopesId, [{MoneyType, 0, ObtainMoney}]),
                    lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, lib_red_envelopes, refresh_and_send_reward, [MoneyType, ObtainMoney]),
                    NewRedEnvelopes = RedEnvelopes#red_envelopes{
                        status = NewStatus, recipients_num = RecipientsNum + 1, recipients_lists = NewRecipientsList, money_list=LeftMoneyList},
                    case NewStatus == ?END of
                        true-> send_receive_max_tv(NewRecipientsList, OwnershipId);
                        false-> ok
                    end,
                    ObtainMoney>0 andalso lib_daily:increment_offline(RoleId, ?MOD_RED_ENVELOPES, 1),
                    {ok, ObtainMoney, NewRedEnvelopes};
                _Err ->
                    ?ERR("receive red envelopes err:~p", [_Err]),
                    {false, ?ERRCODE(system_busy)}
            end;
        _->
            {ok,0,RedEnvelopes}
    end;
do_receive_red_envelopes(_RoleId, _RedEnvelopes, _NowTime, _) ->
    {false, ?MISSING_CONFIG}.

send_receive_max_tv(RecipientsList, OwnershipId) ->
    SortList = lists:keysort(#recipients_record.money, RecipientsList),
    case SortList =/= [] of
        true->
            #recipients_record{role_id = RoleId, money = Money} = lists:last(SortList),
            case lib_role:get_role_show(RoleId) of
                #ets_role_show{figure=Figure} ->
                    Name = Figure#figure.name;
                _ -> Name = ""
            end,
            lib_chat:send_TV({guild, OwnershipId}, ?MOD_RED_ENVELOPES, 3, [Name, Money]);
        false-> ok
    end.

add_look_role(#red_envelopes{type = ?RED_ENVELOPES_TYPE_BONFIRE, has_look_lists = HasLookList} = RedEnvelopes, RoleId) ->
    NewHasLook = [RoleId|lists:delete(RoleId, HasLookList)],
    RedEnvelopes#red_envelopes{has_look_lists = NewHasLook};
add_look_role(RedEnvelopes, _RoleId) -> RedEnvelopes.

%% 日常清理红包数据
daily_clear_red_envelopes() ->
    NowTime = utime:unixtime(),
    ExpiredStime = NowTime - ?DEL_AF_SEND_TIME,
    % AutoSendAFCtime = NowTime - ?AUTO_SEND_AF_TIME,
    db:execute(io_lib:format(?sql_del_red_envelopes_by_stime, [ExpiredStime])),
    %% 清理过期的红包领取记录
    db:execute(io_lib:format(?sql_del_red_envelopes_recipients_record_by_stime, [ExpiredStime])),
    RedEnvelopesMap = lib_red_envelopes_data:get_red_envelopes_map(),
    F = fun() ->
        %% 设置一个通知客户端新红包的缓存
        % put("notify_client_cache", []),
        put("expired_list", []),
        F1 = fun(OwnershipId, Val, TmpMap) ->
            % List = auto_send_red_envelopes(Val, NowTime, ExpiredStime, AutoSendAFCtime, Counter, []),
            List = auto_del_expire_envelopes(Val, ExpiredStime, []),
            maps:put(OwnershipId, List, TmpMap)
        end,
        F2 = fun(OwnershipType, Val, TmpMap) ->
            NewVal = maps:fold(F1, #{}, Val),
            maps:put(OwnershipType, NewVal, TmpMap)
        end,
        NewRedEnvelopesMap = maps:fold(F2, #{}, RedEnvelopesMap),
        ExpiredList = erase("expired_list"),
        % CacheList = erase("notify_client_cache"),
        {ok, ExpiredList, NewRedEnvelopesMap}
    end,
    case catch db:transaction(F) of
        {ok, ExpiredList, NewRedEnvelopesMap} ->
            lib_red_envelopes_data:save_red_envelopes_map(NewRedEnvelopesMap),
            %% 在新的子进程处理 避免阻塞主进程
            spawn(fun() ->
                add_expired_log(ExpiredList, [{expired}])
                % do_af_auto_send_red_envelopes(CacheList, 1)
            end);
        Err ->
            ?ERR("red envelopes daily clear err:~p", [Err])
    end.

%%移除过期的红包
auto_del_expire_envelopes([], _ExpiredStime, List) -> List;
auto_del_expire_envelopes([OneVal|T], ExpiredStime, List) ->
    case OneVal of
        #red_envelopes{status = Status, stime = Stime} ->
            case Status =/= ?NO_SEND andalso Stime =< ExpiredStime of
                true->
                    auto_back_reward(OneVal),
                    ExpiredList = get("expired_list"),
                    put("expired_list", [OneVal|ExpiredList]),
                    auto_del_expire_envelopes(T, ExpiredStime, List);
                false-> auto_del_expire_envelopes(T, ExpiredStime, [OneVal|List])
            end;
        _ -> auto_del_expire_envelopes(T, ExpiredStime, List)
    end.

%%红包过期返回
auto_back_reward(RedEnvelopes) when RedEnvelopes#red_envelopes.type==?RED_ENVELOPES_TYPE_VIP_PLAYER_SEND->
    #red_envelopes{owner_id=RoleId, packet_id = PacKetId, recipients_lists = RecipientsList} = RedEnvelopes,
    #base_vip_red_envelopes_cfg{money = Money} = data_red_envelopes:get_vip(PacKetId),
    Num = calc_left_money(Money, RecipientsList),
    case Num > 0 of
        true ->
            Reward = [{?TYPE_BGOLD, 0, Num}],
            Remark = lists:concat(["vip_expire_back:"]),
            Produce = #produce{ type = red_envelopes, reward=Reward, show_tips=3, remark = Remark},
            lib_goods_api:send_reward_with_mail(RoleId, Produce, ?MOD_RED_ENVELOPES);
        false -> ok
    end;
auto_back_reward(_RedEnvelopes) -> ok.

%%计算剩下红包金额
calc_left_money(Money, []) -> Money;
calc_left_money(Money, [#recipients_record{money = GotMoney}| T]) ->
    calc_left_money(max(0, Money-GotMoney), T).

% %% 添加红包过期被删除的日志
add_expired_log([], _) -> ok;
add_expired_log([T|L], Args) ->
    case T of
        #red_envelopes{
            id = RedEnvelopesId, type = Type, status = Status,
            ownership_type = OwnershipType, ownership_id = OwnershipId, owner_id = OwnerId
        } ->
            %% 日志
            lib_log_api:log_red_envelopes(RedEnvelopesId, OwnershipType, OwnershipId, OwnerId, ?EXPIRED, Type, [{pre_status, Status}|Args]),
            add_expired_log(L, Args);
        _ -> add_expired_log(L, Args)
    end.








