%%%------------------------------------
%%% @Module  : lib_consume_rank_act_mod
%%% @Author  :  xiaoxiang
%%% @Created :  2016-11-17
%%% @Description: 本服充值消费活动
%%%------------------------------------
-module(lib_consume_rank_act_mod).

-compile(export_all).

-include("errcode.hrl").
-include("language.hrl").
-include("common.hrl").
-include("consume_rank_act.hrl").
-include("relationship.hrl").
-include("figure.hrl").
-include("role.hrl").
-include("def_module.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").
-include("custom_act.hrl").

make_record(rank_role, [RankType, SubType, RoleId, IsPublic, AwardState, Value, SecVal, TrdVal, Time]) ->
    #rank_role{
        role_key  = {RankType, RoleId},        % {RankType, RoleId}
        rank_type = RankType,                  % 榜单类型
        sub_type  = SubType,                   % 活动子类型
        role_id   = RoleId,                    % 角色id
        is_public = IsPublic,                  % 是否公开金额 0-不公开 1-公开
        award_state = util:bitstring_to_term(AwardState),
        value     = Value,                     % 排序值
        second_value = SecVal,
        third_value = TrdVal,
        time      = Time                       % 时间戳
    }.

init() ->
    %% 个人数据
    List = db_rank_role_select(),
    RoleList = [make_record(rank_role, T) || T <- List],
    %?PRINT("~p~n", [RoleList]),
    F = fun(#rank_role{rank_type = RankType, sub_type = SubType} = RankRole, Maps) ->
        case maps:get({RankType, SubType}, Maps, false) of
            false -> NewList = [RankRole];
            TmpL -> NewList = [RankRole | TmpL]
        end,
        maps:put({RankType, SubType}, NewList, Maps)
        end,
    RoleMaps = lists:foldl(F, maps:new(), RoleList),
    % 个人榜单
    RankMaps = get_standard_sort_rank_maps(rank_role, RoleList),
    RoleLimitList = clean_redundant_rank_data_from_db(rank_role, RankMaps),
    RankLimit = maps:from_list(RoleLimitList),
    #rank_state{role_maps = RoleMaps, rank_maps = RankMaps, rank_limit = RankLimit}.

%% 获得标准排序Maps(被截取了长度) Key:{RankType, ActType} Value:[#rank_role{}]
get_standard_sort_rank_maps(rank_role, RankList) ->
    MapsKeyList = get_sort_rank_maps(rank_role, RankList),
    F = fun({{RankType, SubType}, TmpList}) ->
        MaxRankLen = lib_consume_rank_act:get_max_len(RankType, SubType),
        CfgLim = lib_consume_rank_act:get_rank_limit(RankType, SubType),
        NewRankList0 = lists:sublist(TmpList, MaxRankLen),
        NewRankList = [ RankRole || RankRole <- NewRankList0, RankRole#rank_role.value >= CfgLim ],
        {{RankType, SubType}, NewRankList}
        end,
    NewKeyList = lists:map(F, MapsKeyList),
    maps:from_list(NewKeyList).

%% 分类到Maps,然后对Maps的Value排序 Key:RankType Value:[#rank_role{}]
get_sort_rank_maps(rank_role, RankList) ->
    F = fun(#rank_role{rank_type = RankType, sub_type = SubType} = RankRole, Maps) ->
        case maps:get({RankType, SubType}, Maps, false) of
            false -> NewList = [RankRole];
            List -> NewList = [RankRole | List]
        end,
        maps:put({RankType, SubType}, NewList, Maps)
        end,
    Maps = lists:foldl(F, maps:new(), RankList),
    sort_rank_maps(Maps).

%% 对Maps的Value排序
sort_rank_maps(Maps) ->
    MapsKeyList = maps:to_list(Maps),
    F = fun({{RankType, SubType}, List}) ->
        RankList = sort_rank_list(List),
        {{RankType, SubType}, RankList}
        end,
    NewKeyList = lists:map(F, MapsKeyList),
    NewKeyList.

%% 根据排序值排序
sort_rank_list(List) ->
    F = fun(A, B) ->
        if
            is_record(A, rank_role) ->
                if
                    A#rank_role.value > B#rank_role.value -> true;
                    A#rank_role.value == B#rank_role.value ->
                        A#rank_role.time =< B#rank_role.time;
                    true -> false
                end;
            true ->
                false
        end
        end,
    NewList = lists:sort(F, List),
    F1 = fun(Member, {TempList, Value}) ->
        if
            is_record(Member, rank_role) ->
                NewMember = Member#rank_role{rank = Value},
                NewTempList = [NewMember | TempList],
                {NewTempList, Value + 1};
            true ->
                {TempList, Value}
        end
         end,
    {NewRankList, _} = lists:foldl(F1, {[], 1}, NewList),
    lists:reverse(NewRankList).

%% 阈值
clean_redundant_rank_data_from_db(rank_role, RankMaps) ->
    MapsKeyList = maps:to_list(RankMaps),
    F = fun
            ({{RankType, SubType}, []}) ->
                Limit = lib_consume_rank_act:get_rank_limit(RankType, SubType),
                {{RankType, SubType}, Limit};
            ({{RankType, SubType}, RankList}) ->
                RankMax = lib_consume_rank_act:get_max_len(RankType, SubType),
                Limit = lib_consume_rank_act:get_rank_limit(RankType, SubType),
                case length(RankList) >= RankMax of
                    true ->
                        RankLast = lists:last(RankList),
                        Value = RankLast#rank_role.value,
                        {{RankType, SubType}, max(Value, Limit)};
                    false ->
                        {{RankType, SubType}, Limit}
                end
        end,
    lists:map(F, MapsKeyList).

%% 更新玩家公开状态
update_role_public(State, RankType, SubType, RoleId, IsPublic) ->
    #rank_state{rank_maps = RankMaps, role_maps = RoleMaps} = State,
    RoleList = maps:get({RankType, SubType}, RoleMaps, []),
    RankList = maps:get({RankType, SubType}, RankMaps, []),
    case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RoleList) of
        false ->
            RankRole = lib_consume_rank_act:make_record(RankType, SubType, RoleId, 0),
            NewRankRole = RankRole#rank_role{is_public = IsPublic},
            db_rank_role_save(NewRankRole),
            NewRankMaps = RankMaps;
        RankRole ->
            NewRankRole = RankRole#rank_role{is_public = IsPublic},
            db_rank_role_update(NewRankRole),
            case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RankList) of
                false ->
                    NewRankMaps = RankMaps;
                TmpRankRole ->
                    TmpNewRankRole = TmpRankRole#rank_role{is_public = IsPublic},
                    NewRankList = lists:keystore({RankType, RoleId}, #rank_role.role_key, RankList, TmpNewRankRole),
                    NewRankMaps = maps:put({RankType, SubType}, NewRankList, RankMaps)
            end
    end,
    NewRoleList = lists:keystore({RankType, RoleId}, #rank_role.role_key, RoleList, NewRankRole),
    NewRoleMaps = maps:put({RankType, SubType}, NewRoleList, RoleMaps),
    NewState = State#rank_state{rank_maps = NewRankMaps, role_maps = NewRoleMaps},
    lib_server_send:send_to_uid(RoleId, pt_331, 33134, [?SUCCESS]),
    send_rank_list(NewState, RankType, SubType, RoleId),
    {ok, NewState}.

%% 更新奖励领取状态
update_participate_award(State, RoleId, RankType, SubType, GradeId) ->
    #rank_state{role_maps = RoleMaps} = State,
    RoleList = maps:get({RankType, SubType}, RoleMaps, []),
    case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RoleList) of
        false ->
            NewState = State;
        #rank_role{award_state  = AwardState} = RankRole ->
            NewAwardState = lists:keystore(GradeId, 1, AwardState, {GradeId, 1}),
            NewRankRole = RankRole#rank_role{award_state  = NewAwardState},
            db_rank_role_save(NewRankRole),
            NewRoleList = lists:keystore({RankType, RoleId}, #rank_role.role_key, RoleList, NewRankRole),
            NewRoleMaps = maps:put({RankType, SubType}, NewRoleList, RoleMaps),
            NewState = State#rank_state{role_maps = NewRoleMaps}
    end,
    {ok, NewState}.


refresh_common_rank(State, RankType, SubType, #rank_role{role_id = RoleId, value = _Value} = RankRole) ->
    %?PRINT("~p~n", [_Value]),
    #rank_state{rank_maps = RankMaps, role_maps = RoleMaps, rank_limit = RankLimit} = State,
    RankList = maps:get({RankType, SubType}, RankMaps, []),
    %% 更新个人值maps 增加值
    RoleList = maps:get({RankType, SubType}, RoleMaps, []),
    case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RoleList) of
        false ->
            NewRankRole = RankRole;
        #rank_role{is_public = IsPublic} ->
            NewRankRole = RankRole#rank_role{is_public = IsPublic}
    end,
    NewRoleList = lists:keystore({RankType, RoleId}, #rank_role.role_key, RoleList, NewRankRole),
    NewRoleMaps = maps:put({RankType, SubType}, NewRoleList, RoleMaps),
    {NewRankList, NewRankLimit} = do_refresh_rank_help(RankList, RankType, SubType, NewRankRole, RankLimit),
    NewRankMaps = maps:put({RankType, SubType}, NewRankList, RankMaps),
    NewState = State#rank_state{rank_maps = NewRankMaps, role_maps = NewRoleMaps, rank_limit = NewRankLimit},
    %%--------db-------------
    db_rank_role_save(RankRole),
    {ok, NewState}.

%% 先判断list中是否存在key和value都相等的玩家，存在则跟新数据，不存在则将其进行队列排序：将其与队
%% 尾比较，若小于队尾并且队列长度大于最长长度，则丢弃，否则将其加入队列，排序并保证队列长度为最大
%% 队列长度，发送数据的时候，先截取显示长度，再判断是否满足limit，满足条件的才加入发送队列。
%% 时刻保证，排序之后队列和数据库保存的数据为整个服的前N，然后在这之中选择发送
do_refresh_rank_help(RankList, RankType, SubType, RankRole, RankLimit) ->
    #rank_role{role_key = RoleKey, value = Value} = RankRole,
    %% 阈值
    Limit = lib_consume_rank_act:get_rank_limit(RankType, SubType),
    %% 最大长度
    MaxRankLen = lib_consume_rank_act:get_max_len(RankType, SubType),
    Temp = maps:get({RankType, SubType}, RankLimit, Limit),
    %% 最终阈值
    LastLimit = max(Temp, Limit),
    case RankList == [] of
        true ->
            if
                Value >= LastLimit ->
                    {[RankRole#rank_role{rank = 1}], RankLimit};
                true ->
                    {[], RankLimit}
            end;
        false ->
            IsExistKey = lists:keymember(RankRole#rank_role.role_key, #rank_role.role_key, RankList),
            % 是否需要排序
            case Value >= LastLimit orelse IsExistKey of
                false ->
                    {RankList, RankLimit};
                true ->
                    NewRankList = lists:keystore(RoleKey, #rank_role.role_key, RankList, RankRole),
                    % 排序
                    NewRankList2 = sort_rank_list(NewRankList),
                    LastList = lists:sublist(NewRankList2, MaxRankLen),
                    case length(NewRankList2) > MaxRankLen of
                        true ->
                            case LastList of
                                [] -> LastRankRole = #rank_role{};
                                _ -> LastRankRole = lists:last(LastList)
                            end,
                            #rank_role{value = TempLastLimit} = LastRankRole,
                            NewLastLimit = max(TempLastLimit, LastLimit);
                        false -> NewLastLimit = LastLimit
                    end,
                    NewRankLimit = maps:put({RankType, SubType}, NewLastLimit, RankLimit),
                    {LastList, NewRankLimit}
            end
    end.

send_rank_list(State, RankType, SubType, RoleId) ->
    %% 配置的最大长度和上榜阈值
   % ?PRINT("~p~n", [State]),
    Limit = lib_consume_rank_act:get_rank_limit(RankType, SubType),
    MaxRankLen = lib_consume_rank_act:get_max_len(RankType, SubType),
    #rank_state{rank_maps = RankMaps, role_maps = RoleMaps} = State,
    %% 自己在活动期间的值
    RoleList = maps:get({RankType, SubType}, RoleMaps, []),
    case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RoleList) of
        #rank_role{value = SelValue, is_public = IsPublic} -> skip;
        _ -> SelValue = 0, IsPublic = 0
    end,
    RankList = maps:get({RankType, SubType}, RankMaps, []),
    case lists:keyfind({RankType, RoleId}, #rank_role.role_key, RankList) of
        #rank_role{rank = RoleRank} -> skip;
        _ -> RoleRank = 0
    end,
    SubRankList = lists:sublist(RankList, MaxRankLen),
    List1 = [RankRole || RankRole <- SubRankList, RankRole#rank_role.value >= Limit],
    %?PRINT("~p~n", [List1]),
    F = fun(#rank_role{role_id = TmpRoleId, is_public = TmpIsPublic, value = Value, rank = Rank}) ->
        Figure = case lib_role:get_role_show(TmpRoleId) of
                     [] -> #figure{};
                     #ets_role_show{figure = TmpFigure} -> TmpFigure
                 end,
        {TmpRoleId, Figure, TmpIsPublic, Value, Rank}
        end,
    List = lists:map(F, List1),
    Sum = length(List),
    if
        Sum == 0 ->
            lib_server_send:send_to_uid(RoleId, pt_331, 33135, [RankType, 0, IsPublic, SelValue, 0, MaxRankLen, Limit, []]);
        true ->
            %?PRINT("~p~n", [[RankType, RoleRank, SelValue, Sum, MaxRankLen, Limit, List]]),
            lib_server_send:send_to_uid(RoleId, pt_331, 33135, [RankType, RoleRank, IsPublic, SelValue, Sum, MaxRankLen, Limit, List])
    end.

clear_recharge_rank_act(State, EndType, SubType) ->
    clear_act_by_rank_type(State, EndType, ?RANK_TYPE_RECHARGE, SubType).

clear_consume_rank_act(State, EndType, SubType) ->
    clear_act_by_rank_type(State, EndType, ?RANK_TYPE_CONSUME, SubType).

%% 发送本服充值榜奖励（活动结束时触发）
clear_act_by_rank_type(State, EndType, RankType, SubType) ->
    #rank_state{rank_maps = RankMaps, role_maps = RoleMaps} = State,
    RankList = maps:get({RankType, SubType}, RankMaps, []),
    RoleList = maps:get({RankType, SubType}, RoleMaps, []),
    % Pred = fun(#rank_role{role_id = RoleId}) -> not lists:keymember(RoleId, #rank_role.role_id, RankList) end,
    % FiltList = lists:filter(Pred, RoleList),
    case EndType of
        ?CUSTOM_ACT_STATUS_CLOSE ->
            %% 排行奖励
            spawn(fun()-> util:multiserver_delay(30, ?MODULE, send_rank_reward, [RankList, RankType, SubType, 1]) end),
            %% 最低奖励
            spawn(fun()-> util:multiserver_delay(30, ?MODULE, send_role_reward, [RoleList, RankType, SubType, 1]) end);
        _ -> skip
    end,
    %% 清空活动数据
    NewState = clear_act_data(State, RankType, SubType),
    {ok, NewState}.

send_rank_reward([], _RankType, _SubType, _) -> skip;
send_rank_reward([RankRole | RankList], RankType, SubType, Count) when is_record(RankRole, rank_role) ->
    #rank_role{role_id = RoleId, rank = Rank, value = Value} = RankRole,
     %%传闻
    if
        Rank =< 3, Rank > 0 ->
            case lib_role:get_role_show(RoleId) of
                [] -> ?ERR("role show no role id, [~p]~n", [RoleId]);
                #ets_role_show{figure = #figure{name = Name}} ->
                    case RankType of
                        ?RANK_TYPE_RECHARGE -> lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 24, [Name, Rank]);
                        ?RANK_TYPE_CONSUME ->  lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 25, [Name, Rank])
                    end
            end;
        true -> skip
    end,
    case lib_consume_rank_act:get_rank_reward(RankType, SubType, RankRole) of
        [] -> send_rank_reward(RankList, RankType, SubType, Count);
        Reward when is_list(Reward) ->
            case RankType of
                ?RANK_TYPE_RECHARGE ->  Title = ?LAN_MSG(3310020), Content = uio:format(?LAN_MSG(3310021), [Rank]);
                ?RANK_TYPE_CONSUME ->  Title = ?LAN_MSG(3310027), Content = uio:format(?LAN_MSG(3310028), [Rank])
            end,
            lib_mail_api:send_sys_mail([RoleId], Title, Content, Reward),
            %% 日志
            lib_log_api:log_recharge_consume_rank_act(RoleId, RankType, SubType, Rank, Value, Reward),
            case Count rem 20 of
                0 -> timer:sleep(20);
                _ -> skip
            end,
            send_rank_reward(RankList, RankType, SubType, Count + 1);
        _ -> skip
    end;
send_rank_reward(_, _, _, _) -> skip.


send_role_reward([], _RankType, _SubType, _Count) -> skip;
send_role_reward([RankRole | RankList], RankType, SubType, Count) when is_record(RankRole, rank_role) ->
    #rank_role{role_id = RoleId, rank = Rank, value = Value} = RankRole,
    Tmp = lib_consume_rank_act:get_reward_num(RankType, SubType),
    case Value >= Tmp of
        true ->
            case lib_consume_rank_act:get_rank_reward(RankType, SubType, RankRole) of
                [] ->
                    send_role_reward(RankList, RankType, SubType, Count);
                Reward when is_list(Reward) ->
                    Title = ?IF(RankType =:= ?RANK_TYPE_RECHARGE, ?LAN_MSG(3310020), ?LAN_MSG(3310027)),
                    Content = uio:format(?LAN_MSG(3310024), []),
                    lib_mail_api:send_sys_mail([RoleId], Title, Content, Reward),
                    %% 日志
                    lib_log_api:log_recharge_consume_rank_act(RoleId, RankType, SubType, Rank, Value, Reward),
                    case Count rem 20 of
                        0 -> timer:sleep(20);
                        _ ->skip
                    end,
                    send_role_reward(RankList, RankType, SubType, Count + 1);
                _ ->
                    send_role_reward(RankList, RankType, SubType, Count)
            end;
        false ->
            send_role_reward(RankList, RankType, SubType, Count)
    end;
send_role_reward(_, _, _, _) -> skip.

%% 结算本次活动（活动结束时触发）
clear_act_data(State, RankType, SubType) ->
    #rank_state{rank_maps = RankMaps, role_maps = RoleMaps, rank_limit = Limit} = State,
    NewRankMaps = maps:remove({RankType, SubType}, RankMaps),
    NewRoleMaps = maps:remove({RankType, SubType}, RoleMaps),
    NewLimit = maps:remove({RankType, SubType}, Limit),
    NewState = State#rank_state{rank_maps = NewRankMaps, role_maps = NewRoleMaps, rank_limit = NewLimit},
    db:execute(io_lib:format(?sql_rank_role_clear, [RankType, SubType])),
    NewState.

handle_merge(State) ->
    [ mod_consume_rank_act:clear_recharge_rank_act(0, SubType)
        ||  SubType <- lib_custom_act_api:get_open_subtype_ids(?CUSTOM_ACT_TYPE_RECHARGE_RANK) ],
    [ mod_consume_rank_act:clear_consume_rank_act(0, SubType)
        ||  SubType <- lib_custom_act_api:get_open_subtype_ids(?CUSTOM_ACT_TYPE_CONSUME_RANK) ],
    {ok, State}.

%%-----------------db-----------------------%%

db_rank_role_select() ->
    Sql = io_lib:format(?sql_rank_role_select, []),
    db:get_all(Sql).

db_rank_role_update(RankRole) ->
    #rank_role{
        rank_type = RankType,
        sub_type  = SubType,
        role_id   = RoleId,
        is_public = IsPublic
    } = RankRole,
    Sql = io_lib:format(?sql_rank_role_update, [IsPublic, RankType, SubType, RoleId]),
    db:execute(Sql).

db_rank_role_save(RankRole) ->
    #rank_role{
        rank_type = RankType,
        sub_type  = SubType,
        role_id   = RoleId,
        is_public = IsPublic,
        award_state = AwardState,
        value     = Value,
        second_value = SecVal,
        third_value = TrdVal,
        time      = Time
    } = RankRole,
    Sql = io_lib:format(?sql_rank_role_save,
        [RankType, SubType, RoleId, IsPublic, util:term_to_bitstring(AwardState), Value, SecVal, TrdVal, Time]),
    db:execute(Sql).

db_rank_delete_by_id(RankType, SubType, RoleId) ->
    Sql = io_lib:format(?sql_rank_role_delete_by_role_id, [RankType, SubType, RoleId]),
    db:execute(Sql).

