%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 团购活动操作接口
%%% @end
%%% -------------------------------------------------------
-module(group_buy_api).
-export([
        notify_other_module/5
        ,receive_reward/4
        ,query_act_grade_list/1
        ,notice_act_grade_change/1
        ,act_grade_change/2
        ,count_curr_price/2
        ,check_return/3
        ,role_check_return/2
    ]).
-include("common.hrl").
-include("predefine.hrl").
-include("custom_act.hrl").
-include("group_buy.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("errcode.hrl").

%% 通知活动状态变化
%% ** 对应活动公共模块回调接口 **
notify_other_module(ActStatus, _IsKfActType = false,
    #act_info{key = {?CUSTOM_ACT_TYPE_GROUP_BUY, SubType}} = ActInfo,
    _STime, _ETime) ->
    % ?INFO("活动状态变动回调"),
    case ActStatus of
        ?CUSTOM_ACT_STATUS_OPEN ->
            % ?INFO("活动开启"),
            lib_custom_act:broadcast_act_info(?CUSTOM_ACT_STATUS_OPEN, [ActInfo]),
            group_buy_mgr:act_start(SubType);
        ?CUSTOM_ACT_STATUS_CLOSE ->
            % ?INFO("活动关闭"),
            lib_custom_act:broadcast_act_info(?CUSTOM_ACT_STATUS_CLOSE, [ActInfo]),
            group_buy_mgr:act_end(SubType);
        ?CUSTOM_ACT_STATUS_MANUAL_CLOSE ->
            % ?INFO("活动关闭2"),
            lib_custom_act:broadcast_act_info(?CUSTOM_ACT_STATUS_MANUAL_CLOSE, [ActInfo]),
            group_buy_mgr:act_end(SubType);
        _ ->
            % ?INFO("未知状态"),
            error
    end;
notify_other_module(_ActStatus, _IsKfActType, _ActInfo, _STime, _ETime) ->
    % ?INFO("错误"),
    ok.

%% 购买 及 领取折扣返还
%% ** 对应活动公共模块回调接口 **
%% （前端要求用同一个接口入口）
receive_reward(Player, Type, SubType, GradeId) when Type =:= ?CUSTOM_ACT_TYPE_GROUP_BUY ->
    case lib_custom_act_util:get_custom_act_open_info(Type, SubType) of
        ActInfo = #act_info{stime = STime, etime = ETime} ->
            %% 判断是否已经购买，进而确定进入购买或领取折扣返还的处理
            CustomActData = #custom_act_data{
                act_data = ActData
            } = lib_custom_act_data:custom_act_data(ActInfo),
            case take_role_group_buy(SubType, GradeId, ActData) of
                {
                    value,
                    RoleGroupBuy = #role_group_buy{buy_time = BuyTime},
                    RemainActData
                } when BuyTime >= STime andalso BuyTime =< ETime ->
                    % ?INFO("已买，领取返还"),
                    %% 已经购买，检查进入领取折扣返还管理
                    get_discount_return(Player, Type, SubType, GradeId, RoleGroupBuy, RemainActData, CustomActData);
                {value, _RoleGroupBuy, RemainActData} ->
                    % ?INFO("数据过期，重新购买"),
                    %% 购买数据过期，清理旧数据
                    NewCustomActData = CustomActData#custom_act_data{
                        act_data = RemainActData
                    },
                    act_buy(Player, Type, SubType, GradeId, ActInfo, NewCustomActData);
                _ ->
                    % ?INFO("未购买，进入购买流程"),
                    %% 未购买，进入购买处理
                    act_buy(Player, Type, SubType, GradeId, ActInfo, CustomActData)
            end;
        _False ->
            % ?INFO("活动未开"),
            send_buy_result(Player#player_status.sid, [?ERRCODE(err331_act_closed), Type, SubType, GradeId]),
            ok
    end;
receive_reward(_Player, _Type, _SubType, _GradeId) ->
    ok.

%% 活动购买礼包
act_buy(Player, Type, SubType, GradeId, ActInfo, CustomActData) ->
    Result =
    case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, GradeId) of
        #custom_act_reward_cfg{} = RewardCfg ->
            Reward = lib_custom_act_util:count_act_reward(Player, ActInfo, RewardCfg),
            CanGive = lib_goods_api:can_give_goods(Player, Reward),
            if
                CanGive =/= true ->
                    %% 不能收纳道具（通常是背包空间不足）
                    % ?INFO("不能收纳道具"),
                    {false, err150_no_cell};
                true ->
                    %% 获取到价格等资料
                    case group_buy_mgr:query_buy_info(SubType, GradeId) of
                        [
                            BuyInfoGrade = #buy_info_grade{
                                gold_type = GoldType
                            }
                        ] when GoldType =:= ?TYPE_GOLD orelse GoldType =:= ?TYPE_BGOLD ->
                            act_buy_2(Player, Type, SubType, GradeId, BuyInfoGrade, Reward, CustomActData);
                        _ ->
                            % ?INFO("货币类型异常"),
                            {false, fail}
                    end
            end;
        _False ->
            % ?INFO("活动配置异常"),
            {false, config_err}
    end,
    case Result of
        {false, ErrCode} when is_integer(ErrCode) ->
            send_buy_result(Player#player_status.sid, [ErrCode, Type, SubType, GradeId]);
        {false, Error} ->
            send_buy_result(Player#player_status.sid, [?ERRCODE(Error), Type, SubType, GradeId]);
        {ok, _NewPlayer} ->
            send_buy_result(Player#player_status.sid, [?SUCCESS, Type, SubType, GradeId]),
            Result
    end.

act_buy_2(Player, Type, SubType, GradeId, BuyInfoGrade, Reward, CustomActData) ->
    #buy_info_grade{
        gold_type = GoldType
        ,original_price = OriginalPrice
        ,discount = Discount
        ,fix_buyer_count = BuyerCount
        ,real_buyer_count = RealBuyerCount
        ,stage = Stage
        ,stage_no_fix = StageNoFix
        ,stage_total = StageTotal
    } = BuyInfoGrade,
    Price = count_curr_price(OriginalPrice, Discount),
    % ?INFO("OriginalPrice:~w, Discount:~w, Price:~w", [OriginalPrice, Discount, Price]),
    CostList = to_object_list(GoldType, Price),
    case lib_goods_api:cost_object_list_with_check(Player, CostList, group_buy, "") of
        {true, PlayerAfCost, _} ->
            #player_status{id = RoleId, sid = Sid,
                gold = OldGold, bgold = OldBGold,
                figure = #figure{vip = Vip}
            } = Player,
            #custom_act_data{act_data = ActData} = CustomActData,
            NowTime = utime:unixtime(),
            RoleGroupBuy = #role_group_buy{
                subtype = SubType
                ,grade_id = GradeId
                ,gold_type = GoldType
                ,buy_cost = Price
                ,got_return = 0
                ,buy_time = NowTime
                ,return_time = 0
                ,auto_return = 0
            },
            NewActData = [RoleGroupBuy | ActData],
            NewCustomActData = CustomActData#custom_act_data{
                act_data = NewActData
            },
            lib_custom_act_data:save_custom_data(RoleId, NewCustomActData),
            Produce = #produce{
                type = group_buy,
                subtype = Type,
                reward = Reward,
                remark = util:term_to_string(SubType)
            },
            lib_log_api:log_group_buy(RoleId, SubType, GradeId,
                GoldType, Price, OldGold, OldBGold, Reward, Discount,
                RealBuyerCount, BuyerCount, Stage, StageNoFix, StageTotal, Vip, NowTime
            ),
            NewPlayer = lib_goods_api:send_reward(PlayerAfCost, Produce),
            %% 通知管理器记录更新
            group_buy_mgr:notice_buyer_add(SubType, GradeId, RoleId),
            %% 推送数据
            send_buyer_count(Sid, SubType, GradeId, BuyerCount, Price),
            %% ?INFO("购买成功:{~w,~w}", [SubType, GradeId]),
            {ok, NewPlayer};
        {false, ErrCode, _NewPlayer} ->
            %% ?INFO("购买失败:{~w,~w}, ErrCode:~w", [SubType, GradeId, ErrCode]),
            {false, ErrCode}
    end.

%% 领取折扣返还
get_discount_return(Player, Type, SubType, GradeId, RoleGroupBuy, RemainActData, CustomActData) ->
    %% 检查活动数据是否匹配
    #role_group_buy{
        gold_type = GoldType
    } = RoleGroupBuy,
    Result =
    case group_buy_mgr:query_buy_info(SubType, GradeId) of
        [
            #buy_info_grade{
                gold_type = GoldType
                ,original_price = OriginalPrice
                ,discount = Discount
                ,fix_buyer_count = _BuyerCount
            }
        ] ->
            get_return_award(Player, RoleGroupBuy, CustomActData, RemainActData, OriginalPrice, Discount, Type);
        _ ->
            %% 数据异常（获取失败，或货币类型不符），不予返还
            % ?INFO("data error"),
            {false, fail}
    end,
    case Result of
        {false, Error} ->
            send_buy_result(Player#player_status.sid, [?ERRCODE(Error), Type, SubType, GradeId]),
            ok;
        {ok, _NewPlayer} ->
            send_buy_result(Player#player_status.sid, [?SUCCESS, Type, SubType, GradeId]),
            Result
    end.

get_return_award(Player, RoleGroupBuy, CustomActData, RemainActData, OriginalPrice, Discount, Type) ->
    #role_group_buy{
        subtype = SubType
        ,grade_id = GradeId
        ,gold_type = GoldType
    } = RoleGroupBuy,
    Price = count_curr_price(OriginalPrice, Discount),
    case calc_act_return_award(RoleGroupBuy, Price, role_get) of
        {ok, NewRoleGroupBuy, AwardList} ->
            #player_status{id = RoleId} = Player,
            NewActData = [NewRoleGroupBuy | RemainActData],
            NewCustomActData = CustomActData#custom_act_data{
                act_data = NewActData
            },
            lib_custom_act_data:save_custom_data(RoleId, NewCustomActData),
            %% 发放返还
            Produce = #produce{
                type = group_buy,
                subtype = Type,
                reward = AwardList
                ,remark = util:term_to_string(SubType)
            },
            %% 日志
            NowTime = utime:unixtime(),
            [{_, _, ReturnNum} | _] = AwardList,
            lib_log_api:log_group_buy_return(RoleId, SubType, GradeId,
                GoldType, ReturnNum, Discount, 0, NowTime
            ),
            NewPlayer = lib_goods_api:send_reward(Player, Produce),
            {ok, NewPlayer};
        Other = {false, _Error} ->
            Other
    end.

%% 计算折扣返还
calc_act_return_award(RoleGroupBuy, Price, GetType) ->
    #role_group_buy{
        buy_cost = BuyCost
        ,gold_type = GoldType
        ,got_return = GotReturn
    } = RoleGroupBuy,
    OldMyCost = BuyCost - GotReturn,
    case OldMyCost =< Price of
        true  ->    %% 不需要返还（暂时借用err503_no_award）
            % ?INFO("no discount return"),
            {false, err503_no_award};
        false ->
            % ?INFO("get discount return"),
            %% 更新领取数据
            NowReturn = OldMyCost - Price,
            AwardList = to_object_list(GoldType, NowReturn),
            NewGotReturn = GotReturn + NowReturn,
            %% 自动返还状态
            AutoReturn =
            case GetType of
                role_get -> 0;
                server_send -> 1
            end,
            NewRoleGroupBuy = RoleGroupBuy#role_group_buy{
                got_return = NewGotReturn
                ,return_time = utime:unixtime()
                ,auto_return = AutoReturn
            },
            {ok, NewRoleGroupBuy, AwardList}
    end.

%% 查询团购活动信息
-spec query_act_grade_list(SubType) -> GradeList when
    SubType :: non_neg_integer(),
    GradeList :: [ {GradeId, BuyerCount, MyCost} ],
    GradeId :: non_neg_integer(), BuyerCount :: non_neg_integer(),
    MyCost :: non_neg_integer().
query_act_grade_list(SubType) ->
    Type = ?CUSTOM_ACT_TYPE_GROUP_BUY,
    case lib_custom_act_util:get_custom_act_open_info(Type, SubType) of
        ActInfo = #act_info{etime = _EndTime} ->
            GradeIdList = lib_custom_act_util:get_act_reward_grade_list(Type, SubType),
            case group_buy_mgr:query_buy_info(SubType, GradeIdList) of
                [_|_] = BuyInfoGradeList ->
                    #custom_act_data{
                        act_data = ActData
                    } = lib_custom_act_data:custom_act_data(ActInfo),
                    to_act_grade_list(ActData, BuyInfoGradeList, []);
                _ ->
                    %% 无数据或失败
                    []
            end;
        _False ->
            %% 活动未开放
            []
    end.

to_act_grade_list(ActData, [BuyInfoGrade | BuyInfoGradeList],
    Acc) when is_record(BuyInfoGrade, buy_info_grade) ->
    #buy_info_grade{
        buy_id = {_SubType, GradeId}
        ,fix_buyer_count = BuyerCount
    } = BuyInfoGrade,
    MyCost =
    case lists:keyfind(GradeId, #role_group_buy.grade_id, ActData) of
        #role_group_buy{
            buy_cost = BuyCost
            ,got_return = GotReturn
        } ->    %% 已购买
            BuyCost - GotReturn;
        _ ->    %% 未购买
            0
    end,
    ActGradeInfo = {GradeId, BuyerCount, MyCost},
    to_act_grade_list(ActData, BuyInfoGradeList, [ActGradeInfo | Acc]);
to_act_grade_list(ActData, [_|BuyInfoGradeList], Acc) ->
    to_act_grade_list(ActData, BuyInfoGradeList, Acc);
to_act_grade_list(_ActData, [], Acc) ->
    Acc.

%% 仿lists:keytake的行为及返回结果（从列表中取出指定关键信息的数据）
take_role_group_buy(SubType, GradeId, ActData) ->
    sub_take_role_group_buy(SubType, GradeId, ActData, []).

sub_take_role_group_buy(SubType, GradeId, [
        RoleGroupBuy = #role_group_buy{
            subtype = SubType, grade_id = GradeId
        } | ActData], Acc) ->
    {value, RoleGroupBuy, ActData ++ Acc};
sub_take_role_group_buy(SubType, GradeId, [Any | ActData], Acc) ->
    sub_take_role_group_buy(SubType, GradeId, ActData, [Any | Acc]);
sub_take_role_group_buy(_SubType, _GradeId, _ActData, _Acc) ->
    false.

%% 发送购买结果
send_buy_result(Sid, Data = [_ErrCode, _Type, _SubType, _GradeId]) ->
    {ok, BinData} = pt_331:write(33105, Data),
    lib_server_send:send_to_sid(Sid, BinData).

%% 推送购买人数变更
send_buyer_count(Sid, SubType, GradeId, BuyerCount, Price) ->
    {ok, BinData} = pt_332:write(33224, [SubType, GradeId, BuyerCount, Price]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 计算当前价格（最新折扣后）
count_curr_price(OriginalPrice, Discount) ->
    max(umath:floor(OriginalPrice * Discount / 10), 1).

to_object_list(GoldType, Num) ->
    case GoldType of
        ?TYPE_GOLD  -> [{GoldType, ?GOODS_ID_GOLD, Num}];
        ?TYPE_BGOLD -> [{GoldType, ?GOODS_ID_BGOLD, Num}]
    end.

notice_act_grade_change(BuyInfoGrade) ->
    %% 通知全部玩家，阶段变更，由各玩家执行各自数据通知
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, act_grade_change, [BuyInfoGrade]).

act_grade_change(Player, BuyInfoGrade) when is_record(BuyInfoGrade, buy_info_grade) ->
    #buy_info_grade{buy_id = {SubType, _}} = BuyInfoGrade,
    Type = ?CUSTOM_ACT_TYPE_GROUP_BUY,
    #custom_act_data{
        act_data = ActData
    } = lib_custom_act_data:custom_act_data(Type, SubType),
    [{GradeId, BuyerCount, MyCost}] = to_act_grade_list(ActData, [BuyInfoGrade], []),
    {ok, BinData} = pt_332:write(33224, [SubType, GradeId, BuyerCount, MyCost]),
    lib_server_send:send_to_sid(Player#player_status.sid, BinData),
    ok.

%% TODO return返还可以做同一个玩家的不同档次数据合并一次操作优化，有空再补

%% 检查折扣返还
check_return([BuyerId | Buyers], Params = #check_return_params{}, Order) ->
    case misc:get_player_process(BuyerId) of
        Pid when is_pid(Pid) ->
            lib_player:apply_cast(Pid, ?APPLY_CAST_STATUS,
                ?MODULE, role_check_return, [Params]
            );
        _ ->
            %% 离线玩家处理
            offline_check_return(BuyerId, Params)
    end,
    case Order rem 20 =:= 0 of
        true  -> timer:sleep(300);
        false -> ignore
    end,
    check_return(Buyers, Params, Order + 1);
check_return([], _Params, _Order) ->
    ok.

%% 在线玩家，在玩家进程中执行折扣返还检查
role_check_return(#player_status{id = RoleId}, Params) ->
    Type = ?CUSTOM_ACT_TYPE_GROUP_BUY,
    check_auto_return(RoleId, Type, Params);
role_check_return(_Player, _) ->
    ok.

%% 离线玩家折扣返还（使用的公共进程，需要先清理进程字典）
offline_check_return(RoleId, Params = #check_return_params{
        subtype = SubType
    }) ->
    Type = ?CUSTOM_ACT_TYPE_GROUP_BUY,
    %% 清理（上一个）玩家缓存数据
    lib_custom_act_data:clean_up_custom_data(0, Type, SubType),
    %% 加载玩家数据
    lib_custom_act_data:load_custom_act_data(RoleId, Type, SubType),
    check_auto_return(RoleId, Type, Params).

check_auto_return(RoleId, Type, #check_return_params{
        subtype = SubType
        ,grade_id = GradeId
        ,gold_type = GoldType
        ,curr_price = CurrPrice
        ,discount = Discount
        ,stime = STime
        ,etime = ETime
    }) ->
    CustomActData = #custom_act_data{
        act_data = ActData
    } = lib_custom_act_data:custom_act_data(Type, SubType),
    % ?INFO("check_auto_return:{~w,~w}", [SubType, GradeId]),
    case take_role_group_buy(SubType, GradeId, ActData) of
        {value, RoleGroupBuy = #role_group_buy{
                gold_type = GoldType
                ,buy_time = BuyTime
            }, RemainActData} when BuyTime >= STime andalso BuyTime =< ETime ->
            % ?INFO("valid role group buy data"),
            case calc_act_return_award(RoleGroupBuy, CurrPrice, server_send) of
                {ok, NewRoleGroupBuy, AwardList} ->
                    % ?INFO("got return:~w", [AwardList]),
                    NewActData = [NewRoleGroupBuy | RemainActData],
                    NewCustomActData = CustomActData#custom_act_data{
                        act_data = NewActData
                    },
                    lib_custom_act_data:save_custom_data(RoleId, NewCustomActData),
                    %% 日志
                    NowTime = utime:unixtime(),
                    [{_, _, ReturnNum} | _] = AwardList,
                    lib_log_api:log_group_buy_return(RoleId, SubType, GradeId,
                        GoldType, ReturnNum, Discount, 1, NowTime
                    ),
                    %% 邮件发放奖励
                    Title = utext:get(?GROUP_BUY_RETURN_TITLE),
                    Content = utext:get(?GROUP_BUY_RETURN_CONTENT),
                    lib_mail:send_sys_mail([RoleId], ?MOD_AC_CUSTOM, ?MOD_NONE, Title, Content, AwardList),
                    ok;
                _ ->
                    % ?INFO("no return"),
                    error
            end;
        {value, _RoleGroupBuy = #role_group_buy{
                gold_type = GoldType
                ,buy_time = BuyTime
            }, _RemainActData} when BuyTime >= STime andalso BuyTime =< ETime ->
            ?ERR("[~w]数据异常，疑似配置变更，货币类型不匹配{~w,~w}", [RoleId, SubType, GradeId]),
            ok;
        {value, _RoleGroupBuy, _RemainActData} ->
            %% 过期数据
            ?ERR("[~w]数据异常，疑似活动期间变更过活动时间范围{~w,~w}", [RoleId, SubType, GradeId]),
            ok;
        _ ->
            %% 数据异常
            ?ERR("[~w]数据异常，疑似返还前数据已被清理{~w,~w}", [RoleId, SubType, GradeId]),
            ok
    end;
check_auto_return(_, _, _) ->
    ok.
