const API = require("../../../lib/API.js");
const db = require("../../../plugin/DataBase/db.js");
const { BaseStdResponse } = require("../../../BaseStdResponse.js");
const AccessControl = require("../../../lib/AccessControl.js");
const { getJkesSettings } = require("../../../plugin/jkes/jkesSettings.js");
const { isJkesRunZone, normalizeJkesRunZone } = require("../../../plugin/jkes/runZones.js");
const {
    DEFAULT_AUTO_RUN_DISTANCE_KM,
    validateAutoRunPresetForSave
} = require("../../../plugin/jkes/autoRunAccountOptions.js");
const { resolveQQAvatarFromEmail } = require("../../../plugin/jkes/resolveQQAvatar.js");

function pickBodyOrDb(bodyVal, dbVal, fallback) {
    if (bodyVal !== undefined && bodyVal !== null && bodyVal !== "") return bodyVal;
    if (dbVal !== undefined && dbVal !== null && dbVal !== "") return dbVal;
    return fallback;
}

class AddAccount extends API {
    constructor() {
        super();

        this.setPath('/Lepao/Account')
        this.setMethod('POST')

        this.emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
        this.banEmailList = ['icloud.com']
    }

    // 生成 6 位数字 + 字母混合码
    async generateCode() {
        try {
            const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
            let code = ""
            for (let i = 0; i < 6; i++) {
                code += chars.charAt(Math.floor(Math.random() * chars.length))
            }

            let sql = 'SELECT id FROM lepao_extra WHERE bind_code = ?'
            let rows = await db.query(sql, [code])
            if (!rows)
                throw new Error('数据库错误，请稍后再试')
            if (rows.length > 0)
                return await this.generateCode()
            return code
        } catch (error) {
            throw error
        }
    }

    async onRequest(req, res) {
        let {
            uuid,
            session,
            student_num,
            email,
            id,
            area,
            auto_time,
            auto_run,
            target_count,
            auto_day,
            notice_type,
            notes,
            auto_run_distance_km,
            auto_run_distance_min_km,
            auto_run_distance_max_km,
            pace_min_sec_per_km,
            pace_max_sec_per_km
        } = req.body

        if ([uuid, session, student_num, auto_time, target_count, auto_day].some(value => value === '' || value === null || value === undefined))
            return res.json({
                ...BaseStdResponse.MISSING_PARAMETER
            })

        const targetKm = Number(target_count)
        if (!Number.isFinite(targetKm) || targetKm < 0 || targetKm > 200) {
            return res.json({
                ...BaseStdResponse.ERR,
                msg: '乐跑目标里程（公里）不在合法范围内（0–200）'
            })
        }

        if (area !== undefined && area !== null && area !== '' && !isJkesRunZone(area)) {
            return res.json({
                ...BaseStdResponse.ERR,
                msg: '跑区无效，请选择学府大道校区或南山校区'
            })
        }
        area = normalizeJkesRunZone(area)

        if (notice_type === 'email') {
            if (!this.emailRegex.test(email)) {
                return res.json({
                    ...BaseStdResponse.ERR,
                    msg: '请检查邮箱格式是否正确'
                })
            }

            const emailDomain = email.split('@')[1].toLowerCase()
            if (this.banEmailList.includes(emailDomain))
                return res.json({
                    ...BaseStdResponse.ERR,
                    msg: `暂不支持使用 ${emailDomain} 域名的邮箱，请更换其他邮箱后重试`
                })
        }

        if (auto_run === 1 && (!Array.isArray(auto_day) || !auto_day.every(v => Number.isInteger(v) && v >= 0 && v <= 6)))
            return res.json({
                ...BaseStdResponse.ERR,
                msg: '自动乐跑日期格式不合法'
            })

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

        let mergeForPreset = null
        if (id) {
            const ownerRows = await db.query(
                "SELECT auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km FROM lepao_account WHERE id = ? AND create_user = ?",
                [id, uuid]
            );
            if (!ownerRows || ownerRows.length === 0) {
                return res.json({ ...BaseStdResponse.ERR, msg: "未找到该乐跑账号或无权限编辑" });
            }
            mergeForPreset = ownerRows[0];
        }

        let countSql =
            "SELECT id, create_user, total_num, term_num, auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km FROM lepao_account WHERE student_num = ?";
        let countRows = await db.query(countSql, [student_num])

        if (!countRows)
            return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败！数据库错误' })

        if (!id && countRows.length > 0) {
            mergeForPreset = countRows[0];
        }

        const cfg = getJkesSettings();
        const paceDefLo = cfg.paceRandomMinSecPerKm ?? 180;
        const paceDefHi = cfg.paceRandomMaxSecPerKm ?? 600;
        const minMissing =
            auto_run_distance_min_km === undefined ||
            auto_run_distance_min_km === null ||
            auto_run_distance_min_km === ''
        const maxMissing =
            auto_run_distance_max_km === undefined ||
            auto_run_distance_max_km === null ||
            auto_run_distance_max_km === ''
        const legacyOnly =
            minMissing &&
            maxMissing &&
            auto_run_distance_km !== undefined &&
            auto_run_distance_km !== null &&
            auto_run_distance_km !== ''

        const presetBody = legacyOnly
            ? {
                  auto_run_distance_km,
                  pace_min_sec_per_km: pickBodyOrDb(
                      pace_min_sec_per_km,
                      mergeForPreset?.pace_min_sec_per_km,
                      paceDefLo
                  ),
                  pace_max_sec_per_km: pickBodyOrDb(
                      pace_max_sec_per_km,
                      mergeForPreset?.pace_max_sec_per_km,
                      paceDefHi
                  )
              }
            : {
                  auto_run_distance_min_km: pickBodyOrDb(
                      auto_run_distance_min_km,
                      mergeForPreset?.auto_run_distance_min_km,
                      DEFAULT_AUTO_RUN_DISTANCE_KM
                  ),
                  auto_run_distance_max_km: pickBodyOrDb(
                      auto_run_distance_max_km,
                      mergeForPreset?.auto_run_distance_max_km,
                      DEFAULT_AUTO_RUN_DISTANCE_KM
                  ),
                  pace_min_sec_per_km: pickBodyOrDb(
                      pace_min_sec_per_km,
                      mergeForPreset?.pace_min_sec_per_km,
                      paceDefLo
                  ),
                  pace_max_sec_per_km: pickBodyOrDb(
                      pace_max_sec_per_km,
                      mergeForPreset?.pace_max_sec_per_km,
                      paceDefHi
                  )
              }
        const preset = validateAutoRunPresetForSave(presetBody);
        if (!preset.ok) {
            return res.json({ ...BaseStdResponse.ERR, msg: preset.msg });
        }

        // 判断是否重复注册
        if (!id) {
            if (countRows.length !== 0 && countRows[0].create_user != null) {
                if (countRows[0].create_user !== uuid)
                    return res.json({ ...BaseStdResponse.ERR, msg: '该乐跑账号已被其他用户绑定，请联系客服解绑' })
                return res.json({ ...BaseStdResponse.ERR, msg: '该乐跑账号您已绑定' })
            }
        }

        if (countRows.length !== 0) {
            if (auto_run === 1 && countRows[0].term_num >= targetKm && targetKm !== 0)
                return res.json({
                    ...BaseStdResponse.ERR,
                    msg: '该账号本月跑步里程已达到或超过预设目标里程，请增大目标后再试'
                })
        }

        const time = new Date().getTime()
        let sql, r, user_avatar

        // 生成用户头像
        user_avatar = resolveQQAvatarFromEmail(email)
        if (!user_avatar) {
            let userSex = 2;
            const urows = await db.query("SELECT sex FROM users WHERE uuid = ? LIMIT 1", [uuid]);
            if (urows?.[0]?.sex === 1) userSex = 1;
            user_avatar =
                userSex === 1
                    ? "https://lepao-cloud.xxoo365.top/view.php/aee85ff43fd30d0df03c6a7dd9797d22.png"
                    : "https://lepao-cloud.xxoo365.top/view.php/fcb54dcc5e6209381e972ef73bdb4a93.png";
        }

        if (!id) {
            if (countRows.length !== 0) {
                sql = "UPDATE lepao_account SET create_user = ?, email = ?, user_avatar = ?, area = ?, auto_time = ?, auto_run = ?, target_count = ?, create_time = ?, update_time = ?, notes = ?, auto_day = ?, notice_type = ?, auto_run_distance_min_km = ?, auto_run_distance_max_km = ?, pace_min_sec_per_km = ?, pace_max_sec_per_km = ? WHERE id = ?";
                r = await db.query(sql, [
                    uuid,
                    email ?? "",
                    user_avatar ?? "",
                    area,
                    auto_time,
                    auto_run,
                    targetKm,
                    time,
                    time,
                    notes ?? "",
                    JSON.stringify(auto_day),
                    notice_type,
                    preset.autoRunDistanceMinKm,
                    preset.autoRunDistanceMaxKm,
                    preset.paceMinSecPerKm,
                    preset.paceMaxSecPerKm,
                    countRows[0].id
                ]);
            }
            else {
                const bind_code = await this.generateCode()

                sql = "INSERT INTO lepao_account (student_num, email, user_avatar, area, auto_time, auto_run, target_count, create_user, create_time, notes, auto_day, notice_type, auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
                r = await db.query(sql, [
                    student_num,
                    email ?? "",
                    user_avatar ?? "",
                    area,
                    auto_time,
                    auto_run,
                    targetKm,
                    uuid,
                    time,
                    notes ?? "",
                    JSON.stringify(auto_day),
                    notice_type,
                    preset.autoRunDistanceMinKm,
                    preset.autoRunDistanceMaxKm,
                    preset.paceMinSecPerKm,
                    preset.paceMaxSecPerKm
                ]);

                let faceSql = 'INSERT INTO lepao_extra (student_num, bind_code) VALUES (?, ?)'
                let faceRows = await db.query(faceSql, [student_num, bind_code])
                if (!faceRows || faceRows.affectedRows !== 1)
                    return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败！数据库错误' })
            }
        } else {
            sql = "UPDATE lepao_account SET student_num = ?, email = ?, user_avatar = ?, area = ?, auto_time = ?, target_count = ?, auto_run = ?, notes = ?, auto_day = ?, update_time = ?, notice_type = ?, auto_run_distance_min_km = ?, auto_run_distance_max_km = ?, pace_min_sec_per_km = ?, pace_max_sec_per_km = ? WHERE id = ? AND create_user = ?";
            r = await db.query(sql, [
                student_num,
                email ?? "",
                user_avatar ?? "",
                area,
                auto_time,
                targetKm,
                auto_run,
                notes ?? "",
                JSON.stringify(auto_day),
                time,
                notice_type,
                preset.autoRunDistanceMinKm,
                preset.autoRunDistanceMaxKm,
                preset.paceMinSecPerKm,
                preset.paceMaxSecPerKm,
                id,
                uuid
            ]);
        }

        try {
            if (r && r.affectedRows > 0) {
                const selectSql = `
                                    SELECT 
                                        a.id, a.create_user, a.total_num, e.bind_code, e.bot_account
                                    FROM
                                        lepao_account a
                                    LEFT JOIN  
                                        lepao_extra e
                                    ON
                                        a.student_num = e.student_num
                                    WHERE 
                                        a.student_num = ?
                                `
                const selectRows = await db.query(selectSql, [student_num])
                if (!selectRows)
                    return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败！数据库错误' })

                res.json({
                    ...BaseStdResponse.OK,
                    id: r.insertId,
                    data: {
                        student_num,
                        email,
                        id,
                        area,
                        auto_time,
                        auto_run,
                        target_count: targetKm,
                        auto_day,
                        notice_type,
                        notes,
                        auto_run_distance_min_km: preset.autoRunDistanceMinKm,
                        auto_run_distance_max_km: preset.autoRunDistanceMaxKm,
                        pace_min_sec_per_km: preset.paceMinSecPerKm,
                        pace_max_sec_per_km: preset.paceMaxSecPerKm,
                        bind_code: selectRows.length !== 0 ? selectRows[0].bind_code : undefined,
                        bot_account: selectRows.length !== 0 ? selectRows[0].bot_account : undefined
                    }
                })
            } else {
                return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败！数据库错误' })
            }
        } catch (err) {
            this.logger.error(`添加乐跑账号失败！${err.stack}`)
            res.json({
                ...BaseStdResponse.ERR,
                msg: "添加乐跑账号失败！",
            });
        }
    }
}

module.exports.AddAccount = AddAccount;