
import { Service } from 'egg';
import { REDIS_KEY, PAY_37_CALLBACK_CODE, SDK_37_CONST, ORDER_STATE, SDK_37_TREAT_CODE, SERVER_STATUS, SDK_37_REFUND_CODE } from '@consts';
import { GetGuildInfoByUserParam, GetServerListParam, GuildNameCallBackParam, IOSRefundParam, PayCallback37Data, RoleNameCallBackParam } from '../domain/sdk';
import { RedisClient } from 'redis';
import { checkParamPrice, get37GetServerMd5Sign, get37Md5SignA, get37Md5SignB, getChannelId, getRedisSubChannel } from '../pubUtils/sdkUtil';
import { UserOrderModel } from '@db/UserOrder';
import { nowSeconds } from 'app/pubUtils/timeUtil';
import { RoleModel } from '@db/Role';
import { gameData } from 'app/pubUtils/data';
import { resResult } from 'app/pubUtils/util';
import { UserModel } from '@db/User';
import { UserGuildModel } from '@db/UserGuild';
import { GuildModel } from '@db/Guild';
import { ServerlistModel } from '@db/Serverlist';
import moment = require('moment');
import { RegionModel } from '@db/Region';

/**
 * Test Service
 */
export default class Sdk extends Service {

    public check37Sign(params: PayCallback37Data|IOSRefundParam) {
        let sign = get37Md5SignA(params.getBody(), SDK_37_CONST.PAY_KEY);
        console.log('******37Sign', sign);
        return sign == params.sign;
    }

    public async pay37Callback(params: PayCallback37Data) {
        const { ctx } = this;
        const { app } = ctx;

        let checkResult = this.check37Sign(params);
        if(!checkResult) return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.MD5_ERR, '');
        
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check sign ok`);
        if(gameData.whiteip.indexOf(ctx.clientIp) == -1) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.IP_LIMIT, '');
        }

        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check ip ok`);
        if(nowSeconds() - params.time > 15 * 60) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.TIME_IS_EXPIRED, '');
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check time ok`);

        let order = await UserOrderModel.findOrder(params.order_no);
        if(!order) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.FAIL, '');
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check order ok`);
        if(order.state != ORDER_STATE.APPLY) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.SUCCESS, ''); 
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check order status ok ${params.money} ${typeof params.money}`);
        if(!checkParamPrice(order.price, params.money)) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.PAY_ERR, ''); 
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check money ok ${order.price} ${params.money}`);

        let role = await RoleModel.findByRoleId(order.roleId);
        if(!role) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.ROLE_NOT_FOUND, ''); 
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check role ok`);
        if(role.serverId != parseInt(`${params.sid}`)) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.SERVER_NOT_FOUND, ''); 
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback check server ok`);
        
        order = await UserOrderModel.check(order.roleId, order.localOrderID);
        if(!order) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.FAIL, '');
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback save order check ok`);

        let redisClient: RedisClient = app.context.redisClient;
        let name = getRedisSubChannel(REDIS_KEY.PAY_CHANNEL, app.config.env);
        let result = await redisClient.publishAsync(name, JSON.stringify(params));
        if(result == 0) {
            return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.SERVER_IS_BUSY, ''); 
        }
        ctx.service.utils.log('DEBUG', `[${ctx.request.url}] [${ctx.logcode}] pay37Callback redis publish ok`);

        return ctx.service.utils.resResult(PAY_37_CALLBACK_CODE.SUCCESS, '');
    }

    // public async pay37IOSCallback(params: PayCallback37Data) {
    //     const { ctx } = this;
    //     const { app } = ctx;

    //     let checkResult = this.check37Sign(params);
    //     if(!checkResult) return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.MD5_ERR, '');
        
    //     console.log('*****pay37Callback check sign ok')
    //     if(gameData.whiteip.indexOf(ctx.clientIp) == -1) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.IP_LIMIT, '');
    //     }

    //     console.log('*****pay37Callback check ip ok')
    //     if(nowSeconds() - params.time > 15 * 60) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.TIME_IS_EXPIRED, '');
    //     }
    //     console.log('*****pay37Callback check time ok')

    //     let order = await UserOrderModel.findOrder(params.order_no);
    //     if(!order) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.ORDER_DUPLICATE, '');
    //     }
    //     console.log('*****pay37Callback check order ok')
    //     if(order.state != ORDER_STATE.APPLY) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.SUCCESS, ''); 
    //     }
    //     console.log('*****pay37Callback check order status ok', params.money, typeof params.money)
    //     if(order.price != parseFloat(params.money)) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.FAIL, ''); 
    //     }
    //     console.log('*****pay37Callback check money ok', order.price, params.money)

    //     let role = await RoleModel.findByRoleId(order.roleId);
    //     if(!role) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.ROLE_NOT_FOUND, ''); 
    //     }
    //     console.log('*****pay37Callback check role ok')
    //     if(role.serverId != params.sid) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.ROLE_NOT_FOUND, ''); 
    //     }
    //     console.log('*****pay37Callback check server ok')
        
    //     order = await UserOrderModel.check(order.roleId, order.localOrderID);
    //     if(!order) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.FAIL, '');
    //     }
    //     console.log('*****pay37Callback save order check ok')

    //     let redisClient: RedisClient = app.context.redisClient;
    //     let name = getRedisSubChannel(REDIS_KEY.PAY_CHANNEL, app.config.env);
    //     let result = await redisClient.publishAsync(name, JSON.stringify(params));
    //     if(result == 0) {
    //         return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.SERVER_IS_BUSY, ''); 
    //     }
    //     console.log('*****pay37Callback redis publish ok')

    //     return ctx.service.utils.resResult(PAY_IOS_37_CALLBACK_CODE.SUCCESS, '');
    // }

    public async refundIOSCallback(params: IOSRefundParam) {
        const { ctx } = this;
        const { app } = ctx;

        let checkResult = this.check37Sign(params);
        if(!checkResult) return ctx.service.utils.resResult(SDK_37_REFUND_CODE.MD5_ERR, '');
        
        console.log('*****refundIOSCallback check sign ok')
        if(gameData.whiteip.indexOf(ctx.clientIp) == -1) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.IP_LIMIT, '');
        }

        console.log('*****refundIOSCallback check ip ok')
        if(nowSeconds() - params.time > 15 * 60) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.TIME_IS_EXPIRED, '');
        }
        console.log('*****refundIOSCallback check time ok, order_no:', params.order_no)

        let order = await UserOrderModel.findOrder(params.order_no);
        if(!order) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.FAIL, '');
        }
        console.log('*****refundIOSCallback check order ok')
        if(order.state != ORDER_STATE.RESULT_SUCCESS) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.SUCCESS, ''); 
        }
        console.log('*****refundIOSCallback check order status ok', params.money, typeof params.money)

        let role = await RoleModel.findByRoleId(order.roleId);
        if(!role) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.ROLE_NOT_FOUND, ''); 
        }
        console.log('*****refundIOSCallback check role ok')
        if(role.serverId != params.sid) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.ROLE_NOT_FOUND, ''); 
        }
        console.log('*****refundIOSCallback check server ok')
        
        order = await UserOrderModel.startRefund(order.roleId, order.localOrderID);
        if(!order) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.FAIL, '');
        }
        console.log('*****refundIOSCallback save order check ok')

        let redisClient: RedisClient = app.context.redisClient;
        let name = getRedisSubChannel(REDIS_KEY.REFUND_CHANNEL, app.config.env);
        let result = await redisClient.publishAsync(name, JSON.stringify(params));
        if(result == 0) {
            return ctx.service.utils.resResult(SDK_37_REFUND_CODE.FAIL, ''); 
        }
        console.log('*****refundIOSCallback redis publish ok')

        return ctx.service.utils.resResult(SDK_37_REFUND_CODE.SUCCESS, '');
    }

    public check37WordsSign(params: RoleNameCallBackParam|GuildNameCallBackParam|GetGuildInfoByUserParam) {
        let sign = get37Md5SignB(params.getBody(), SDK_37_CONST.CHAT_KEY);
        console.log('******37Sign', sign);
        return sign == params.sign;
    }

    public treatRoleOrGuildNameValidate(params: RoleNameCallBackParam|GuildNameCallBackParam|GetGuildInfoByUserParam) {

        if(!params.checkParams()) return SDK_37_TREAT_CODE.WRONG_PARAMS;

        if(params.game_key != SDK_37_CONST.GAME_KEY) {
            console.error('用户名或军团名违规处理, gamekey错误', params.game_key);
            return SDK_37_TREAT_CODE.ERR;
        }

        let checkResult = this.check37WordsSign(params);
        if(!checkResult) return SDK_37_TREAT_CODE.SIGN_ERR;

        if(nowSeconds() - params.time > 15 * 60) {
            return SDK_37_TREAT_CODE.TIME_IS_EXPIRED;
        }

        // if(gameData.whiteip.indexOf(ip) == -1) {
        //     console.error('用户名或军团名违规处理, ip限制', ip);
        //     return SDK_37_TREAT_CODE.ERR;
        // }
        return SDK_37_TREAT_CODE.SUCCESS
    }


    // 用户名违规处理
    public async treatRoleName(params: RoleNameCallBackParam) {
        const { ctx } = this;
        const { app } = ctx;
        let check = this.treatRoleOrGuildNameValidate(params);
        if(check.code != SDK_37_TREAT_CODE.SUCCESS.code) return check.code;

        // 1. 标记这个玩家违规
        let channelId = getChannelId('37', params.username);
        console.log(channelId);
        let user = await UserModel.findUserByChannel(channelId);
        if(!user) {
            console.error('用户名违规处理, 未找到玩家账号', channelId);
            return SDK_37_TREAT_CODE.SUCCESS.code;
        }

        let role = await RoleModel.findByUidAndSetMark(user.uid, params.sid);
        if(!role) {
            console.error('用户名违规处理, 未找到玩家角色', user.uid, params.sid);
            return SDK_37_TREAT_CODE.SUCCESS.code;
        }

        if(params.actor.trim() != role.roleName.trim() || params.actor_id.trim() != role.roleId.trim()) {
            console.error('用户名违规处理, roleName或roleId对不上', params.actor, role.roleName, params.actor.trim() == role.roleName.trim(), params.actor_id, role.roleId);
            return SDK_37_TREAT_CODE.SUCCESS.code;
        }

        // 2. redis发布
        let redisClient: RedisClient = app.context.redisClient;
        let name = getRedisSubChannel(REDIS_KEY.TREAT_ROLE_CHANNEL, app.config.env);
        let result = await redisClient.publishAsync(name, role.roleId);
        if(result == 0) {
            console.error('用户名违规处理, 未发布到订阅频道');
            return SDK_37_TREAT_CODE.ERR.code; 
        }

        return check.code
    }

    // 公会信息违规处理
    public async treatGuildName(params: GuildNameCallBackParam) {
        const { ctx } = this;
        const { app } = ctx;
        let check = this.treatRoleOrGuildNameValidate(params);
        if(check.code != SDK_37_TREAT_CODE.SUCCESS.code) return check.code;

        // 1. 标记军团违规
        let guild = await GuildModel.findByCodeAndSetMark(params.guildid, params.sid);
        if(!guild) {
            console.log('未找到军团', params.guildid);
            return SDK_37_TREAT_CODE.SUCCESS.code;
        }

        // 2. redis发布
        let redisClient: RedisClient = app.context.redisClient;
        let name = getRedisSubChannel(REDIS_KEY.TREAT_GUILD_CHANNEL, app.config.env);
        let content = JSON.stringify({ code: guild.code, serverId: params.sid, type: params.type });
        let result = await redisClient.publishAsync(name, content);
        if(result == 0) {
            return SDK_37_TREAT_CODE.ERR.code; 
        }

        return check.code
    }

    // 根据玩家获得公会信息
    public async getGuildByUser(params: GetGuildInfoByUserParam) {
        let check = this.treatRoleOrGuildNameValidate(params);
        if(check.code != SDK_37_TREAT_CODE.SUCCESS.code) return resResult(check);

        let channelId = getChannelId('37', params.uid);
        let user = await UserModel.findUserByChannel(channelId);
        if(!user) return resResult(SDK_37_TREAT_CODE.USER_NOT_FOUND);

        let role = await RoleModel.findByUid(user.uid, params.sid);
        if(!role) return resResult(SDK_37_TREAT_CODE.ROLE_NOT_FOUND);

        let userGuild = await UserGuildModel.getMyGuild(role.roleId);
        if(userGuild) {
            let guild = await GuildModel.findByCode(userGuild.guildCode, params.sid);
            if(guild) {
                return resResult(check, [{ guildid: guild.code, guild_name: guild.name }]);
            }
        }

        return resResult(check, []);
    }

    private valiedateGetServerList(params:GetServerListParam) {
        if(!params.checkParams()) return SDK_37_TREAT_CODE.WRONG_PARAMS;
        if(params.gid != SDK_37_CONST.GAME_ID) return SDK_37_TREAT_CODE.WRONG_PARAMS

        let sign = get37GetServerMd5Sign(params, SDK_37_CONST.LOGIN_KEY);
        if(sign != params.sign) return SDK_37_TREAT_CODE.SIGN_ERR;

        if(nowSeconds() - params.time > 15 * 60) {
            return SDK_37_TREAT_CODE.TIME_IS_EXPIRED;
        }
        return SDK_37_TREAT_CODE.SUCCESS;
    }

    public async getServerList(params:GetServerListParam) {
        let validate = this.valiedateGetServerList(params);
        if(validate.code != SDK_37_TREAT_CODE.SUCCESS.code) {
            return {
                state: 0,
                data: null,
                msg: resResult(validate).msg
            }
        }

        let servers = await ServerlistModel.getAllServerList();
        let regions = await RegionModel.getAllRegion();
        let data = servers.map(server => {
            let statue = 0;
            switch(server.status) {
                case SERVER_STATUS.MAINTENANCE:
                    statue = 0; break;
                case SERVER_STATUS.WILL_OPEN:
                    statue = 1; break;
                case SERVER_STATUS.NEW:
                case SERVER_STATUS.HOT:
                    statue = 2; break;
            }
            let region = regions.find(cur => cur.env == server.env);
            return {
                dsid: server.id,
                dsname: region? `${region.name} ${server.name}`: server.name,
                start_time: moment(server.openTime * 1000).format('YYYY-MM-DD HH:mm:ss'),
                statue
            }
        })

        return {
            state: 1,
            data,
            msg: '成功'
        }
    }

    public reportTAEventWithDistinctId(distinctId: string, eventName: string, properties: any, ip: string) {
        let ta = this.app.context.ta;
        if(!ta) return
        let event = {
            // 账号 ID （可选)
            accountId: "",
            // 访客 ID （可选)，账号 ID 和访客 ID 不可以都为空
            distinctId: `${distinctId}`,
            // 事件名称 （必填)
            event: eventName,
            // 事件时间 (可选) 如果不填，将以调用接口时的时间作为事件时间
            time: new Date(),
            // 事件 IP (可选) 当传入 IP 地址时，后台可以解析所在地
            ip: ip,
            // 事件属性 (可选)
            properties,
            callback(err) {
                console.log('*****测试接入事件', err)
            }

        };
        ta.track(event);
    }

    public reportTAEventWithRoleIdAndDistinctId(roleId: string, distinctId: string, eventName: string, properties: any, ip?: string) {
        let ta = this.app.context.ta;
        if(!ta) return
        let event = {
            // 账号 ID （可选)
            accountId: `${roleId}`,
            // 访客 ID （可选)，账号 ID 和访客 ID 不可以都为空
            distinctId: distinctId? `${distinctId}`: "",
            // 事件名称 （必填)
            event: eventName,
            // 事件时间 (可选) 如果不填，将以调用接口时的时间作为事件时间
            time: new Date(),
            // 事件 IP (可选) 当传入 IP 地址时，后台可以解析所在地
            ip: ip,
            // 事件属性 (可选)
            properties,
            callback(err) {
                console.log('*****测试接入事件', err)
            }

        };
        ta.track(event);
    }

}
