%% ---------------------------------------
%% desc:运营定制,php支持,限时邮件
%% module:limit_mail_mgr.erl
%% author:hh
%% time  :20/08/21
%% ---------------------------------------


-module(limit_mail_mgr).

-behaviour(gen_server).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% API
-export([
    start_link/0
    ,update_limited_mail/2
    ,timer_check/0
    ,calc_format_args/1
    ]).

-define(SERVER, ?MODULE).

-include("mail.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").

%% 限时邮件
-record(mail_state, {
    wait_lists = [],
    init_ref = undefined
    }).

-record(one_mail_cfg, {
    index = 0,              %% 邮件编号
    title = "",             %% 标题
    content = "",           %% 内容
    rewards = [],           %% 奖励
    target_time = 0,        %% 目标时间
    open_day = [],          %% 开服天数
    last_day_login = 0,     %% 最近多少天登录
    lv = [],                %% 等级区间
    vip = [],               %% vip区间
    source = ""             %% 渠道
    }).


%%%-------------------------------------------------------------------
%%% API Functions
%%%-------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%% php调用
update_limited_mail(MailIndex, Type) ->
    gen_server:cast(?MODULE, {update_limited_mail, MailIndex, Type}).

timer_check() ->
    gen_server:cast(?MODULE, {timer_check}).

%%%-------------------------------------------------------------------
%%% Callback Functions
%%%-------------------------------------------------------------------
init(_Args) ->
    Ref = erlang:send_after(100, self(), {proc_init}),
    {ok, #mail_state{init_ref = Ref}}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast({update_limited_mail, MailIndexsBin, Type}, State) ->
    #mail_state{wait_lists = WaitList} = State,
    MailIndexs = util:bitstring_to_term(MailIndexsBin),
    if
        not is_list(MailIndexs) ->
            ?ERR("update args error ~p", [MailIndexs]),
            {noreply, State};
        Type == 1 ->
            case load_new_mail(MailIndexs) of
                MailCfgList = [#one_mail_cfg{}|_] ->
                    NewWaitList = lists:foldl(fun(MailCfg, TempWaiting) -> 
                        lists:keystore(MailCfg#one_mail_cfg.index, #one_mail_cfg.index, TempWaiting, MailCfg)
                    end, WaitList, MailCfgList),
                    NewState = State#mail_state{wait_lists = NewWaitList},
                    {noreply, NewState};
                _A ->
                    {noreply, State}
            end;
        Type == 2 ->
            delete_mail(MailIndexs),
            NewWaitList = lists:foldl(fun(MailIndex, TempWaiting) -> 
                lists:keydelete(MailIndex, #one_mail_cfg.index, TempWaiting)
            end, WaitList, MailIndexs),
            NewState = State#mail_state{wait_lists = NewWaitList},
            {noreply, NewState};
        true ->
            {noreply, State}
    end;

handle_cast({timer_check}, State) ->
    % ?ERR("timer_check", []),
    #mail_state{wait_lists = WaitList} = State,
    Now = utime:unixtime(),
    NewWaitList = lists:foldl(fun(MailCfg, Acc) -> 
        #one_mail_cfg{target_time = Time} = MailCfg,
        LimitTime = Now - Time,
        if
            LimitTime >= 0 andalso LimitTime < 60 ->
                execute_timing_mail(MailCfg),
                % db:execute(?SQL_LIMIT_MAIL_DELETE, [Id]),
                Acc;
            true ->
                [MailCfg|Acc] 
        end
    end, [], WaitList),
    {noreply, State#mail_state{wait_lists = NewWaitList}};

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({proc_init}, State) ->
    #mail_state{init_ref = Ref} = State,
    util:cancel_timer(Ref),
    WaitList = load_wait_mail_list(),
    {noreply, State#mail_state{wait_lists = WaitList, init_ref = undefined}};

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%-------------------------------------------------------------------
%%% Internal Functions
%%%-------------------------------------------------------------------

load_wait_mail_list() ->
    case catch db:get_all(?SQL_LIMIT_MAIL_SELECT, []) of
        Data = [_|_] ->
            Now = utime:unixtime(),
            lists:foldl(fun([Id, Title, Content, Gold, Bgold, RewardBin, OpenDaysBin, TarTime, LastDayLogin, LvsBin, VipsBin, Source|_], Acc) -> 
                if Now >= TarTime ->
                        % delete_mail(Id),
                        Acc;
                    true ->
                        MailCfg = init_mail_data([Id, Title, Content, Gold, Bgold, RewardBin, OpenDaysBin, TarTime, LastDayLogin, LvsBin, VipsBin, Source]),
                        [MailCfg|Acc]                        
                end
            end, [], Data);
        _ ->
            []
    end.

load_new_mail(Indexs) ->
    {NumRet, IndexFormatArgs} = calc_format_args(Indexs),
    if
        is_integer(NumRet) andalso NumRet > 0 ->
            SQL = io_lib:format(?SQL_TARGET_LIMIT_MAIL_SELECT, [IndexFormatArgs]),
            case catch db:get_all(SQL) of
                DataList = [_|_] ->
                % [Id, Title, Content, Gold, Bgold, RewardBin, OpenDaysBin, TarTime, LastDayLogin, LvsBin, VipsBin, Source|_] ->
                    lists:map(fun(Data) ->
                        init_mail_data(Data)
                    end, DataList);
                Error -> ?ERR("~p", [Error]), []
            end;
        true -> []
    end.

delete_mail(Indexs) ->
    {NumRet, IndexFormatArgs} = calc_format_args(Indexs),
    if
        is_integer(NumRet) andalso NumRet > 0 ->
            SQL = io_lib:format(?SQL_TARGET_LIMIT_MAIL_DELETE, [IndexFormatArgs]),
            db:execute(SQL);
        true ->
            skip
    end,
    % catch db:execute(?SQL_LIMIT_MAIL_DELETE, [Index]),
    ok.

calc_format_args([]) -> {0, []};
calc_format_args(Indexs = [_|_]) ->
    IndexList = util:link_list(Indexs),
    {length(Indexs), "(" ++ IndexList ++ ")"}.


init_mail_data([Id, Title, Content, Gold, Bgold, RewardBin, OpenDaysBin, TarTime, LastDayLogin, LvsBin, VipsBin, Source|_]) ->
    Rewards     = util:bitstring_to_term(RewardBin),
    OpenDays    = util:bitstring_to_term(OpenDaysBin),
    Lvs         = util:bitstring_to_term(LvsBin),
    Vips        = util:bitstring_to_term(VipsBin),
    Rewards1 =
    if Gold == 0 ->
            Rewards;
        true ->
            [{?TYPE_GOLD, 0, Gold}] ++ Rewards
    end,
    Rewards2 =
    if Bgold == 0 ->
            Rewards1;
        true ->
            [{?TYPE_BGOLD, 0, Bgold}] ++ Rewards1
    end,
    #one_mail_cfg{
        index = Id,
        title = Title,         
        content = Content,       
        rewards = util:term_to_string(Rewards2),       
        target_time = TarTime,
        open_day = OpenDays,
        last_day_login = LastDayLogin,
        lv = Lvs,
        vip = Vips,
        source = Source                 
    }.

execute_timing_mail(MailCfg = #one_mail_cfg{}) ->
    #one_mail_cfg{
        index = _Index,
        title = Title,        
        content = Content,      
        rewards = Rewards,      
        target_time = _Time,
        open_day = OpenDays,
        last_day_login = LastDayLogin,
        lv = Lvs, 
        vip = Vips,       
        source = Source
    } = MailCfg,
    CheckOpenDay = check_open_day(OpenDays),
    [MinLv, MaxLv|_] = check_lv(Lvs),
    [MinVip, MaxVip|_] = check_vip(Vips),
    if
        CheckOpenDay ->
            lib_mail_api:send_sys_mail_to_all(Title, Content, Rewards, Source, MinLv, MaxLv, MinVip, MaxVip, LastDayLogin, ?SEND_LOG_TYPE_YES, ?MOD_NONE, ?MOD_NONE, ?NOT_IMPORTANT_MAIL);
        true ->
            ?ERR("open day limit", []),
            ok
    end,
    ok;
execute_timing_mail(_MailCfg) ->
    ?ERR("mail cfg error", [_MailCfg]),
    ok.

check_open_day([MinOpenDay, MaxOpenDay|_]) ->
    OpenDay = util:get_open_day(),
    OpenDay >= MinOpenDay andalso OpenDay =< MaxOpenDay;
check_open_day(_) ->
    true.

check_lv([MinLv, MaxLv|_]) ->
    [MinLv, MaxLv];
check_lv(_) ->
    [30, 999].

check_vip([MinVip, MaxVip|_]) ->
    [MinVip, MaxVip];
check_vip(_) ->
    [0, 0].