import { MailModel, MailType } from './../db/Mail';
import { DividendModel } from './../db/Dividend';
import { LOT_CODE_LEN, AUCTION_STAGE, AUCTION_TIME, DIVIDEND_CODE_LEN, DIVIDEND_STATUS, LOT_STATUS, MAIL_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, STATUS, DIVIDENDWEEKENDRATE, DIVIDENDMAXRATIO, DIVIDENDPOSITIONMAXRATIO, ROLE_RECEIVE_STATUS, AUCTION_BID_STATUS, DEBUG_MAGIC_WORD } from './../consts';
import { DividendRec, ItemReward } from "../domain/dbGeneral";
import { genCode, resResult } from '../pubUtils/util';
import { LotModel, LotParam } from '../db/Lot';
import { getZeroPointD, getTimeFunD } from '../pubUtils/timeUtil';
import { gameData, getGoodById } from '../pubUtils/data';
import { DividendParam, DividendType } from '../db/Dividend';
import { pushMail } from '../pubUtils/interface';
import { sendMailByContent } from './mailService';
import { pinus, BackendSession, FrontendOrBackendSession } from 'pinus';
import { participants } from './guildActivityService';
import { Member } from '../domain/battleField/guildActivity';

// ! 获取底价，假数据
export function getBasePrice(gid: number, count: number) {
    const good = getGoodById(gid);
    return (good ? good.quality * 100 : 100) * count;
}

// ! 获取一口价，假数据
export function getMaxPrice(gid: number, count: number) {
    const good = getGoodById(gid);
    return (good ? good.quality * 200 : 200) * count;
}

export function auctionStage() {
    const curTime = new Date().getTime();
    if (curTime < todayGuildBegin().getTime()) return AUCTION_STAGE.DEFAULT;
    if (curTime < todayWorldBegin().getTime() && curTime > todayGuildBegin().getTime()) return AUCTION_STAGE.GUILD;
    if (curTime > todayWorldBegin().getTime() && curTime < todayWorldEnd().getTime()) return AUCTION_STAGE.WORLD;
    if (curTime > todayWorldEnd().getTime()) return AUCTION_STAGE.END;
}

export async function debugAuctionLots(session: FrontendOrBackendSession, begin: Date) {
    const serverId = session.get('serverId');
    const lots = await LotModel.findWorldLotsByBegin(serverId, begin);
    return lots;
}

export async function officialAuctionLots(session: FrontendOrBackendSession, begin: Date) {
    const serverId = session.get('serverId');
    const guildCode = session.get('guildCode');
    const stage = auctionStage();
    let lots = [];
    if (stage === AUCTION_STAGE.DEFAULT || stage === AUCTION_STAGE.GUILD) {
        lots = await LotModel.findGuildLotsByBegin(guildCode, begin);
    } else if (stage === AUCTION_STAGE.WORLD) {
        lots = await LotModel.findWorldLotsByBegin(serverId, begin);
    }
    return lots;
}

export function auctionBegin() {
    return <Date>getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN);
}

export function guildAuctionEnd() {
    return <Date>getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.GUILD_END_HOUR, AUCTION_TIME.GUILD_END_MIN);
}

export function auctionEnd() {    
    return <Date>getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.WORLD_END_HOUR, AUCTION_TIME.WORLD_END_MIN);
}

export function todayGuildBegin() {
    return <Date>getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN);
}

export function yestodayGuildBegin() {    
    return <Date>getTimeFunD(getZeroPointD()).getAfterDayWithHour(-1, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN);
}

export function todayWorldBegin() {
    return <Date>getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.WORLD_BEGIN_HOUR, AUCTION_TIME.WORLD_BEGIN_MIN);
}

export function todayWorldEnd() {
    return <Date>getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.WORLD_END_HOUR, AUCTION_TIME.WORLD_END_MIN);
}

/**
 * @description 生成拍卖数据
 * @export
 * @param {string} guildCode
 * @param {number} sourceType
 * @param {string} sourceCode
 * @param {number} serverId
 * @param {ItemReward[]} rewards
 */
export async function genAuction(guildCode: string, sourceType: number, sourceCode: string, serverId: number, rewards: ItemReward[]) {
    const begin = auctionBegin();
    const guildEnd = guildAuctionEnd();
    const end = auctionEnd();
    const lotsData: LotParam[] = rewards.map(reward => {
        const { id, count } = reward;
        const code = genCode(LOT_CODE_LEN);
        return {
            auctionStage: AUCTION_STAGE.DEFAULT, sourceType,
            sourceCode, serverId, guildCode, code, gid: id, count, begin, end, status: LOT_STATUS.DEFAULT,
            maxPrice: getMaxPrice(id, count), curPrice: getBasePrice(id, count),
        }
    });
    const lots = await LotModel.createRecs(lotsData);
    const dividendCode = genCode(DIVIDEND_CODE_LEN);
    const dividendData: DividendParam = {
        guildCode, sourceType, sourceCode, serverId, code: dividendCode, dividends: [], totalPrice: 0, begin, lots: lots.map(lot => {
            const { code, gid } = lot;
            return { code, gid, price: 0, time: guildEnd, max: false, count: lot.count }
        }),
    };
    const dividend = await DividendModel.createDividend(dividendData);
    return { lots, dividend };
}

function posDividend(totalPrice: number, roleRatio: number, totalRatio: number) {
    const dividend = Math.floor(totalPrice * roleRatio / totalRatio);
    const maxDividend = Math.floor(totalPrice * DIVIDENDMAXRATIO);
    return dividend <= maxDividend ? dividend : Math.floor(maxDividend * roleRatio / DIVIDENDPOSITIONMAXRATIO);
}

function weekendDividend(posNum: number, date: Date) {
    const day = date.getDay();
    return (day === 0 || day === 6) ? Math.floor(posNum * DIVIDENDWEEKENDRATE) : 0;
}

function dividendRate(data: Member) {
    return gameData.guildPosition.get(data.job).sellRatio;
}

function totalDividendRatio(participantsData: Member[]) {
    const result = participantsData.reduce((sum, data) => {
        return sum + dividendRate(data);
    }, 0);
    return result;
}

export async function calculateDividend(dividend: DividendType) {
    if (!dividend) return null;
    const { code, guildCode, sourceType, sourceCode, lots, totalPrice, status, begin } = dividend;
    if (status === DIVIDEND_STATUS.SENT) return dividend;
    const calcuTotalPrice = lots.reduce((acc, lot) => { return acc + lot.price }, 0);
    if (calcuTotalPrice !== totalPrice) {
        await DividendModel.updateDividend(code, { totalPrice: calcuTotalPrice });
        // 更新 totalPrice
    }
    const participantsData = await participants(guildCode, sourceType, sourceCode);
    const totalRatio = totalDividendRatio(participantsData);
    const dividends: DividendRec[] = participantsData.map(data => {
        const { roleId } = data;
        const posNum = posDividend(calcuTotalPrice, dividendRate(data), totalRatio);
        const weekendNum = weekendDividend(posNum, begin);
        return {
            roleId,
            posNum, // 职位分红
            weekendNum, // 额外分红，周末
            total: posNum + weekendNum, // 总分红
            status: 0, // 0：未领取，1：已领取
        };
    });
    return await DividendModel.updateDividend(code, { dividends });
}

export async function startGuildAuction() {
    try {
        console.log('schedule startGuildAuction called:', new Date());
        const begin = todayGuildBegin();
        await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.GUILD);
        await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.ING);
        return true;
    } catch (e) {
        console.error('startGuildAuction err: ', e);
        return false;
    }
}

export async function startWorldAuction() {
    try {
        console.log('schedule startWorldAuction called:', new Date());
        const begin = todayGuildBegin();
        await LotModel.setLotSoldByBegin(begin);
        await LotModel.updateUnSoldLotsStageByBegin(begin, AUCTION_STAGE.WORLD);
        await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.END);
        return true;
    } catch (e) {
        console.error('startWorldAuction err: ', e);
        return false;
    }
}

export async function stopAuction() {
    try {
        console.log('schedule stopAuction called:', new Date());
        const begin = todayGuildBegin();
        await LotModel.setLotSoldByBegin(begin);
        await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.END);
        return true;
    } catch (e) {
        console.error('stopAuction err: ', e);
        return false;
    }
}

async function debugDividends() {
    const begin = yestodayGuildBegin();
    const dividends = await DividendModel.findDividendsByBegin(begin);
    const todayBegin = todayGuildBegin();
    const todayDividends = await DividendModel.findDividendsByBegin(todayBegin);
    return [...dividends, ...todayDividends];
}

async function officialDividends() {
    const begin = yestodayGuildBegin();
    const dividends = await DividendModel.findDividendsByBegin(begin);
    return dividends;
}

async function updateDebugDividendsStatus(status: number) {
    const begin = yestodayGuildBegin();
    await DividendModel.updateDividendsStatus(begin, status);
    const todayBegin = todayGuildBegin();
    await DividendModel.updateDividendsStatus(todayBegin,status);
}

async function updateOfficialDividendsStatus(status: number) {
    const begin = yestodayGuildBegin();
    await DividendModel.updateDividendsStatus(begin, status);
}

export async function sendUngotDividendJob() {
    try {
        return await sendUngotDividend();
    } catch (e) {
        console.error('sendUngotDividend err: ', e);
        return false;
    }
}

export function auctionBidStatus(roleId: string, lot: LotParam) {
    const { curBuyer, status } = lot;
    return curBuyer !== roleId ? AUCTION_BID_STATUS.RETURN : status === LOT_STATUS.SOLD || status === LOT_STATUS.MAX ? AUCTION_BID_STATUS.SUC : AUCTION_BID_STATUS.LEAD;
}

export function guildBidStatus(lot: { max: boolean, gid: number, count: number, price: number, dividendStatus: number }) {
    const { max, gid, count, price: lotPrice, dividendStatus } = lot;
    return max || lotPrice === getMaxPrice(gid, count) || ((dividendStatus == DIVIDEND_STATUS.END || dividendStatus == DIVIDEND_STATUS.SENT) && lotPrice !== 0)
}

export async function sendUngotDividend(debug = false) {
    try {
        console.log('schedule sendUngotDividend called:', new Date());
        const dividends = debug === true ? await debugDividends() : await officialDividends();
        const rewards = new Map<string, number>();
        for (let dividend of dividends) {
            if (!dividend.dividends || dividend.status === DIVIDEND_STATUS.SENT) continue;
            for (let dividendRec of dividend.dividends) {
                if (dividendRec.status === ROLE_RECEIVE_STATUS.YES) continue;
                const sum = rewards.get(dividendRec.roleId) || 0;
                rewards.set(dividendRec.roleId, sum + dividendRec.total);
            }
        }

        for (let [roleId, count] of rewards) {
            // TODO: 邮件类型修改
            await sendMailByContent(MAIL_TYPE.GUILD_BOSS_REWARD, roleId, {
                params: [JSON.stringify(count)],
                goods: [{ id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.GOLD), count }]
            });
        }
        
        debug === true ? await updateDebugDividendsStatus(DIVIDEND_STATUS.SENT) : await updateOfficialDividendsStatus(DIVIDEND_STATUS.SENT);
        return true;
    } catch (e) {
        console.error('sendUngotDividend err: ', e);
        return false;
    }
}

/**
 * 获取拍卖行数据
 * @param guildCode 
 * @param session 
 * @param magicWord 
 */
export async function getAuction(guildCode: string, session: FrontendOrBackendSession, magicWord?: string) {
    const begin = todayGuildBegin();
    let lots = magicWord === DEBUG_MAGIC_WORD ? await debugAuctionLots(session, begin) : await officialAuctionLots(session, begin);

    const dividends = await DividendModel.findGuildDividendsByBegin(guildCode, begin);
    return { lots, dividends };
}