%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     TODO 直购礼包，合服后清空购买记录
%%% @end
%%%-------------------------------------------------------------------
-module(lib_recharge).
-author("liushl").

-include("server.hrl").
-include("rec_recharge.hrl").
-include("def_recharge.hrl").
-include("common.hrl").
-include("def_cache.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("predefine.hrl").
-include("language.hrl").
-include("def_goods.hrl").
-include("def_module.hrl").
-include("def_fun.hrl").
-include("counter.hrl").
-include("custom_act.hrl").
-include("errcode.hrl").
-include("recharge_act.hrl").
-include("goods.hrl").
-include("figure.hrl").
-include("def_daily.hrl").
-include("task.hrl").
-include("vip.hrl").
-include("offline_hosting.hrl").
%% API
-export([
    pay/1,
    notify_handle_all_orders/0,
    pay_by_goods/3
]).

-export([
    login/2,
    load_data/1,
    deal_with_charge/3,         %% 订单处理
    handle_product/2,
    get_statistic/1
]).

login(Player, Now) ->
    Player1 = load_data(Player),
    case Player1#player_status.is_pay of
        true  ->
            Player2 = update_statistic_without_recharge(Player1, Now),
            OldRfmt = get_statistic(Player1),
            NewRfmt = get_statistic(Player2),
            OldRfmt =/= NewRfmt andalso ta_agent_fire:charge_rfmt(Player2, NewRfmt),
            Player2;
        false ->
            Player1
    end.

load_data(Player) ->
    case db:get_row(?sql_get_recharge_statistic, [Player#player_status.id]) of
        [LastPayTime, TotalMoney, TotalGold, Recency, Frequency, Monetary, Top] ->
            RechargeStatistic = #role_recharge_statistic{
                last_pay_time = LastPayTime
                , total_money = TotalMoney
                , total_gold = TotalGold
                %% 根据上面参数计算的评级
                , recency = Recency
                , frequency = Frequency
                , monetary = Monetary
                , top = Top
            },
            Player1 = Player#player_status{recharge_statistic = RechargeStatistic, is_pay = true, last_pay_time = LastPayTime},
            Player1;
        _ ->
            Player#player_status{recharge_statistic = #role_recharge_statistic{}}
    end.
%% 充值入账游戏服务器
%% 触发情况
%% 1.充值入账php后台后，调用游戏服务器接口
%% 2.定时器每分钟会调用一次该接口

%% 注:pay/1
pay(PS) ->
    #player_status{id = PlayerId} = PS,
    %% 取出玩家所有充值待处理记录
    case catch lib_recharge_data:get_my_all_recharge(PlayerId) of
        [] ->
            {ok, PS};
        PayList when is_list(PayList) ->
            Now = utime:unixtime(),
            NewPS = pay(PS, PayList, Now),
            {ok, NewPS};
        Err ->
            ?ERR("recharge db error:~p~n", [Err]),
            {ok, PS}
    end.

%% 实现充值
pay(PS, [], _) -> PS;
pay(PS, [[Id, _ProductType, ProductId, Money, Gold, _Ctime, PayNo] | PayList], Now) when PayNo =/= undefined ->
    %% 订单预处理
    ProductCfg = pre_deal_with_order(PS, ProductId, Money, Gold),
    NewPS = do_pay(PS, ProductCfg, Id, PayNo, Now),
    pay(NewPS, PayList, Now);
pay(PS, [_T | PayList], Now) ->
    ?ERR("recharge order error, Error = ~p~n", [_T]),
    pay(PS, PayList, Now).

do_pay(Player, ProductCfg, Id, PayNo, Now) ->
    #base_recharge_product{
        money = Money,
        act_value = ActGold
    } = ProductCfg,
    %% 订单通用处理
    case do_pay_common(Player, ProductCfg, Id, PayNo, Now) of
        Player1 when is_record(Player, player_status) ->
            %% 按类型处理
            Player2 = recharge_router:deal_with_by_type(Player1, ProductCfg, PayNo),
            %% 处理关联商品
            Player3 = deal_with_associate(Player2, ProductCfg),
            IsChargeCard = check_charge_card(PayNo),
            %% 其他事件处理!!!触发其他活动时所用的参数为 act_value 专门为活动充值额度配置的字段
            CallBackData = #callback_recharge_data{money = Money, gold = ActGold, recharge_product = ProductCfg, is_charge_card = IsChargeCard},
            {ok, Player4} = lib_player_event:dispatch(Player3, ?EVENT_RECHARGE, CallBackData),
            ta_agent_fire:order_finish(Player4, Now, [Id, ProductCfg, Player]),
            Player4;
        _ ->
            Player
    end.

%% 先统一修改订单数据，记录充值额度, 有元宝则增加元宝
do_pay_common(PS, ProductCfg, Id, PayNo, Now) ->
    #player_status{id = PlayerId} = PS,
    #base_recharge_product{
        product_id = ProductId,
        product_type = Type,
        money = Money,
        game_currency = Gold,
        act_value = ActGold
    } = ProductCfg,
    F = fun() ->
        %% !!!! 修改充值订单状态为已经处理
        lib_recharge_data:finish_recharge(Id),
        %% 充值记录-复制charge表的信息
        lib_recharge_data:db_insert_recharge_log(PS, Type, ProductId, Money, ActGold, Now),
        %% 更新最新充值时间
        lib_recharge_data:db_update_player_last_pay_time(PlayerId, Now),
        %% 刷新充值统计
        NewPS = update_statistic_with_recharge(PS, Money, ActGold, Now),
        OldRfmt = get_statistic(PS),
        NewRfmt = get_statistic(NewPS),
        OldRfmt =/= NewRfmt andalso ta_agent_fire:charge_rfmt(NewPS, NewRfmt),
        {ok, NewPS}
        end,
    case goods_transaction:transaction(F) of
        {ok, Player} ->
            %% 更新缓存-充值总额
            lib_recharge_data:update_total_rmb(PlayerId, Money),
            %% 更新缓存-今日充值钱和游戏币-记录累充额度
            lib_recharge_data:update_daily_recharge(PlayerId, Now, ActGold, Money),
            %% 更新缓存-充值游戏币总额-记录累充额度
            lib_recharge_data:update_total_gold(PlayerId, ActGold),
            %% 增加元宝
            Player1 = case Gold > 0 of
                          true ->
                              LogAbout = lists:concat(["PayNo : ", binary_to_list(PayNo)]),
                              lib_goods_api:add_money(Player, Gold, ?TYPE_GOLD, recharge, LogAbout);
                          false ->
                              Player
                      end,
            %% 记录快速充值日志
            Player2 = add_quick_recharge_log(Player1, ProductId, ActGold, Now),
            Player2#player_status{is_pay = true, last_pay_time = Now};
        Error ->
            ?ERR("recharge error id = ~p error = ~n~p~n", [Id, Error]),
            false
    end.
%% ---------------------------------------------------------------------------
%% @doc 校验订单
%% ---------------------------------------------------------------------------
pre_deal_with_order(Player, ProductId, Money, Gold) ->
    case data_recharge:get_product(ProductId) of
        #base_recharge_product{money = MoneyCfg, product_type = ProductType} = ProductCfg ->
            case Money == MoneyCfg of
                true when ProductType =:= ?PRODUCT_TYPE_NORMAL ->
                    %% 普通充值 ID和价格对得上，则直接返回, 元宝数不读SDK数值，以配置为准(运营)
                    ProductCfg;
                true when ProductType =:= ?PRODUCT_TYPE_WELFARE ->
                    AlreadyHas = lib_recharge_welfare:check_same_type(Player, ProductId),
                    case lib_recharge_check:check_product(Player, ProductCfg) of
                        {true, _} when 1 >= AlreadyHas ->
                            ProductCfg;
                        _ ->
                            %% 条件不符合，则算随心充
                            init_usual_product(ProductId, Money, Gold)
                    end;
                true -> %% 其他类型，继续判断商品
                    case lib_recharge_check:check_product(Player, ProductCfg) of
                        {true, _} ->
                            ProductCfg;
                        _ ->
                            %% 条件不符合，则算随心充
                            init_usual_product(ProductId, Money, Gold)
                    end;
                false ->
                    init_usual_product(ProductId, Money, Gold)

            end;
        _ ->
            %% 匹配不上，算随心充
            init_usual_product(ProductId, Money, Gold)
    end.

init_usual_product(ProductId, Money, Gold) ->
    ?ERR("recharge money error:~p", [{ProductId, Money, Gold}]),
    #base_recharge_product{product_id = 0, product_type = ?PRODUCT_TYPE_NORMAL, money = Money, game_currency = Gold, act_value = Gold}.
%% ---------------------------------------------------------------------------
%% @doc 处理关联商品: 目前是触发首充
%% ---------------------------------------------------------------------------
deal_with_associate(Player, ProductCfg) ->
    AssociateIds = get_associate_ids_helper(ProductCfg#base_recharge_product.associate_list, [], ProductCfg#base_recharge_product.product_id),
    {ok, Player1} = recharge_router:deal_with_associate_product(Player, AssociateIds, ProductCfg),
    Player1.

%% ExcludeId :: 需要排除的商品Id, 即不包含当前使用自身
get_associate_ids_helper([], ResList, _ExcludeId) -> ResList;
get_associate_ids_helper([{ProductType, ProductSubType} | T], ResList, ExcludeId) ->
    IdList = data_recharge:get_product_ids_by_type(ProductType, ProductSubType),
    NewResList = [{ProductType, Id} || Id <- IdList, Id =/= ExcludeId] ++ ResList,
    get_associate_ids_helper(T, NewResList, ExcludeId).

handle_product(Player, _ProductId) ->
    {ok, Player}.
%% ---------------------------------------------------------------------------
%% @doc 常规充值处理
%% ---------------------------------------------------------------------------
deal_with_charge(Player,
        #base_recharge_product{
            product_type = ?PRODUCT_TYPE_NORMAL,
            game_currency = Gold
        } = ProductCfg, _PayNo) ->
    #player_status{id = PlayerId} = Player,
    %% 发邮件
    {{Y, M, D}, {H, N, _S}} = utime:unixtime_to_localtime(utime:unixtime()),
    Title = ?LAN_MSG(?LAN_TITLE_RECHAEGE),
    Content = uio:format(?LAN_MSG(?LAN_CONTENT_RECHAEGE), [Y, M, D, H, N, Gold]),
    lib_mail_api:send_sys_mail([PlayerId], ?MOD_RECHARGE, Title, Content, []),
    %% 触发返利
    {ok, Player1} = lib_recharge_return:check_recharge_return(Player, ProductCfg),
    {ok, Player1};
%% 佣兵补给
deal_with_charge(Player, #base_recharge_product{
    product_id = ProductId,
    product_name = ProductName,
    product_type = ?PRODUCT_TYPE_GIFT,
    product_subtype = ?PRODUCT_TYPE_SELLSWORDS_RESUPPLY
} = _ProductCfg, _PayNo) ->
    case data_recharge:get_recharge_goods(ProductId) of
        #base_recharge_goods{award = Award, limit = Limit, condition = Condition} ->
            #player_status{id = RoleId, figure = Figure} = Player,
            case Limit of
                [?PRODUCT_LIMIT_DAILY, Count] ->
                    Already = lib_daily:get_count(RoleId, ?MOD_RECHARGE, 1, ProductId),
                    lib_daily:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                [?PRODUCT_LIMIT_WEEK, Count] ->
                    Already = lib_week:get_count(RoleId, ?MOD_RECHARGE, 1, ProductId),
                    lib_week:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                [?PRODUCT_LIMIT_LIFE, Count] ->
                    Already = lib_counter:get_count(RoleId, ?MOD_RECHARGE, 1, ProductId),
                    lib_counter:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                _ ->
                    Count = 0, Already = false
            end,
            case lists:keyfind(custom_act, 1, Condition) of
                {custom_act, Type, SubType, GradeId} when Already =/= false andalso is_integer(Already) ->
                    Player1 = lib_custom_act:update_receive_times(Player, Type, SubType, GradeId, Already + 1),
                    case custom_act_personal:is_personal_act_open(Player1, Type, SubType) of
                        {true, ActInfo} when Already + 1 >= Count ->
                            Player2 = custom_act_sellswords_resupply:end_sub_act(Player1, Type, SubType),
                            lib_custom_act_do_normal:send_reward_status(Player2, ActInfo);
                        {true, ActInfo} ->
                            lib_custom_act_do_normal:send_reward_status(Player1, ActInfo),
                            Player2 = Player1;
                        false -> Player2 = Player1
                    end,
                    % 记录日志
                    #figure{lv = Lv, vip = Vip} = Figure,
                    RechargeTotal = lib_recharge_data:get_total_rmb(RoleId),
                    lib_log_api:log_charge_goods([RoleId, ProductId, Type, SubType, ProductName, RechargeTotal, Lv, Vip, utime:unixtime()]),
                    NewPlayer = lib_goods_api:send_reward(Player2, Award, recharge_gift),
                    {ok, NewPlayer};
                _ ->
                    ?ERR("missing recharge goods cfg:~p", [{Player#player_status.id, ProductId}]),
                    {ok, Player}
            end;
        _ ->
            ?ERR("missing recharge goods cfg:~p", [{Player#player_status.id, ProductId}]),
            {ok, Player}
    end;
%% 直购礼包
deal_with_charge(Player, #base_recharge_product{
    product_id = ProductId,
    product_name = ProductName,
    product_type = ?PRODUCT_TYPE_GIFT,
    product_subtype = SubType
} = _ProductCfg, _PayNo) ->
    case data_recharge:get_recharge_goods(ProductId) of
        #base_recharge_goods{award = Award, limit = Limit} ->
            #player_status{id = RoleId, figure = Figure} = Player,
            case Limit of
                [?PRODUCT_LIMIT_DAILY, _Count] ->
                    lib_daily:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                [?PRODUCT_LIMIT_WEEK, _Count] ->
                    lib_week:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                [?PRODUCT_LIMIT_LIFE, _Count] ->
                    lib_counter:increment(RoleId, ?MOD_RECHARGE, 1, ProductId);
                _ ->
                    ok
            end,
            Player1 = lib_goods_api:send_reward(Player, Award, recharge_gift),
            % 记录日志
            #figure{lv = Lv, vip = Vip} = Figure,
            RechargeTotal = lib_recharge_data:get_total_rmb(RoleId),
            lib_log_api:log_charge_goods([RoleId, ProductId, ?PRODUCT_TYPE_GIFT, SubType, ProductName, RechargeTotal, Lv, Vip, utime:unixtime()]),
            pp_recharge_act:handle(15903, Player1, [?PRODUCT_TYPE_GIFT]),
            custom_act_mobilize:update_mobilize_task(RoleId, directbuy, 1),
            {ok, Player1};
        _ ->
            ?ERR("missing recharge goods cfg:~p", [{Player#player_status.id, ProductId}]),
            {ok, Player}
    end;
%% 契约直购
deal_with_charge(Player, _ProductCfg = #base_recharge_product{
    product_id = ProductId,
    product_type = ?PRODDUCT_TYPE_CONTRACT
    }, _PayNo) ->
        case data_recharge:get_recharge_goods(ProductId) of
            #base_recharge_goods{condition = Condition = [{custom_act, _Type, _SubType, _Grade}|_]} ->
                custom_act_contract:recharge_ace_contract(Player, Condition);
            _ ->
                ?ERR("missing recharge goods cfg:~p", [{Player#player_status.id, ProductId}]),
                {ok, Player}
        end;
%% 默认匹配
deal_with_charge(Player, _ProductCfg, _PayNo) ->
    {ok, Player}.

%% ---------------------------------------------------------------------------
%% @doc 通知处理一批待处理的充值订单，准备入账游戏服务器
%% ---------------------------------------------------------------------------
notify_handle_all_orders() ->
    spawn(fun() -> do_notify_handle_all_orders() end),
    ok.

do_notify_handle_all_orders() ->
    case catch lib_recharge_data:get_all_recharge() of
        [] -> skip;
        List when is_list(List) ->
            F = fun([_Id, PlayerId, _CTime], PlayerIdList) ->
                case lists:member(PlayerId, PlayerIdList) of
                    true -> PlayerIdList;
                    _ -> [PlayerId | PlayerIdList]
                end
                end,
            PlayerIdList = lists:foldl(F, [], List),
            notify_handle_player_order_helper(PlayerIdList, 0);
        _ ->
            skip
    end.

%% 处理每个玩家订单
%% 玩家在线：发到该玩家进程，让玩家再从充值表拿出来处理
notify_handle_player_order_helper([], _I) -> ok;
notify_handle_player_order_helper([PlayerId | T], I) ->
    case I rem 10 of
        0 -> timer:sleep(500);
        _ -> skip
    end,
    lib_player:apply_cast(PlayerId, ?APPLY_CAST_STATUS, lib_recharge, pay, []),
    notify_handle_player_order_helper(T, I + 1).

%% ---------------------------------------------------------------------------
%% @doc 游戏内使用物品进行充值
%% ---------------------------------------------------------------------------
pay_by_goods(Player, ProductCfg, GoodsId) ->
    do_pay(Player, ProductCfg, 0, integer_to_binary(GoodsId), utime:unixtime()).

%% ---------------------------------------------------------------------------
%% @doc 充值数据统计
%% ---------------------------------------------------------------------------
%% 充值的时候刷新统计数据
update_statistic_with_recharge(Player, Money, Gold, Now) ->
    #player_status{
        id = RoleId,
        recharge_statistic = RechargeStatistic
    } = Player,
    #role_recharge_statistic{total_money = TotalMoney
        , total_gold = TotalGold
        , top = Top                 %% 评级：最高额充值 (充值时更新）
    } = RechargeStatistic,
    NewTotalMoney = TotalMoney + Money,
    NewTotalGold = TotalGold + Gold,
    RecencyValue = 1,
    FrequencyValue = calc_before_recharge_counts(RoleId, Now) + 1,
    NewTop = data_vsn:calc_recharge_top(Money),
    NewRechargeStatistic =
        RechargeStatistic#role_recharge_statistic{
            last_pay_time = Now
            , total_money = NewTotalMoney
            , total_gold = NewTotalGold
            , recency = data_vsn:calc_recharge_recency(RecencyValue)
            , frequency = data_vsn:calc_recharge_frequency(FrequencyValue)
            , monetary = data_vsn:calc_recharge_monetary(NewTotalMoney)
            , top = ?IF(NewTop > Top, NewTop, Top)
        },
    lib_recharge_data:db_save_recharge_statistic(RoleId, NewRechargeStatistic),
    NewTotalMoney =/= TotalMoney andalso ta_agent_fire:total_pay_amount(Player, Now, NewTotalMoney),
    Player#player_status{recharge_statistic = NewRechargeStatistic}.

%% 非充值的时候刷新统计数据
update_statistic_without_recharge(Player, Now) ->
    #player_status{
        id = RoleId,
        recharge_statistic = RechargeStatistic
    } = Player,
    #role_recharge_statistic{
        last_pay_time = LastPayTime
    } = RechargeStatistic,
    RecencyValue = utime:day_span(LastPayTime, LastPayTime),
    FrequencyValue = calc_before_recharge_counts(RoleId, Now),
    NewRechargeStatistic =
        RechargeStatistic#role_recharge_statistic{
            recency = data_vsn:calc_recharge_recency(RecencyValue)
            , frequency = data_vsn:calc_recharge_frequency(FrequencyValue)
        },
    RechargeStatistic =/= NewRechargeStatistic andalso
        lib_recharge_data:db_save_recharge_statistic(RoleId, NewRechargeStatistic),
    Player#player_status{recharge_statistic = NewRechargeStatistic}.

%% 计算前一段时间的充值次数
calc_before_recharge_counts(RoleId, Now) ->
    Before_30_Day = Now - 30 * 86400,
    Detail = lib_recharge_data:get_daily_recharge_detail(RoleId, Before_30_Day),
    lists:sum([1 || Daily <- Detail, _ <- Daily#daily_recharge.detail]).

get_statistic(Player) ->
    #player_status{recharge_statistic = RechargeStatistic} = Player,
    {
        RechargeStatistic#role_recharge_statistic.recency,
        RechargeStatistic#role_recharge_statistic.frequency,
        RechargeStatistic#role_recharge_statistic.monetary,
        RechargeStatistic#role_recharge_statistic.top
    }.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%% 快速充值日志
add_quick_recharge_log(Player, ProductId, Gold, Now) ->
    #player_status{id = PlayerId, quick_recharge = QuickRecharge} = Player,
    case lists:keyfind(ProductId, 1, QuickRecharge) of
        {_, MoneyGap} ->
            lib_log_api:log_quick_recharge(PlayerId, ProductId, Gold, MoneyGap, Now),
            NewPlayer = Player#player_status{quick_recharge = lists:keydelete(ProductId, 1, QuickRecharge)};
        _ ->
            NewPlayer = Player
    end,
    NewPlayer.

%% 判断是否是充值卡
check_charge_card(PayNo) ->
    case catch binary_to_integer(PayNo) of
        GoodsId when is_integer(GoodsId) ->
            case data_goods_type:get(GoodsId) of
                #ets_goods_type{type = ?GOODS_TYPE_STUFF, subtype = ?GOODS_STUFF_SUBTYPE_CHARGE_CARD} ->
                    true;
                _ ->
                    false
            end;
        _ ->
            false
    end.