%% -------------------------------------------
%% module     :custom_act_single_recharge.erl
%% desc       :每日单笔充值
%% author     :hh
%% time       :20/08/18
%% -------------------------------------------

-module(custom_act_single_recharge).


-export([
    count_reward_progress/4
    ,check_reward_receive_times/3
    ,check_receive_reward_conditions/3
    ,receive_reward/4
    ,act_end/1
    ,refresh_act_list_status/3
    ,role_settlement_do/3
    ,handle_event/2
    ]).

-export([
    gm_act_end/1
    ,gm_reset_role_progress/1
    ,db_refresh_single_charge_support_data/1]).

-include("server.hrl").
-include("custom_act.hrl").
-include("rec_recharge.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("language.hrl").
-include("def_module.hrl").
-include("common.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").

handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{id = RoleId} = Player,
    RoleSingleCharge = load_single_charge_support_data(RoleId),
    NewPlayer = Player#player_status{role_single_charge = RoleSingleCharge},
    {ok, NewPlayer};
handle_event(Player, _) ->
    {ok, Player}.

load_single_charge_support_data(RoleId) ->
    case db:get_all(<<"SELECT subtype, grade_id, receive_count, total_count, time FROM role_single_charge WHERE role_id = ~p">>, [RoleId]) of
        Data = [_|_] ->
            lists:foldl(fun([SubType, GradeId, ReceiveCount, TotalCount, Time], Acc) -> 
                [{{SubType, GradeId}, ReceiveCount, TotalCount, Time}|Acc]
            end, [], Data);
        _ ->
            []
    end.

%% 单笔充值奖励领取次数是否达到限制
%% @doc return false 表示已经领取
%%      return 其他  表示未达条件可领取或者可领取
check_reward_receive_times(Player, #act_info{key = {_Type, SubType}, etime = EndTime}, RwCfg) ->
    #player_status{role_single_charge = RoleSingleCharge} = Player,
    #custom_act_reward_cfg{grade = GradeId, condition = Condition} = RwCfg,
    case lib_custom_act_check:check_act_condtion([times_lim], Condition) of
        [TimesLim|_] when TimesLim > 0 ->
            case lists:keyfind({SubType, GradeId}, 1, RoleSingleCharge) of
                {_, ReceiveCount, TotalCount, _FirstTime} ->
                    CanReceiveTimes = min(TotalCount, TimesLim),
                    Now = utime:unixtime(),
                    if Now >= EndTime -> false;
                        true ->
                            case ReceiveCount >= CanReceiveTimes andalso CanReceiveTimes >= TimesLim of
                                true -> false;
                                _    -> {true, ReceiveCount+1}
                            end
                    end;
                _ ->
                    {true, 1}
            end;
        _ -> 
            false
    end.


%% 每日单笔充值检测领奖条件是否满足
%% @doc return  true 表示可领取
%%      return  其他 表示不可领取:未达条件或已经领完
check_receive_reward_conditions(Player, #act_info{key = {_Type, SubType}}, RwCfg) ->
    #player_status{role_single_charge = RoleSingleCharge} = Player,
    #custom_act_reward_cfg{grade = GradeId, condition = Condition} = RwCfg,
    case lib_custom_act_check:check_act_condtion([gold_range, times_lim], Condition) of
        [[_MinGold, _MaxGold], TimesLim|_] when TimesLim > 0 ->
            case lists:keyfind({SubType, GradeId}, 1, RoleSingleCharge) of
                {_, ReceiveCount, TotalCount, _FirstTime} ->
                    CanReceiveTimes = min(TotalCount, TimesLim),
                    case ReceiveCount < CanReceiveTimes of
                        true -> true;
                        _    -> {false, ?ERRCODE(err331_act_can_not_get)}
                    end;
                _ -> %% 数据不正常
                    {false, ?ERRCODE(err331_act_can_not_get)}
            end;
        _ -> {false, ?ERRCODE(err_config)}
    end.


%% 获取满足特定档次的单笔充值
get_charge_data(RoleId, StartTime, MinGold, MaxGold) ->
    %% 暂定个人触发活动当天开始零点
    % UnixDate = utime:unixdate(StartTime),
    DailyRecharges = lib_recharge_data:get_daily_recharge_detail(RoleId, StartTime),
    lists:foldl(fun(#daily_recharge{detail = Details}, Acc) -> 
        FilterDetails = lists:filter(fun([_TimeX, GoldX|_]) -> 
            GoldX >= MinGold andalso GoldX =< MaxGold
        end, Details),
        lists:append(FilterDetails, Acc)
    end, [], DailyRecharges).


%% 领取奖励
receive_reward(Player, ActType, SubType, GradeId) ->
    #player_status{sid = Sid} = Player,
    case lib_custom_act_check:check_receive_reward(Player, ActType, SubType, GradeId) of
        {true, ActInfo, NewReceiveTimes, RewardList, RewardCfg} ->
            %% 先更新次数 避免更新次数出问题玩家重复领取
            Player1 = lib_custom_act_do_normal:update_receive_times_normal(Player, ActType, SubType, GradeId, NewReceiveTimes),
            case lib_custom_act_do_normal:do_receive_reward(Player1, ActInfo, RewardList, RewardCfg) of
                {false, ErrCode} -> NewPlayer = Player1;
                NewPlayer -> ErrCode = ?SUCCESS
            end;
        {false, ErrCode} -> NewPlayer = Player
    end,
    %% 通知前端更新
    {ok, BinData} = pt_331:write(33105, [ErrCode, ActType, SubType, GradeId]),
    lib_server_send:send_to_sid(Sid, BinData),
    % ?ERR("~p", [ErrCode]),
    %% 更新充值档位领取次数数据
    if
        ErrCode == 1 ->
            NewPlayer1 = update_role_act_recharge_count(NewPlayer, SubType, GradeId);
        true ->
            NewPlayer1 = NewPlayer
    end,
    {ok, NewPlayer1}.

%% 更新单笔充值辅助数据
update_role_act_recharge_count(Player, SubType, GradeId) ->
    #player_status{id = RoleId, role_single_charge = RoleSingleCharge} = Player,
    case lists:keyfind({SubType, GradeId}, 1, RoleSingleCharge) of
        {_, ReceiveCount, TotalCount, Time} ->
            NewRoleSingleCharge = lists:keyreplace({SubType, GradeId}, 1, RoleSingleCharge, {{SubType, GradeId}, ReceiveCount+1, TotalCount, Time}),
            db_refresh_single_charge_support_data([{RoleId, SubType, GradeId, ReceiveCount+1, TotalCount, Time}]),
            Player#player_status{role_single_charge = NewRoleSingleCharge};
        _ ->
            Player
    end.


%% 活动结束时帮未领取完奖励的玩家发放奖励
act_end(ActInfo) -> 
    act_end(normal, ActInfo).

act_end(GmType, #act_info{key = {?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, SubType}}) ->
    spawn_link(fun() ->
        GmType == normal andalso timer:sleep(180000),
        case catch db_get_act_role(SubType) of
            ActRoles = [_|_] ->
                ActRoles1 = calc_act_roles(ActRoles),
                OfflineActRoles = role_settlement(ActRoles1, SubType),
                offline_role_settlement(OfflineActRoles, SubType);
            _A -> %% 无参与活动的玩家
                ok
        end,
        %% 正常结算删除数据,秘籍结算保留数据
        GmType == normal andalso db:execute(<<"DELETE FROM role_single_charge WHERE subtype = ~p">>, [SubType]),
        GmType == gm andalso db:execute(<<"UPDATE role_single_charge SET receive_count = 0, time = ~p WHERE subtype = ~p">>, [utime:unixtime(), SubType])
    end);
act_end(_GmType, _ActInfo) ->
    ok.

db_get_act_role(SubType) ->
    % db:get_all(<<"SELECT `player_id`, `gold` FROM `recharge_log` WHERE time >= ~p and time <= ~p">>, [StartTime, EndTime]).
    db:get_all(<<"SELECT role_id, grade_id, receive_count, total_count FROM role_single_charge WHERE subtype = ~p">>, [SubType]).

calc_act_roles(Roles) ->
    lists:foldl(fun([RoleId, GradeId, ReceiveCount, TotalCount], Acc) -> 
        case lists:keyfind(RoleId, 1, Acc) of
            {_, RoleL} ->
                lists:keystore(RoleId, 1, Acc, {RoleId, [{GradeId, ReceiveCount, TotalCount}|RoleL]});
            _ ->
                lists:keystore(RoleId, 1, Acc, {RoleId, [{GradeId, ReceiveCount, TotalCount}]})
        end
    end, [], Roles).

role_settlement(Roles, SubType) ->
    lists:foldl(fun({RoleId, RoleL}, Acc) -> 
        Pid = misc:get_player_process(RoleId),
        % GradeId, ReceiveCount, TotalCount
        case is_pid(Pid) andalso misc:is_process_alive(Pid) of
            true ->
                lib_player:apply_cast(Pid, ?APPLY_CAST_STATUS, ?MODULE, role_settlement_do, [SubType, RoleL]),
                Acc;
            _ ->
                [{RoleId, RoleL}|Acc]
        end
    end, [], Roles).

%% 在线玩家结算未领取奖励
role_settlement_do(Player, SubType, RoleL) ->
    #player_status{id = RoleId} = Player,
    case lib_custom_act_util:get_act_reward_cfg_list(?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, SubType) of
        RwCfgL = [_|_] ->
            NewRewards = lists:foldl(fun({GradeId, ReceiveCount, TotalCount}, Acc) ->
                Rewards = calc_act_receiving_reward(GradeId, ReceiveCount, TotalCount, RwCfgL),
                lists:append(Rewards, Acc)
             end, [], RoleL),
            Title = ?LAN_MSG(3314801),
            Content = uio:format(?LAN_MSG(3314802), []),
            NewRewards =/= [] andalso
             spawn(fun() -> 
                timer:sleep(urand:rand(1000, 5000)), 
                lib_mail_api:send_sys_mail([RoleId], ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, Title, Content, NewRewards) end),
            ok;
        _ ->
            ok
    end.

%% 计算未领取奖励
calc_act_receiving_reward(GradeId, ReceiveCount, TotalCount, RwCfgL) ->
    case lists:keyfind(GradeId, #custom_act_reward_cfg.grade, RwCfgL) of
        #custom_act_reward_cfg{condition = Condition, reward = Rewards} ->
            case lib_custom_act_check:check_act_condtion([times_lim], Condition) of
                [TimesLim|_] when TimesLim > 0 ->
                    TotalCanReceiveCount = min(TimesLim, TotalCount),
                    if ReceiveCount < TotalCanReceiveCount ->
                            [{GoodsType, GoodsTypeId, round(Num*max(1, TotalCanReceiveCount - ReceiveCount))} || {GoodsType, GoodsTypeId, Num} <- Rewards];
                        true ->
                            []
                    end;
                _ -> %% 配置有误
                    []
            end;
        _ ->
            []
    end.

%% 离线玩家结算未领取奖励
offline_role_settlement(Roles, SubType) ->
    spawn_link(fun() ->
        timer:sleep(120000),
        case lib_custom_act_util:get_act_reward_cfg_list(?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, SubType) of
            RwCfgL = [_|_] ->
                offline_role_settlement_do(Roles, RwCfgL, SubType, 0);
            _ ->
                ok
        end
    end).

offline_role_settlement_do([], _, _, _) -> ok;
offline_role_settlement_do([{RoleId, RoleL}|Rest], RwCfgL, SubType, Index) ->
    if
        Index =/= 0 andalso Index rem 10 == 0 ->
            timer:sleep(urand:rand(2000, 5000));
        true ->
            ok
    end,
    % ?ERR("~p ~p", [RoleId, RoleL]),
    offline_role_settlement_do_logic(RoleId, RoleL, RwCfgL, SubType),
    offline_role_settlement_do(Rest, RwCfgL, SubType, Index+1).

% GradeId, ReceiveCount, TotalCount
offline_role_settlement_do_logic(RoleId, RoleL, RwCfgL, _SubType) ->
    NewRewards = lists:foldl(fun({GradeId, ReceiveCount, TotalCount}, Acc) ->
        Rewards = calc_act_receiving_reward(GradeId, ReceiveCount, TotalCount, RwCfgL),
        lists:append(Rewards, Acc)
    end, [], RoleL),
    Title = ?LAN_MSG(3314801),
    Content = uio:format(?LAN_MSG(3314802), []),
    NewRewards =/= [] andalso
     lib_mail_api:send_sys_mail([RoleId], ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, Title, Content, NewRewards),
    ok.

%% 充值变动时更新数据
refresh_act_list_status(Player, _NowTime, #callback_recharge_data{gold = Gold}) ->
    Type = ?CUSTOM_ACT_TYPE_SINGLE_RECHARGE,
    StartActs = lib_custom_act_api:get_open_custom_act_infos(Type),
    case StartActs of
        [_|_] ->
            #player_status{id = RoleId, role_single_charge = RoleSingleCharge} = Player,
            Now = utime:unixtime(),
            {NewRoleSingleCharge, SubSingleChargeL} = refresh_act_list_status_do(RoleId, Gold, StartActs, RoleSingleCharge, Now, []),
            db_refresh_single_charge_support_data(SubSingleChargeL),
            NewPlayer = Player#player_status{role_single_charge = NewRoleSingleCharge},
            [lib_custom_act_do_normal:send_reward_status(NewPlayer, ActInfo) || ActInfo <- StartActs],
            NewPlayer;
        _ ->
            Player
    end.

%% 更新单笔充值档次对应次数
refresh_act_list_status_do(_RoleId, _Gold, [], RoleSingleCharge, _, DbList) ->
    {RoleSingleCharge, DbList};
refresh_act_list_status_do(RoleId, Gold, [#act_info{key = {Type, SubType}}|Rest], RoleSingleCharge, Now, DbList) ->
    case lib_custom_act_util:get_act_reward_cfg_list(Type, SubType) of
        RwCfgL = [_|_] ->
            case calc_gold_grade(Gold, RwCfgL) of
                GoldGrades = [_|_] ->
                    {NewRoleSingleCharge, NewDbList} =
                    lists:foldl(fun(GradeId, {TempRoleSingleCharge, TempDbList}) -> 
                        case lists:keyfind({SubType, GradeId}, 1, TempRoleSingleCharge) of
                            {_, ReceiveCount, TotalCount, Time} ->
                                NewGradeData = {{SubType, GradeId}, ReceiveCount, TotalCount+1, Time},
                                NewTempRoleSingleCharge = lists:keystore({SubType, GradeId}, 1, TempRoleSingleCharge, NewGradeData),
                                NewTempDbList = [{RoleId, SubType, GradeId, ReceiveCount, TotalCount+1, Time}|TempDbList];
                            _ ->
                                %% 要将个人开始前的同一天充值数据计算在内的话,则要先计算之前的档位充值次数.暂定此方案
                                TodayCount = calc_today_before_act_charge_count(RoleId, GradeId, RwCfgL, Now),
                                NewTotalCount = TodayCount+1,
                                NewGradeData = {{SubType, GradeId}, 0, NewTotalCount, Now},
                                NewTempRoleSingleCharge = lists:keystore({SubType, GradeId}, 1, TempRoleSingleCharge, NewGradeData),
                                NewTempDbList = [{RoleId, SubType, GradeId, 0, NewTotalCount, Now}|TempDbList]
                        end,
                        {NewTempRoleSingleCharge, NewTempDbList}
                    end, {RoleSingleCharge, DbList}, GoldGrades),
                    refresh_act_list_status_do(RoleId, Gold, Rest, NewRoleSingleCharge, Now, NewDbList);
                _ ->
                    refresh_act_list_status_do(RoleId, Gold, Rest, RoleSingleCharge, Now, DbList)
            end;
        _ ->
            refresh_act_list_status_do(RoleId, Gold, Rest, RoleSingleCharge, Now, DbList)
    end.

calc_today_before_act_charge_count(RoleId, GradeId, RwCfgL, PersonStartTime) ->
    case lists:keyfind(GradeId, #custom_act_reward_cfg.grade, RwCfgL) of
        #custom_act_reward_cfg{condition = Condition} ->
            case lib_custom_act_check:check_act_condtion([gold_range], Condition) of
                [[MinGold, MaxGold]|_] ->
                    ChargeL = get_charge_data(RoleId, PersonStartTime, MinGold, MaxGold),
                    length(ChargeL);
                _ ->
                    0
            end;
        _ ->
            0
    end.

db_refresh_single_charge_support_data(SubSingleChargeL) ->
    SqlList = [
    io_lib:format(<<"(~p, ~p, ~p, ~p, ~p, ~p)">>,
        [RoleId, SubType, GradeId, ReceiveCount, TotalCount, Time]) || {RoleId, SubType, GradeId, ReceiveCount, TotalCount, Time} <- SubSingleChargeL],
    SQL = io_lib:format(<<"REPLACE INTO role_single_charge(`role_id`, `subtype`, `grade_id`, `receive_count`, `total_count`, `time`) VALUES ~ts">>, [string:join(SqlList, ",")]),
    SqlList =/= [] andalso db:execute(SQL),
    ok.

%% 获取本次单笔充值满足的档位需求
calc_gold_grade(Gold, RwCfgL) ->
    lists:foldl(fun(#custom_act_reward_cfg{grade = Grade, condition = Condition}, Acc) -> 
        case lib_custom_act_check:check_act_condtion([gold_range], Condition) of
            [[MinGold, MaxGold]|_] ->
                if 
                    Gold >= MinGold andalso Gold =< MaxGold ->
                        [Grade|Acc];
                    true -> Acc
                end;
            _ -> Acc
        end
    end, [], RwCfgL).


%% 获取档次奖励领取进度
count_reward_progress(Player, #act_info{key = {_Type, SubType}}, CommonProgress, #custom_act_reward_cfg{grade = GradeId}) ->
    #player_status{role_single_charge = RoleSingleCharge} = Player,
    case lists:keyfind({SubType, GradeId}, 1, RoleSingleCharge) of
        {_, ReceiveTime0, _TotalCount, _FirstTime} ->
            ReceiveTimes = ReceiveTime0;
        _ ->
            ReceiveTimes = CommonProgress
    end,
    ReceiveTimes.

%% 秘籍手动结算活动
gm_act_end(SubType) ->
    Type = ?CUSTOM_ACT_TYPE_SINGLE_RECHARGE,
    case lib_custom_act_api:get_open_custom_act_infos(Type) of
        StartActs = [_|_] ->
            case lists:keyfind({Type, SubType}, #act_info.key, StartActs) of
                ActInfo = #act_info{} ->
                    act_end(gm, ActInfo);
                _ ->
                    ok
            end;
        _ ->
            ok
    end.

gm_reset_role_progress(Player) ->
    #player_status{
        id = RoleId, status_custom_act = StatusCustomAct = #status_custom_act{reward_map = RewardMap},
        role_single_charge = RoleSingleCharge} = Player,
    SingleChargeType = ?CUSTOM_ACT_TYPE_SINGLE_RECHARGE,
    %% 更新个人奖励领取次数(重置)
    {NewRewardMap, Reset} = maps:fold(fun({Type, SubType, GradeId}, RewardStatus, {TempMap, NeedReset}) ->
        if Type == SingleChargeType ->
                NewTempMap = maps:put({Type, SubType, GradeId}, RewardStatus#reward_status{receive_times = 0}, TempMap),
                case lists:member({SubType, GradeId}, NeedReset) of
                    true ->
                        {NewTempMap, NeedReset};
                    _ ->
                        {NewTempMap, [{SubType, GradeId}|NeedReset]}
                end;
            true ->
                {TempMap, NeedReset}
        end
    end, {RewardMap, []}, RewardMap),
    % ?ERR("~p", [NewRewardMap]),
    [db:execute(?delete_custom_act_reward_receive_by_stype, [SingleChargeType, SubTypeX]) || {SubTypeX, _} <- Reset],
    [db:execute(?insert_custom_act_reward_receive, [RoleId, SingleChargeType, SubTypeX, GradeIdX, RCnt, UtimeX]) 
    || {{_, SubTypeX, GradeIdX}, #reward_status{receive_times = RCnt, utime = UtimeX}} <- maps:to_list(NewRewardMap)],
    %% 更新个人单笔充值辅助数据(重置)
    Now = utime:unixtime(),
    NewRoleSingleCharge = lists:foldl(fun({ResetSubType, ResetGradeId}, TempRoleSingleCharge) ->
        gm_reset_role_single_charge(RoleId, ResetSubType, ResetGradeId, Now, TempRoleSingleCharge)
     end, RoleSingleCharge, Reset),
    Player#player_status{
        status_custom_act = StatusCustomAct#status_custom_act{reward_map = NewRewardMap},
        role_single_charge = NewRoleSingleCharge
    }.


gm_reset_role_single_charge(RoleId, ResetSubType, ResetGradeId, Now, RoleSingleCharge) ->
    case lists:keyfind({ResetSubType, ResetGradeId}, 1, RoleSingleCharge) of
        {_, _ReceiveCount, _TotalCount, _Time} ->
            case lib_custom_act_util:get_act_reward_cfg_list(?CUSTOM_ACT_TYPE_SINGLE_RECHARGE, ResetSubType) of
                RwCfgL = [_|_] ->
                    ChargeCount = calc_today_before_act_charge_count(RoleId, ResetGradeId, RwCfgL, Now),
                    db:execute(<<"REPLACE INTO role_single_charge SET role_id = ~p, subtype = ~p, grade_id = ~p, receive_count = ~p, total_count = ~p, time = ~p">>, 
                        [RoleId, ResetSubType, ResetGradeId, 0, ChargeCount, Now]),
                    lists:keystore({ResetSubType, ResetGradeId}, 1, RoleSingleCharge, {{ResetSubType, ResetGradeId}, 0, ChargeCount, Now});
                _ ->
                    db:execute(<<"DELETE FROM role_single_charge WHERE role_id = ~p AND subtype = ~p AND grade_id = ~p">>, [RoleId, ResetSubType, ResetGradeId]),
                    lists:keydelete({ResetSubType, ResetGradeId}, 1, RoleSingleCharge)
            end;
        _ ->
            RoleSingleCharge
    end.