import { RewardInter, pushMail, mailData } from "../pubUtils/interface";
import { MailModel, MailType } from "../db/Mail";
import { GroupMailModel, GroupMailType } from "../db/GroupMail";
import { ServerMailModel, ServerMailType } from '../db/ServerMail';
import { getRoleOnlineInfo } from "./redisService";
import { pinus } from "pinus";
import { gameData } from "../pubUtils/data";
import { getCurDay, nowSeconds } from '../pubUtils/timeUtil';
import { STATUS } from '../consts/statusCode';
import { genCode, resResult } from '../pubUtils/util';
import { GM_MAIL_TYPE, ITID, MAIL_STATUS, MAIL_TIME_TYPE, MAIL_TYPE, PUSH_ROUTE, SEND_NAME, SEND_TITLE } from "../consts";
import { MailParam } from '../domain/roleField/mail';
import { GMMailType, GMMailModel, GMMailTypeParam } from "../db/GMMail";
import { getGuildChannelSid, getWorldChannelSid } from "./chatChannelService";
import { GMMailRecordModel } from "../db/GMMailRecord";
import { BAG } from "../pubUtils/dicParam";
import { GuildModel, GuildType } from "../db/Guild";
import moment = require("moment");
import { sendMessageToGuildWithSuc, sendMessageToServerWithSuc, sendMessageToUsersWithSuc } from "./pushService";
import { GVGLeagueModel, GVGLeagueType } from "../db/GVGLeague";

/**
 * 获取邮件信息
 * @param {String} roleId 玩家id 
 * @param {Number} serverId 服务器id
 */
export async function getMails(roleId: string, serverId: number) {
    let mails = await MailModel.findByRoleId(roleId)||[];
    let groupMails = await GroupMailModel.findByRoleId(roleId)||[];
    let serverMails = await ServerMailModel.findByRoleId(roleId, serverId)||[];

    let list = getMailInfos(roleId, mails, groupMails, serverMails);
    list.sort((a, b) => a.sendTime - b.sendTime);
    return list;
}

/**
 * 根据数据库数据，生成返回数据
 * @param {String} roleId 玩家id
 * @param {MailType[]} mails 单人邮件 
 * @param {GroupMailType[]} groupMails 多人邮件 
 * @param {ServerMailType[]} serverMails 全服邮件
 */
function getMailInfos(roleId: string, mails: MailType[], groupMails: GroupMailType[], serverMails: ServerMailType[]) {
    let list: MailParam[] = []; // 结果
    for(let mail of mails) {
        list.push(new MailParam( GM_MAIL_TYPE.SINGLE, mail, roleId));
    }
    for(let mail of groupMails) {
        list.push(new MailParam(GM_MAIL_TYPE.GROUP, mail, roleId));
    }
    for(let mail of serverMails) {
        list.push(new MailParam(GM_MAIL_TYPE.SERVER, mail, roleId));
    }
    return list;
}

export async function sendMailByContent(contentId: MAIL_TYPE, hisRoleId: string, params: { sendName?: string, endTime?: number, params?: string[], goods?: RewardInter[] }) {
    let f = new SendMailFun();
    let code = f.setWithContentId(contentId, params);
    await f.createSingleMails(code, [hisRoleId]);
    await f.pushToUsers();
}

export async function sendMailToGuildByContent(contentId: MAIL_TYPE, guildCode: string, params: { sendName?: string, endTime?: number, params?: string[], goods?: RewardInter[] }, guild?: GuildType) {
    if(!guild) guild = await GuildModel.findByCode(guildCode, null, '+members');
    if(!guild) return false;
    let f = new SendMailFun();
    let code = f.setWithContentId(contentId, params);
    await f.createGroupMails(code, guild.members, guildCode);
    await f.pushToUsers();
    return true;
}

export async function sendMailToLeagueByContent(contentId: MAIL_TYPE, leagueCode: string, params: { sendName?: string, endTime?: number, params?: string[], goods?: RewardInter[] }, league?: GVGLeagueType) {
    if(!league) league = await GVGLeagueModel.findByCodeWithoutPopulate(leagueCode);
    if(!league) return false;
    let f = new SendMailFun();
    let code = f.setWithContentId(contentId, params);
    await f.createGroupMails(code, league.members.map(cur => cur.roleId), null, leagueCode);
    await f.pushToUsers();
    return true;
}

export async function sendMailsByGmMail(gmmails: GMMailType[]) {
    let f = new SendMailFun();
    for(let gmmail of gmmails) {
        let { receivers, mailType, sendTimes } = gmmail;
        let code = await f.setWithGmMail(gmmail);
        if(!code) continue;
        let mapTemp = f.getMailTemp(code);

        if(sendTimes.indexOf(mapTemp.sendTime) == -1 && checkCanSend(gmmail, mapTemp)) {
            if(mailType == GM_MAIL_TYPE.SINGLE) {
                await f.createSingleMails(code, receivers.map(cur => cur.roleId));
            } else if (mailType == GM_MAIL_TYPE.GROUP) {
                await f.createGroupMails(code, receivers.map(cur => cur.roleId));
            } else if (mailType == GM_MAIL_TYPE.SERVER) {
                await f.createServerMails(code, receivers.map(cur => cur.serverId));
            }
        }
    }
    await f.pushToUsers();
}

function checkCanSend(gmmail: GMMailType, mapTemp: MailTemp ) {
    let { circleStart, circleEnd, timeType } = gmmail;
    if(timeType == MAIL_TIME_TYPE.CIRCLE) {
        if(circleStart > mapTemp.sendTime || circleEnd < mapTemp.sendTime) return false;
    }
    return true;
}
class MailTemp {
    public code: string;
    public gmmail: GMMailTypeParam;
    private contentId: MAIL_TYPE = MAIL_TYPE.SEND_MAIL;     // 0-读GmMail，1以上读dicMail
    private sendName: string = SEND_NAME;
    private title: string = SEND_TITLE;
    private content: string = '';
    private hasGoods: boolean = false;                      // 是否内含奖励
    private goods: RewardInter[] = [];                      // 发送的奖励
    public sendTime: number;
    private endTime: number;

    // 从dicMail读取数据
    public setWithContentId(contentId: MAIL_TYPE, params: { sendName?: string, params?: string[], goods?: RewardInter[] }) {
        this.code = genCode(8);
        let dicMail = gameData.mail.get(contentId);
        this.contentId = contentId;
        this.sendTime = nowSeconds();
        this.endTime = this.sendTime + dicMail?.time||0;
        this.content = this.getContent(dicMail.content, params.params);
        if(dicMail.title) this.title = dicMail.title;
        if(dicMail.sendName || params.sendName) this.sendName = params.sendName||dicMail.sendName;

        this.hasGoods = params.goods?.length > 0;
        this.goods = params.goods||[];
    }

    public async setWithGmMail(gmmail: GMMailType) {
        if(gmmail.timeType == MAIL_TIME_TYPE.IMMEDIATE) {
            this.sendTime = nowSeconds();
        } else if (gmmail.timeType == MAIL_TIME_TYPE.DELAY) {
            this.sendTime = gmmail.startTime;
        } else if (gmmail.timeType == MAIL_TIME_TYPE.CIRCLE) {
            let time = moment(moment().format('YYYY-MM-DD ' + gmmail.circleHour)).unix();
            if(time < nowSeconds()) {
                if(gmmail.circleDay == 0) {
                    time += 86400;
                } else {
                    let date = getCurDay(true); // 今天星期几
                    if(date < gmmail.circleDay) {
                        time += (gmmail.circleDay - date) * 86400;
                    } else {
                        time += (7 + date - gmmail.circleDay) * 86400;
                    }
                }
            }
            this.sendTime = time;
        }
        this.code = genCode(8);
        this.gmmail = gmmail;
        this.contentId = 0;
        this.endTime = this.sendTime + gmmail.expire * 60 * 60;
        this.sendName = gmmail.sendName;
        this.title = gmmail.title;
        this.content = gmmail.content;
        this.hasGoods = gmmail.hasGoods;
        this.goods = gmmail.goods;
        return true;
    }

    private getContent(content: string, params: string[] = []) {
        if(!content) content = '%d';
        for(let p of params) {
            content = content.replace(/%d/, p);
        }
        return content
    }

    public getCreateMailParams() {
        return {
            contentId: this.contentId, 
            gmmailId: this.gmmail?._id, 
            sendTime: this.sendTime, 
            endTime: this.endTime, 
            goods: this.goods,
            title: this.title,
            sendName: this.sendName,
            content: this.content,
            hasGoods: this.hasGoods
        }
    }
}

/**
 * 发送邮件方法类
 */
export class SendMailFun {
    private mailTemps = new Map<string, MailTemp>();
    private mails: MailType[] = [];
    private groupMails: GroupMailType[] = [];
    private serverMails: ServerMailType[] = [];

    public getMailTemp(code: string) {
        return this.mailTemps.get(code);
    }

    // 从dicMail读取数据
    public setWithContentId(contentId: MAIL_TYPE, params: { sendName?: string, params?: string[], goods?: RewardInter[] }) {
        let mailTemp = new MailTemp();
        mailTemp.setWithContentId(contentId, params);
        this.mailTemps.set(mailTemp.code, mailTemp);
        return mailTemp.code;
    }

    // 从GMMail表读取数据
    public async setWithGmMail(gmmail: GMMailType) {
        let mailTemp = new MailTemp();
        let canSet = mailTemp.setWithGmMail(gmmail);
        if(!canSet) return false
        this.mailTemps.set(mailTemp.code, mailTemp);
        return mailTemp.code;
    }

    // 生成单人邮件
    public async createSingleMails(code: string, roleIds: string[]) {
        let mapTemp = this.mailTemps.get(code);
        if(mapTemp.sendTime < nowSeconds()) return;
        for(let roleId of roleIds) {
            let originMail = await MailModel.addMail({ roleId, ...mapTemp.getCreateMailParams() });
            this.mails.push(originMail);
        }
        if(mapTemp.gmmail) await GMMailModel.sendMail(mapTemp.gmmail._id, mapTemp.sendTime);
    }

    // 生成多人邮件
    public async createGroupMails(code: string, roleIds: string[], guildCode?: string, leagueCode?: string) {
        let mapTemp = this.mailTemps.get(code);
        if(mapTemp.sendTime < nowSeconds()) return;
        let roleStatus = roleIds.map(roleId => {
            return { roleId, status: MAIL_STATUS.CREATE }
        })
        let originMail = await GroupMailModel.addMail({ roleStatus, guildCode, leagueCode, ...mapTemp.getCreateMailParams() });
        if(mapTemp.gmmail) await GMMailModel.sendMail(mapTemp.gmmail._id, mapTemp.sendTime);
        this.groupMails.push(originMail);
    }

    // 生成全服邮件
    public async createServerMails(code: string, serverIds: number[]) {
        let mapTemp = this.mailTemps.get(code);
        // console.log('###### createServerMails 1', code, this.mailTemps, mapTemp)
        if(mapTemp.sendTime < nowSeconds()) return;
        for(let serverId of serverIds) {
            let originMail = await ServerMailModel.addMail({ serverId, ...mapTemp.getCreateMailParams() });
            this.serverMails.push(originMail);
        }
        if(mapTemp.gmmail) await GMMailModel.sendMail(mapTemp.gmmail._id, mapTemp.sendTime);
    }

    public async setMails(mails: MailType[], groupMails: GroupMailType[], serverMails: ServerMailType[]) {
        this.mails.push(...mails);
        this.groupMails.push(...groupMails);
        this.serverMails.push(...serverMails);
    }

    // 将存储的邮件发出
    public async pushToUsers(myRoleId?: string, mySid?: string) {
        // 将可以发的邮件发出
        let pushByRoleId = new Map<string, MailParam[]>();
        let pushByGuildCode = new Map<string, MailParam[]>();
        let pushByServerId = new Map<number, MailParam[]>();

        for(let mail of this.mails) {
            if(mail.sendTime <= nowSeconds() && mail.endTime > nowSeconds()) {
                let mailParam = new MailParam(GM_MAIL_TYPE.SINGLE, mail);
                if(!pushByRoleId.has(mail.roleId)) pushByRoleId.set(mail.roleId, []);
                pushByRoleId.get(mail.roleId).push(mailParam);
            }
        }
        for(let mail of this.groupMails) {
            if(mail.sendTime <= nowSeconds() && mail.endTime > nowSeconds()) {
                let mailParam = new MailParam(GM_MAIL_TYPE.GROUP, mail);
                if(!!mail.guildCode) {
                    if(!pushByGuildCode.has(mail.guildCode)) pushByGuildCode.set(mail.guildCode, []);
                    pushByGuildCode.get(mail.guildCode).push(mailParam);
                } else {
                    for(let { roleId } of mail.roleStatus) {
                        if(!pushByRoleId.has(roleId)) pushByRoleId.set(roleId, []);
                        pushByRoleId.get(roleId).push(mailParam);
                    }
                }
            }
        }
        for(let mail of this.serverMails) {
            if(mail.sendTime <= nowSeconds() && mail.endTime > nowSeconds()) {
                let mailParam = new MailParam(GM_MAIL_TYPE.SERVER, mail);
                if(!pushByServerId.has(mail.serverId)) pushByServerId.set(mail.serverId, []);
                pushByServerId.get(mail.serverId).push(mailParam);
            }
        }

        for(let [roleId, mails ] of pushByRoleId) {
            let uids = await this.getUids(roleId, myRoleId, mySid);
            if(uids.length > 0 && mails.length > 0) {
                sendMessageToUsersWithSuc(PUSH_ROUTE.MAILS_ADD, { mails }, uids);
            }
        }

        for(let [guildCode, mails] of pushByGuildCode) {
            if(mails.length > 0) {
                await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.MAILS_ADD, { mails });
            }
        }

        for(let [serverId, mails] of pushByServerId) {
            if( mails.length > 0) {
                await sendMessageToServerWithSuc(serverId, PUSH_ROUTE.MAILS_ADD, { mails }, true);
            }
        }

        // 延时邮件，设置定时器
        pinus.app.rpc.systimer.systimerRemote.addMailsToSchedule.broadcast(this.mails, this.groupMails, this.serverMails);
    }

    private async getUids(roleId: string, myRoleId?: string, mySid?: string) {
        let uids: { uid: string, sid: string }[] = [];
        let sid = '';
        if(roleId == myRoleId) {
            sid = mySid;
        } else {
            let hisOnlineInfo = await getRoleOnlineInfo(roleId);
            if(hisOnlineInfo.isOnline) {
                sid = hisOnlineInfo.sid;
            }
        }
        if(!!sid) {
            uids.push({ uid: roleId, sid });
        }
        return uids;
    }

    public async saveRecord(createUid: number) {
        for(let mail of this.mails) {
            await GMMailRecordModel.createRecord(GM_MAIL_TYPE.SINGLE, mail, createUid);
        }
        for(let mail of this.groupMails) {
            await GMMailRecordModel.createRecord(GM_MAIL_TYPE.GROUP, mail, createUid);
        }
        for(let mail of this.serverMails) {
            await GMMailRecordModel.createRecord(GM_MAIL_TYPE.SERVER, mail, createUid);
        }
    }
}

export function checkMailGoods(mail: MailType | GroupMailType | ServerMailType, jewelCount: number) {
    let isEquipOver = false;
    for (let good of mail.goods) {
        let dicGoods = gameData.goods.get(good.id);
        let dicItid = ITID.get(dicGoods.itid);
        if (dicItid.table == 'jewel') { // 装备
            if (++jewelCount > BAG.BAG_EQUIP_UPLIMITED) {
                isEquipOver = true;
                break;
            }
        }
    }

    return {
        isEquipOver, jewelCount
    }
}