const API = require('../../lib/API')
const db = require('../../plugin/DataBase/db')
const Redis = require('../../plugin/DataBase/Redis')
const { BaseStdResponse } = require('../../BaseStdResponse')
const AccessControl = require('../../lib/AccessControl')
const { validateCoupon, recordUsage, roundMoney } = require('../../lib/CouponService')
const { buildPaymentSession } = require('../../lib/OrderPayment')
const { createPaymentAttempt, generateGatewayOrderNo } = require('../../lib/OrderPaymentAttempt')
const { enqueueOrderPaymentCheck } = require('../../plugin/mq/orderPaymentWorker')

function generateOrderId() {
    const now = new Date()
    const pad = (n, w = 2) => n.toString().padStart(w, '0')
    return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}` +
        `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}` +
        `${pad(now.getMilliseconds(), 3)}`
}

async function acquireCouponUsageLock(couponId) {
    const lockKey = `coupon:usage:${couponId}`
    const rows = await db.query('SELECT GET_LOCK(?, 5) AS ok', [lockKey])
    return { lockKey, ok: Number(rows?.[0]?.ok || 0) === 1 }
}

async function releaseCouponUsageLock(lockKey) {
    if (!lockKey) return
    try { await db.query('SELECT RELEASE_LOCK(?)', [lockKey]) } catch (_) { }
}

class CreateOrder extends API {
    constructor() {
        super()
        this.setPath('/Order/CreateOrder')
        this.setMethod('POST')
    }

    async onRequest(req, res) {
        const { uuid, session, goods_id, pay_type, coupon_code } = req.body

        if ([uuid, session, goods_id, pay_type].some(v => v === '' || v === null || v === undefined)) {
            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
        }

        if (!await AccessControl.checkSession(uuid, session)) {
            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
        }

        let couponLockKey = null
        try {
            const goodsRows = await db.query(
                'SELECT name, price, num, state FROM goods WHERE id = ? LIMIT 1',
                [goods_id]
            )
            if (!goodsRows || goodsRows.length !== 1) {
                return res.json({ ...BaseStdResponse.ERR, msg: '商品不存在' })
            }

            const goods = goodsRows[0]
            if (Number(goods.num) < 1 || Number(goods.state) !== 1) {
                return res.json({ ...BaseStdResponse.ERR, msg: '商品已下架或库存不足' })
            }

            const createTime = Date.now()
            const orderId = generateOrderId()
            const normalizedPayType = String(pay_type).trim()
            const originalPrice = roundMoney(goods.price)
            let finalPrice = originalPrice
            let discountAmount = 0
            let couponId = null
            let appliedCouponCode = null

            if (coupon_code && String(coupon_code).trim()) {
                let couponResult = await validateCoupon({
                    code: coupon_code,
                    userUuid: uuid,
                    goodsId: goods_id,
                    goodsPrice: goods.price
                })
                if (!couponResult.ok) return res.json({ ...BaseStdResponse.ERR, msg: couponResult.msg })

                const lockRet = await acquireCouponUsageLock(couponResult.couponId)
                if (!lockRet.ok) {
                    return res.json({ ...BaseStdResponse.ERR, msg: '优惠码校验繁忙，请稍后重试' })
                }
                couponLockKey = lockRet.lockKey

                couponResult = await validateCoupon({
                    code: coupon_code,
                    userUuid: uuid,
                    goodsId: goods_id,
                    goodsPrice: goods.price
                })
                if (!couponResult.ok) return res.json({ ...BaseStdResponse.ERR, msg: couponResult.msg })

                finalPrice = couponResult.finalPrice
                discountAmount = couponResult.discountAmount
                couponId = couponResult.couponId
                appliedCouponCode = couponResult.code
            }

            const gatewayOrderNo = generateGatewayOrderNo(orderId)
            const payment = await buildPaymentSession({
                orderId,
                gatewayOrderNo,
                payType: normalizedPayType,
                goodsName: goods.name,
                price: finalPrice,
                deviceType: req.headers['device-type']
            })

            const conn = await db.connect()
            let insertResult
            try {
                await conn.beginTransaction()

                const [stockRes] = await conn.execute(
                    'UPDATE goods SET num = num - 1 WHERE id = ? AND num > 0 AND state = 1',
                    [goods_id]
                )
                if (!stockRes || stockRes.affectedRows !== 1) {
                    await conn.rollback()
                    return res.json({ ...BaseStdResponse.ERR, msg: '商品库存不足或已下架' })
                }

                const [orderRes] = await conn.execute(
                    `INSERT INTO orders (
                        orderId, create_user, create_time, goods_id, price, pay_type,
                        original_price, discount_amount, coupon_id, coupon_code
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
                    [
                        orderId,
                        uuid,
                        createTime,
                        goods_id,
                        finalPrice,
                        normalizedPayType,
                        originalPrice,
                        discountAmount,
                        couponId,
                        appliedCouponCode
                    ]
                )
                insertResult = orderRes

                await createPaymentAttempt({
                    orderId,
                    payType: normalizedPayType,
                    gatewayOrderNo,
                    executor: conn
                })

                if (couponId) {
                    await recordUsage(couponId, orderId, uuid, discountAmount, conn)
                }

                await conn.commit()
            } catch (writeError) {
                try { await conn.rollback() } catch (_) { }
                throw writeError
            }

            if (!insertResult || insertResult.affectedRows < 1) {
                return res.json({ ...BaseStdResponse.ERR, msg: '创建订单失败' })
            }

            await Redis.set(`payData:${orderId}`, JSON.stringify(payment.payData), { EX: 300 })

            try {
                await enqueueOrderPaymentCheck(orderId)
            } catch (error) {
                this.logger.error(`推送订单支付检查消息失败，订单号：${orderId}，错误：${error.stack || error}`)
            }

            return res.json({
                ...BaseStdResponse.OK,
                id: orderId,
                pay: payment
            })
        } catch (err) {
            this.logger.error(`创建订单失败：${err.stack || err}`)
            return res.json({ ...BaseStdResponse.ERR, msg: '创建订单异常，请联系管理员' })
        } finally {
            await releaseCouponUsageLock(couponLockKey)
        }
    }
}

module.exports.CreateOrder = CreateOrder
