%%%--------------------------------------
%%% @Module  : lib_shop
%%% @Author  : xiaoxiang
%%% @Created : 2017-01-04
%%% @Description:  商城
%%%--------------------------------------
-module(lib_shop).
-compile(export_all).
-include("server.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("vip.hrl").
-include("common.hrl").
-include("shop.hrl").
-include("goods.hrl").
-include("def_event.hrl").
-include("def_fun.hrl").
-include("def_goods.hrl").
-include("def_module.hrl").
-include("fishing.hrl").
-include("dungeon.hrl").

%% 获取商城列表
get_goods_list(PS, ShopType) ->
    #player_status{
        id = RoleId, sid = Sid, figure = #figure{lv = Lev, career = RoleCareer}
    } = PS,
    OpenDay = util:get_open_day(),
    WorldLv = util:get_world_lv(),
    KeyList = data_shop:get_shop(ShopType),
    F = fun(KeyId, GoodsList) ->
        case data_shop:get_shop_config(KeyId) of
            #shop{
                key_id = KeyId, shop_type = ShopType, rank = Rank, ctype = MoneyType
                ,goods_id = GoodsId, num = Num, price = Price, discount = Discount
                ,quota_type = QuotaType, quota_num = TmpQuotaNum, on_sale = OnSale
                ,halt_sale = HaltSale,counter_module = Module, counter_id = CounterId
                ,bind = Bind, wlv_sale = WlvSale, wlv_unsale = WlvUnsale, career = Career
                ,condition = Condition, preview = Preview, on_lev = OnLev, down_lev = DownLev
            } ->
                if
                    (OnSale > OpenDay) orelse (OpenDay > HaltSale andalso HaltSale =/= 0) ->
                        GoodsList;
                    (WlvSale > WorldLv) orelse (WorldLv > WlvUnsale andalso WlvUnsale =/= 0) ->
                        GoodsList;
                    not (OnLev =< Lev andalso Lev =< DownLev) ->
                        GoodsList;
                    not (RoleCareer =:= Career orelse Career =:= 0) ->
                        GoodsList;
                    true ->
                        case util:term_to_string(Condition) of
                            undefined ->
                                ?ERR("bad condition ~p~n", [Condition]),
                                GoodsList;
                            StrCondition ->
                                %% 购买次数容错一下，避免策划忘记配置计数器id
                                QuotaNum = get_quota_num(PS, Condition, TmpQuotaNum),
                                UsedTime = ?IF(QuotaNum > 0, min(lib_shop_check:get_buyed_time(RoleId, KeyId, Module, CounterId, QuotaType), QuotaNum), 0),
                                [{KeyId, Rank, GoodsId, Num, MoneyType, Price, Discount, QuotaType, QuotaNum, UsedTime, StrCondition, Bind, Preview}|GoodsList]
                        end
                end;
            _ ->
                GoodsList
        end
    end,
    NewGoodsList = lists:foldl(F, [], KeyList),
    {ok, BinData} = pt_153:write(15301, [ShopType, NewGoodsList]),
    lib_server_send:send_to_sid(Sid, BinData).

%%  ----------------------- 购买商品 -----------------------------
buy_goods_by_money_type(Player, KeyList, Type) ->
    NewKeyList = util:combine_list(KeyList),
    case shop_type_check(Player, NewKeyList) of
        {false, ErrorCode} ->
            lib_server_send:send_to_sid(Player#player_status.sid, pt_153, 15302, [ErrorCode, KeyList]);
        {true, CostList, _GoodsList} ->
            case send_goods(KeyList, Type, Player) of
                {true, TmpPS} ->
                    lib_achievement_api:shop_buy_event(Player, CostList),
                    NewPS = handle_buy(TmpPS, KeyList, Type),
                    lib_server_send:send_to_sid(Player#player_status.sid, pt_153, 15302, [?SUCCESS, KeyList]),
                    {ok, NewPS};
                {false, ErrorCode, _NewPS} ->
                    lib_server_send:send_to_sid(Player#player_status.sid, pt_153, 15302, [ErrorCode, KeyList])
            end
    end.

%% 商城类型前置条件检测(错误各自功能返回)
shop_type_check(Player, KeyList) ->
    do_buy_goods_by_money_type(Player, KeyList).

do_buy_goods_by_money_type(Player, KeyList) ->
    {CostList, GoodsList, ErrCodes} = check_and_get_sum_cost(Player, KeyList),
    %% 筛选出是否有不合法的错误码
    case lists:filter(fun(E) -> E /= ?SUCCESS end, ErrCodes) of
        %% 没有错误码，检查背包空间和金钱是否足够
        [] -> %% 背包
            CheckList = [{bag_is_enough, GoodsList}, {money_is_enough, CostList}],
            case lib_shop_check:check_buy_goods(CheckList, Player) of
                {true, _Player2} -> %%背包足够则检查钱
                    {true, CostList, GoodsList};
                {false, ErrCode, _Player2} -> %% 背包空间不足
                    {false, ErrCode}
            end;
        ErrCodes1 -> %% 如果检查有未通过的地方，将第一个遇到的错误码返回
            Code = lists:last(ErrCodes1),
            {false, Code}
    end.

%% 检测购买列表并且计算总消费
check_and_get_sum_cost(Player, KeyList) ->
    Fun1 = fun(KeyElement, {CostList, GoodsList, ErrCodes}) ->
        {KeyId, BuyNum} = KeyElement,
        %% 获取配置
        case data_shop:get_shop_config(KeyId) of
            #shop{
                key_id = KeyId, shop_type = _ShopType, goods_id = GoodsId, num = GNum, discount = Discount
                ,ctype = CType,  price = Price, bind = Bind, quota_type = QuotaType, career = Career
                ,quota_num = QuotaNum, on_sale = OnSale, halt_sale = HaltSale, wlv_sale = WlvSale
                ,wlv_unsale = WlvUnsale, condition = Condition, counter_module = CounterModule
                ,counter_id = CounterId, on_lev = OnLev, down_lev = DownLev
            } ->
                  %% 有配置先检查，先做前几个检查，同时记录下价格
                CheckList = [
                    {args, [BuyNum]}, {open_day, OnSale, HaltSale}
                    ,{wlv, WlvSale, WlvUnsale}, {condition, Condition}
                    ,{quota_num, Condition, QuotaType, QuotaNum, CounterModule, CounterId, KeyId, BuyNum}
                    ,{on_lev, OnLev, DownLev}, {career, Career}
                ],
                case lib_shop_check:check_buy_goods(CheckList, Player) of
                    {true, Player} ->
                        MoneyNum = round(Price * BuyNum * Discount/?BASE_DISCOUNT),
                        NewGoodsList = case Bind of
                            ?UNBIND -> [{?TYPE_GOODS, GoodsId, GNum*BuyNum} | GoodsList];
                            _ -> [{?TYPE_BIND_GOODS, GoodsId, GNum*BuyNum} | GoodsList]
                        end,
                        %% 检查通过累加价格,同时将成功码加入列表里（有用到特殊货币的麻烦根据商城类型往后加）
                        case CType of
                            ?TYPE_GOLD ->
                                {[{?TYPE_GOLD, 0, MoneyNum} | CostList], NewGoodsList, [?SUCCESS|ErrCodes]};
                            ?TYPE_BGOLD ->
                                {[{?TYPE_BGOLD, 0, MoneyNum} | CostList], NewGoodsList, [?SUCCESS|ErrCodes]};
                            ?TYPE_COIN ->
                                {[{?TYPE_COIN, 0, MoneyNum} | CostList], NewGoodsList, [?SUCCESS|ErrCodes]};
                            ?TYPE_HONOUR ->
                                {[{?TYPE_HONOUR, 0, MoneyNum} | CostList], NewGoodsList, [?SUCCESS|ErrCodes]};
                            ?TYPE_MATE->
                                {[{?TYPE_MATE, 0, MoneyNum} | CostList], NewGoodsList, [?SUCCESS|ErrCodes]};
                            _ ->
                                {CostList, GoodsList, [?ERRCODE(err153_money_type_wrong)|ErrCodes]}
                        end;
                    {false, ErrCode, _NewPS} ->
                        %% 检查失败，将错误码加入列表
                        {CostList, GoodsList, [ErrCode|ErrCodes]}
                end;
            _ ->
                {CostList, GoodsList, [?ERRCODE(err_config)|ErrCodes]}
        end
    end,
    lists:foldl(Fun1, {[], [], []}, KeyList).

%% 通用的扣除东西
do_cost_money(Player, Price, CType, BuyNum, Discount, GoodsId, Num, ShopType, Type) ->
    MoneyNum = round(Price * BuyNum * Discount/?BASE_DISCOUNT),
    About = #cost_log_about{type = 3, data = [GoodsId, Num*BuyNum], remark = "shop cost"},
    CostType = case ShopType of
        ?SHOP_GOLD -> shop_gold;
        ?SHOP_BGOLD -> shop_bgold;
        ?SHOP_COIN when Type == 1 ->
            equip_buy_cost_coin;
        ?SHOP_COIN -> shop_coin;
        ?SHOP_HONOUR when Type == 1 ->
            equip_buy_cost_coin;
        ?SHOP_HONOUR -> shop_honour;
        ?SHOP_MATE -> shop_mate;
        ?SHOP_1VN_DEF -> shop_1vn;
        ?SHOP_1VN -> shop_1vn;
        ?SHOP_DIAMOND_VIP -> shop_diamond_vip;
        _ -> shop_life
    end,
    Cost = [{CType, 0, MoneyNum}],
    case lib_goods_api:cost_object_list_with_check(Player, Cost, CostType, About) of
        {true, NewPS,_} -> {true, CostType, MoneyNum, NewPS};
        {false, Errcode, NewPS} -> {false, Errcode, NewPS}
    end.

%% 扣钱,发送物品
send_goods([], _, Player) ->
    {true, Player};
send_goods([{KeyId, BuyNum}|KeyLists], Type, #player_status{id = RoleId, figure = #figure{lv = Lv, vip = VipLv}} = Player) ->
    GoodShop = data_shop:get_shop_config(KeyId),
    #shop{shop_type = ShopType, goods_id = GoodsId, num = GoodNum, quota_type = QuotaType,
          ctype = CType,  price = Price, discount = Discount, bind = Bind, condition = Condition,
          quota_num = TmpQuotaNum, counter_module = CounterModule, counter_id = CounterId} = GoodShop,
    case do_cost_money(Player, Price, CType, BuyNum, Discount, GoodsId, GoodNum, ShopType, Type) of
        {true, CostType, MoneyNum, NewPS} ->
            %% 发送物品
            Produce = #produce{type = CostType, subtype = 0, remark = "shop sale",
                               reward = [{Bind*?TYPE_BIND_GOODS, GoodsId, GoodNum*BuyNum}], show_tips = 1},
            LastPS = lib_goods_api:send_reward(NewPS, Produce),
            %% 添加次数
            QuotaNum = get_quota_num(Player, Condition, TmpQuotaNum),
            ?IF(QuotaNum > 0, lib_shop_check:add_buyed_time(RoleId, KeyId, CounterModule, CounterId, QuotaType, BuyNum), skip),
            %% 记录日志，派发事件
            log_shop(RoleId, Lv, VipLv, ShopType, GoodsId, GoodNum*BuyNum, BuyNum, CType, MoneyNum),
            % lib_player_event:async_dispatch(RoleId, ?EVENT_SHOP_CONSUME,  GoodNum*BuyNum),
            send_goods(KeyLists, Type, LastPS);
        {false, ErrCode, NewPS} ->
            {false, ErrCode, NewPS}
    end.

%% 记录日志
log_shop(RoleId, Lv, VipLv, ShopType, GoodsId, AllNum, BuyNum, CType, AllPrice) ->
    Time = utime:unixtime(),
    lib_log_api:log_shop(RoleId, Lv, VipLv, ShopType, GoodsId, AllNum, BuyNum, CType, AllPrice, Time).

%% 获取商品列表的所有商品类型
get_shop_type(KeyList) ->
    F = fun({KeyId, _Num}, ShopTypeList) ->
        case data_shop:get_shop_config(KeyId) of
            #shop{shop_type = Type} -> [Type | ShopTypeList];
            _ -> ShopTypeList
        end
    end,
    lists:foldl(F, [], KeyList).

%% 当前等级是否可以购买物品
get_lv_can_buy(GoodsId, Lv) ->
    {MinLv, MaxLv} = get_need_lv_to_buy(GoodsId),
    MinLv =< Lv andalso Lv =< MaxLv.

%% 获取购买所需要的等级 return {min_lv, max_lv}
get_need_lv_to_buy(GoodsId) ->
    case data_shop:get_key_ids_by_goods_id(GoodsId) of
        KeyIds when KeyIds =/= [] andalso is_list(KeyIds) ->
            do_get_need_lv_to_buy(KeyIds, 0, 9999);
        _ -> {0, 9999}
    end.

do_get_need_lv_to_buy([], MinLv, MaxLv) -> {MinLv, MaxLv};
do_get_need_lv_to_buy([KeyId|KeyIds], OldMinLv, OldMaxLv) ->
    #shop{condition = Condition} = data_shop:get_shop_config(KeyId),
    case lists:keyfind(lv, 1, Condition) of
        {lv, MinLv, MaxLv} ->
            NewMinLv = ?IF(MinLv > OldMinLv andalso OldMinLv == 0, MinLv, OldMinLv),
            NewMaxLv = ?IF(MaxLv < OldMaxLv andalso OldMaxLv == 9999, MaxLv, OldMaxLv);
        _ ->
            NewMinLv = OldMinLv,
            NewMaxLv = OldMaxLv
    end,
    do_get_need_lv_to_buy(KeyIds, NewMinLv, NewMaxLv).

quick_buy(PS, GoodsId, Num, BuyType) ->
    #player_status{id = RoleId} = PS,
    case data_quick_buy:get_quick_buy_price(GoodsId) of
        [] ->
            Code = ?MISSING_CONFIG,
            NewPS = PS;
        QuickBuyPriceCon ->
            %% 检测购买次数限制
            case get_limit_times(PS, GoodsId, BuyType) of
                {true, KeyId, CounterModule, CounterId, QuotaType, LeftNum} ->
                    if
                        LeftNum >= Num ->
                            {NewPS, Code} = do_quick_buy(PS, QuickBuyPriceCon, GoodsId, Num, BuyType),
                            IsSuccess = Code == ?SUCCESS, %% 成功加次数
                            ?IF(IsSuccess, lib_shop_check:add_buyed_time(RoleId, KeyId, CounterModule, CounterId, QuotaType, Num), ok);
                        true -> %% 次数不够
                            NewPS = PS,
                            Code = ?ERRCODE(err153_not_enough_goods_to_buy)
                    end;
                false -> %% 无购买限制
                    {NewPS, Code} = do_quick_buy(PS, QuickBuyPriceCon, GoodsId, Num, BuyType)
            end
    end,
    {ok, Bin} = pt_153:write(15304, [Code, GoodsId, Num, BuyType]),
    lib_server_send:send_to_uid(RoleId, Bin),
    {ok, NewPS}.

do_quick_buy(PS, QuickBuyPriceCon, GoodsId, Num, BuyType) ->
    #player_status{id = RoleId, gold = BeforeGold, bgold = BeforeBGold, coin = BeforeCoin} = PS,
    #quick_buy_price{gold_price = GoldPrice, bgold_price = BGoldPrice, coin_price = CoinPrice, consume_type = ConsumeType} = QuickBuyPriceCon,
    NewConsumeType = ?IF(ConsumeType == unknown, quick_buy, ConsumeType),
    case BuyType of
        1 -> %% 钻石
            MoneyType = gold,
            Price = GoldPrice * Num;
        2 -> %% 绑钻
            MoneyType = bgold,
            Price = BGoldPrice * Num;
        _ ->
            MoneyType = coin,
            Price = CoinPrice * Num %% 金币
    end,
    case Price =:= 0 of
        true ->
            Code = ?ERRCODE(err153_qb_not_right_money),
            NewPS = PS;
        _ ->
            GoodsList = case BuyType of
                1 -> [{?TYPE_GOODS, GoodsId, Num}]; %% 用钻石购买的就不是绑定物品
                _ -> [{?TYPE_BIND_GOODS, GoodsId, Num}]
            end,
            case lib_goods_api:can_give_goods(PS, GoodsList) of
                false ->
                    Code = ?ERRCODE(err401_bag_cell_not_enough),
                    NewPS = PS;
                true ->
                    Remark = get_remark(PS, NewConsumeType),
                    case lib_goods_api:cost_money(PS, MoneyType, Price, NewConsumeType, #cost_log_about{type = 3, data=[GoodsId, Num], remark = Remark}) of
                        {1, NewPS1} ->
                            Code = ?SUCCESS,
                            NewPS2 = lib_goods_api:send_reward(NewPS1, GoodsList, quick_buy, 0),
                            NewPS = quick_buy_after(NewPS2, GoodsList, GoodsId, Num, Price, BuyType),
                            #player_status{gold = AfterGold, bgold = AfterBGold, coin = AfterCoin} = NewPS,
                            lib_achievement_api:shop_buy_event(PS, [{MoneyType,0,Num}]),
                            lib_log_api:log_quick_buy(RoleId, BeforeGold, BeforeBGold, BeforeCoin, AfterGold, AfterBGold, AfterCoin, GoodsList);
                        {_, NewPS} ->
                            Code = case MoneyType of
                                gold -> ?ERRCODE(gold_not_enough);
                                bgold -> ?ERRCODE(bgold_not_enough);
                                _ -> ?ERRCODE(coin_not_enough)
                            end
                    end
            end
    end,
    {NewPS, Code}.

%% 快速购买后处理
quick_buy_after(PS, GoodsList, GoodsId, Num, Price, BuyType) ->
    CoreList = [fish],
    quick_buy_after_core(CoreList, PS, GoodsList, GoodsId, Num, Price, BuyType).

quick_buy_after_core([], PS, _GoodsList, _GoodsId, _Num, _Price, _BuyType) -> PS;
quick_buy_after_core([fish | T], PS, GoodsList, GoodsId, Num, Price, BuyType) ->
    case data_fishing:get_fishing_gear_property(GoodsId) of
        #base_fishing_gear{} ->
            lib_fishing:add_log_buy_fishing_gear(PS, GoodsList, [{BuyType, 0, Price}]);
        _ -> skip
    end,
    quick_buy_after_core(T, PS, GoodsList, GoodsId, Num, Price, BuyType);
quick_buy_after_core([_ | T], PS, GoodsList, GoodsId, Num, Price, BuyType) ->
    quick_buy_after_core(T, PS, GoodsList, GoodsId, Num, Price, BuyType).

%% 特别的消费类型特殊处理消费remark
get_remark(PS, dungeon_encourage) -> %% 副本购买鼓舞次数，记录副本id
    #player_status{dungeon = Dungeon} = PS,
    DungeonId = Dungeon#status_dungeon.dun_id,
    io_lib:format("DungeonId:~p", [DungeonId]);
get_remark(_PS, _ConsumeType) ->
    "quick buy cost".

get_limit_times(PS, GoodsId, BuyType) when BuyType == 2 -> %% 绑钻才限制购买次数
    ShopKeyList = data_shop:get_key_ids_by_goods_id(GoodsId),
    F = fun(ShopKey) ->
        case data_shop:get_shop_config(ShopKey) of
            #shop{condition = Condition, shop_type = ShopType, quota_num = TmpQuotaNum} ->
                QuotaNum = get_quota_num(PS, Condition, TmpQuotaNum),
                lists:member(ShopType, [?SHOP_BGOLD]) andalso QuotaNum > 0; %% 目前只有绑钻商城需要检测
            _ -> false
        end
    end,
    FilterKeyList = lists:filter(F, ShopKeyList),
    case FilterKeyList of
        [ShopKey|_] ->
            #shop{condition = Condition, quota_type = QuotaType, quota_num = TmpQuotaNum, counter_module = CounterModule, counter_id = CounterId} = data_shop:get_shop_config(ShopKey),
            QuotaNum = get_quota_num(PS, Condition, TmpQuotaNum),
            UsedTime = lib_shop_check:get_buyed_time(PS#player_status.id, ShopKey, CounterModule, CounterId, QuotaType),
            LeftTime = max(QuotaNum - UsedTime, 0),
            {true, ShopKey, CounterModule, CounterId, QuotaType, LeftTime};
        _ -> false
    end;
get_limit_times(_PS, _GoodsId, _BuyType) -> false.

%% 购买东西成功回调
handle_buy(PS, [], _Type) -> PS;
handle_buy(PS, [{KeyId, GoodsNum}|KeyList], Type) ->
    case data_shop:get_shop_config(KeyId) of
        #shop{goods_id=GoodsId} = Cfg->
            lib_task_api:score_buy(PS,GoodsId),
            Player1 = equipment_task:event_shop_buy(PS, GoodsId),
            ta_agent_fire:role_shop_by(PS, [Cfg, GoodsNum, Type]),
            task_event_shop_buy(Cfg,GoodsNum,PS),
            handle_buy(Player1, KeyList, Type);
        _->
            handle_buy(PS, KeyList, Type)
    end;
handle_buy(PS, [_|KeyList], Type) -> handle_buy(PS, KeyList, Type).

get_quota_num(_PS, Condition, TmpQuotaNum) ->
    case lists:keyfind(vip_extra_times, 1, Condition) of
        {vip_extra_times, _Id} ->
            ExtraNum = 0;
            % {VipType, VipLv} = lib_vip_api:get_valid_vip_lv(PS),
            % ExtraNum = lib_vip:get_vip_privilege(VipType, VipLv, ?MOD_SHOP, Id);
        _ -> ExtraNum = 0
    end,
    TmpQuotaNum + ExtraNum.

%%获取技能书购买次数
get_buy_times(NeedNum,Status)->
    Shops = [5,4,3,2,1],
    get_buy_times(Shops,NeedNum,Status).
get_buy_times(Shops,NeedNum,Status)->
    get_buy_times(Shops,0,NeedNum,Status).
get_buy_times([],Num,_NeedNum,_Status)->
    Num;
get_buy_times([H|T],Num,NeedNum,Status)->
    #player_status{id=RoleId}=Status,
    Keys = data_shop:get_shop(H),
    NewNum = do_get_buy_times(H,RoleId,Keys,Num,NeedNum),
    case NewNum>=NeedNum of
        true->
            NeedNum;
        _->
            get_buy_times(T,NewNum,NeedNum,Status)
    end.


do_get_buy_times(_ShopKey,_RoleId,[],Num,_NeedNum)->
    Num;
do_get_buy_times(ShopKey,RoleId,[H|T],Num,NeedNum)->
    case data_shop:get_shop_config(H) of
        #shop{shop_type=1,sub_type=3,quota_type = QuotaType,counter_module=Mod,counter_id=CountId} when Mod>0->
            Used = lib_shop_check:get_buyed_time(RoleId, ShopKey, Mod, CountId, QuotaType),
            NewNum = Used+Num,
            case NewNum>=NeedNum of
                true->
                    NeedNum;
                _->
                    do_get_buy_times(ShopKey,RoleId,T,NewNum,NeedNum)
            end;
        #shop{shop_type=2,sub_type=2,quota_type = QuotaType,counter_module=Mod,counter_id=CountId} when Mod>0->
            Used = lib_shop_check:get_buyed_time(RoleId, ShopKey, Mod, CountId, QuotaType),
            NewNum = Used+Num,
            case NewNum>=NeedNum of
                true->
                    NeedNum;
                _->
                    do_get_buy_times(ShopKey,RoleId,T,NewNum,NeedNum)
            end;
        #shop{shop_type=5,sub_type=1,quota_type = QuotaType,counter_module=Mod,counter_id=CountId} when Mod>0->
            Used = lib_shop_check:get_buyed_time(RoleId, ShopKey, Mod, CountId, QuotaType),
            NewNum = Used+Num,
            case NewNum>=NeedNum of
                true->
                    NeedNum;
                _->
                    do_get_buy_times(ShopKey,RoleId,T,NewNum,NeedNum)
            end;
        _->
            do_get_buy_times(ShopKey,RoleId,T,Num,NeedNum)
    end.

task_event_shop_buy(Cfg,Num,Status)->
    case Cfg of
        #shop{shop_type=1,sub_type=3,counter_module=Mod} when Mod>0->
            Type = 1;
        #shop{shop_type=2,sub_type=2,counter_module=Mod} when Mod>0->
            Type = 2;
        #shop{shop_type=5,sub_type=1,counter_module=Mod} when Mod>0->
            Type = 5;
        _->
            Type = 0
    end,
    case Type>0 of
        true->
            lib_task_api:some_shop_buy_book(Status,Num,Type),
            lib_task_api:any_shop_buy_book(Status,Num);
        _->
            skip
    end.