const axios = require('axios')
const Redis = require('../../plugin/DataBase/Redis')
const db = require('../../plugin/DataBase/db')
const mq = require('../../plugin/mq')
const Logger = require('../Logger')
const path = require('path')
const EmailTemplate = require('../../plugin/Email/emailTemplate')
const { offsetGeoPoints, geoPointsToString, getCurrentTime } = require('./generatePath')
const { sendStartLepao, sendStopLepao, lepaoUserInfo } = require('./lepaoAPI')

class cgLepao {
    constructor() {

        this.logger = new Logger(path.join(__dirname, '../logs/cgLepao.log'), 'INFO')
    }

    // 获取跑步线路
    async getPath(account) {
        this.logger.info(`${account}开始获取路径`)
        const accountSql = 'SELECT area, sex FROM lepao_account WHERE student_num = ?'
        const rows = await db.query(accountSql, [account])
        if (!rows || rows.length === 0) {
            this.logger.error(`${account}无法获取账号数据`)
            throw new Error('无法获取账号数据')
        }

        const { area, sex } = rows[0]

        let max = 4.00
        let min = 2.00
        let speed = 9
        if (sex === 1) {
            max = 2.00
            min = 1.60
            speed = 11
        }

        this.logger.info(`${account}路径参数: area=${area ?? '随机'}`)

        let pathSql = 'SELECT id, time FROM path_data WHERE state = 1 AND distance < ? AND distance > ? AND speed <= ?'
        const pathParams = [max, min, speed]

        if (area) {
            pathSql += ' AND run_zone_name = ?'
            pathParams.push(area)
        }

        pathSql += ' ORDER BY count ASC LIMIT 1'

        const paths = await db.query(pathSql, pathParams)
        if (!paths || paths.length === 0) {
            this.logger.error(`${account}未找到符合条件的路线`)
            throw new Error('未找到符合条件的路线，请改变路径选择条件')
        }

        const randomPath = paths[0]

        const updateSql = 'UPDATE path_data SET count = count + 1 WHERE id = ?'
        await db.query(updateSql, [randomPath.id])
        this.logger.info(`${account}路径选中id=${randomPath.id}，计数加1成功`)
        return { path_id: randomPath.id, path_time: randomPath.time, area }
    }

    async writeRedis(account) {
        try {
            // 计算至明日0时过期的秒数
            const now = new Date()
            const tomorrow = new Date().setHours(24, 0, 0, 0)
            const exp = Math.floor((tomorrow - now) / 1000)

            await Redis.set(`cgLepaoSuccess:${account}`, account, {
                EX: exp
            })
        } catch (error) {
            this.logger.error(`redis缓存乐跑记录失败: ${error.stack || '未知错误'}`)
        }
    }

    async lepaoFail(uuid) {
        try {
            this.logger.info(`返还用户 ${uuid} 乐跑次数`)
            let sql = 'UPDATE users SET lepao_count = lepao_count + 1 WHERE uuid  = ?'
            await db.query(sql, [uuid])

            this.logger.info(`返还用户 ${uuid} 乐跑次数成功`)
        } catch (error) {
            this.logger.error(`返还用户 ${uuid} 乐跑次数时出错: ${error.stack || error.message}`)
        }
    }

    async sendSuccessEmail(account, lepaoData, total_num) {
        try {
            this.logger.info(`${account}发送乐跑成功邮件`)
            const emailSql = 'SELECT name, email, target_count, auto_run FROM lepao_account WHERE student_num = ?'
            const rows = await db.query(emailSql, [account])
            if (!rows || rows.length === 0) {
                this.logger.error(`${account}查找用户邮箱失败`)
                throw new Error('查找用户邮箱失败')
            }

            const data = {
                ...lepaoData,
                term_num: rows[0].target_count,
                total_num,
                name: rows[0].name,
                account
            }

            await EmailTemplate.lepaoSuccess(rows[0].email, data)
            this.logger.info(`${account}乐跑成功邮件发送完成`)

            if (rows[0].target_count !== 0 && total_num >= rows[0].target_count && rows[0].auto_run === 1) {
                this.logger.info(`${account}乐跑目标完成，发送乐跑结束邮件并关闭自动乐跑`)
                await EmailTemplate.lepaoOver(rows[0].email, data)
                let overSql = 'UPDATE lepao_account SET auto_run = 0 WHERE student_num = ?'
                let overRows = await db.query(overSql, [account])
                if (!overRows || overRows.affectedRows !== 1)
                    this.logger.warn(`${account}乐跑结束后关闭自动乐跑失败`)
                else
                    this.logger.info(`${account}自动乐跑关闭成功`)
            }
        } catch (error) {
            this.logger.error(`发送成功邮件失败: ${error.stack || error.message}`)
        }
    }

    async sendFailEmail(account, reason) {
        try {
            this.logger.info(`${account}发送乐跑失败邮件，原因: ${reason}`)
            const emailSql = 'SELECT name, email FROM lepao_account WHERE student_num = ?'
            const rows = await db.query(emailSql, [account])
            if (!rows || rows.length == 0) {
                this.logger.error(`${account}查找用户邮箱失败`)
                throw new Error('查找用户邮箱失败')
            }

            const data = {
                name: rows[0].name,
                account,
                reason: reason || '系统繁忙，请联系客服或稍后再试'
            }

            await EmailTemplate.lepaoFail(rows[0].email, data)
            this.logger.info(`${account}乐跑失败邮件发送完成`)
        } catch (error) {
            this.logger.error(`发送失败邮件失败: ${error.stack || error.message}`)
        }
    }

    async beginLepao(uuid, account) {
        try {
            this.logger.info(`${account}开始执行乐跑流程`)

            // 检查redis是否存在当天乐跑成功记录
            // const isSuccess = await Redis.get(`cgLepaoSuccess:${account}`)
            // if (isSuccess)
            //     throw new Error('该账号当天已存在成功乐跑记录')
            const isProgress = await Redis.get(`cgLepaoProgress:${account}`)
            if (isProgress)
                throw new Error('该账号已进入乐跑任务队列，请等待乐跑完成后再进行乐跑操作')

            //已开始乐跑，存入Redis
            await Redis.set(`cgLepaoProgress:${account}`, account)

            // 扣除乐跑次数
            this.logger.info(`${account}开始扣减乐跑次数`)
            const useLepaoCountSql = 'UPDATE users SET lepao_count = lepao_count - 1 WHERE uuid  = ?'
            await db.query(useLepaoCountSql, [uuid])
            this.logger.info(`${account}扣减乐跑次数完成`)

            const userPermissionSql = 'SELECT lepao_count FROM users WHERE uuid = ?'
            const userPermissionData = await db.query(userPermissionSql, [uuid])
            if (!userPermissionData || userPermissionData.length !== 1) {
                this.logger.error(`${account}无法获取用户信息`)
                throw new Error('无法获取用户信息，请重试或联系哪吒乐跑客服')
            }

            if (userPermissionData[0].lepao_count < 1) {
                this.logger.warn(`${account}乐跑次数不足`)
                throw new Error('用户乐跑次数不足，请购买乐跑套餐！')
            }

            // 获取路径 ID
            const { path_id, path_time, area } = await this.getPath(account)

            // 提交乐跑请求
            const { task_id, startTime } = await sendStartLepao(account)

            let taskSql = 'INSERT INTO lepao_record (uuid, lepao_account, task_id, path_id, startTime, time, area) VALUES (?, ?, ?, ?, ?, ?, ?)'
            let taskRows = await db.query(taskSql, [uuid, account, task_id, path_id, startTime, path_time, area])
            if (!taskRows || taskRows.affectedRows === 0) {
                this.logger.error(`${account}乐跑任务入库失败`)
                throw new Error('乐跑任务发起失败，请联系客服或稍后再试')
            }

            let task = {
                uuid, account, task_id, path_id, path_time, startTime
            }
            let delayMs = path_time * 1000

            const ch = await mq.getChannel('cg_run_task')

            ch.sendToQueue(
                'cg_run_delay_queue',
                Buffer.from(JSON.stringify(task)),
                {
                    expiration: delayMs.toString(),
                    persistent: true
                }
            )

            this.logger.info(`已投递跑步任务 user=${account} 延迟=${path_time}s`)
        } catch (error) {
            await this.lepaoFail(uuid)
            let failSql = 'UPDATE lepao_records SET state = 2 WHERE lepao_account = ? AND state = 0 ORDER BY id DESC LIMIT 1'
            await db.query(failSql, [account])
            await this.sendFailEmail(account, error.message)
            await Redis.del(`cgLepaoProgress:${account}`)
            this.logger.error(`乐跑流程失败: ${error.stack || '未知错误'}`)
        }
    }

    async lepaoFinish() {
        const ch = await mq.getChannel('cg_run_finish')
        ch.prefetch(1) // 控制并发

        await ch.consume('cg_run_finish_queue', async (msg) => {
            if (!msg) return
            const task = JSON.parse(msg.content.toString())
            this.logger.info(`开始处理乐跑结束任务 user=${task.account} task_id=${task.task_id}`)

            try {
                const currentTime = getCurrentTime()

                let pathSql = 'SELECT * FROM path_data WHERE id = ?'
                const pathRows = await db.query(pathSql, [task.path_id])
                if (!pathRows || pathRows.length === 0) {
                    this.logger.error(`${task.account}乐跑路径数据获取失败 id=${task.path_id}`)
                    throw new Error('乐跑路径数据获取失败，请联系客服或稍后再试')
                }
                let pathData = pathRows[0]
                let pathLine = offsetGeoPoints(pathData.data)
                let pathLineStr = geoPointsToString(pathLine)

                let lepaoData = {
                    account: task.account,
                    calorie: Number(pathData.calorie).toFixed(2),
                    distance: Number(pathData.distance).toFixed(6),
                    distribution: pathData.speed,
                    duration: pathData.time,
                    endTime: currentTime,
                    id: task.task_id,
                    maxDistribution: '0.00',
                    pathLine: pathLineStr,
                    startTime: task.startTime,
                }

                this.logger.info(`${task.account}乐跑数据构造结束：`)
                console.log(lepaoData)

                let lepaoResult = await sendStopLepao(lepaoData)

                if (lepaoResult.status !== 1) {
                    this.logger.error(`${task.account}乐跑失败！`)
                    let updateSql = 'UPDATE lepao_record SET startTime = ?, endTime = ?, frequency = ?, distance = ?, path_data = ?, state = 2 WHERE task_id = ?'
                    await db.query(updateSql, [
                        lepaoResult.startTime,
                        lepaoResult.endTime,
                        lepaoResult.frequency,
                        pathData.distance,
                        pathLine,
                        task.task_id
                    ])

                    throw new Error('乐跑失败，请联系客服或稍后再试')
                }

                let updateSql = 'UPDATE lepao_record SET startTime = ?, endTime = ?, frequency = ?, distance = ?, path_data = ?, state = 1 WHERE task_id = ?'
                await db.query(updateSql, [
                    lepaoResult.startTime,
                    lepaoResult.endTime,
                    lepaoResult.frequency,
                    pathData.distance,
                    pathLine,
                    task.task_id
                ])

                this.logger.info(`${task.account}乐跑成功，获取账号信息`)

                let userInfo = await lepaoUserInfo(task.account)
                const { frequency } = userInfo

                lepaoResult.time = pathData.time
                lepaoResult.distance = Number(pathData.distance).toFixed(2)
                lepaoResult.area = pathData.run_zone_name

                await this.writeRedis(task.account)

                let updateAccountSql = 'UPDATE lepao_account SET total_num = ? WHERE student_num = ?'
                await db.query(updateAccountSql, [frequency, task.account])

                this.sendSuccessEmail(task.account, lepaoResult, frequency)
            } catch (error) {
                await this.lepaoFail(task.uuid)
                await this.sendFailEmail(task.account, error.message)
                this.logger.error(`乐跑结束处理失败: ${error.stack || '未知错误'}`)
            } finally {
                await Redis.del(`cgLepaoProgress:${task.account}`)
                ch.ack(msg)
            }
        })

        this.logger.info('乐跑任务结束消费者启动完成')
    }
}

const Lepao = new cgLepao()
module.exports.Lepao = Lepao
