import crypto from 'node:crypto';

export function loadRechargeConfig() {
  const tiers = (process.env.H5_RECHARGE_TIERS_CENTS ?? '500,1000,3000,5000,10000,20000')
    .split(',')
    .map((value) => Number(value.trim()))
    .filter((value) => Number.isFinite(value) && value > 0);
  return {
    tiersCents: tiers.length ? tiers : [500, 1000, 3000, 5000, 10000, 20000],
    minRechargeCents: Number(process.env.H5_MIN_RECHARGE_CENTS ?? 500),
    orderTtlMs: Number(process.env.H5_RECHARGE_ORDER_TTL_MS ?? 15 * 60 * 1000),
    maxPendingOrders: Number(process.env.H5_RECHARGE_MAX_PENDING ?? 3),
    dailyLimitCents: Number(process.env.H5_RECHARGE_DAILY_LIMIT_CENTS ?? 200_000),
  };
}

export function isAllowedRechargeAmount(amountCents, config = loadRechargeConfig()) {
  return config.tiersCents.includes(Number(amountCents));
}

export function buildInsufficientBalancePayload(balanceCents, config = loadRechargeConfig()) {
  return {
    code: 'INSUFFICIENT_BALANCE',
    message: '余额不足，请充值后继续使用',
    balanceCents: Number(balanceCents ?? 0),
    minRechargeCents: config.minRechargeCents,
    suggestedTiers: config.tiersCents,
  };
}

function createOutTradeNo() {
  const stamp = Date.now().toString(36).toUpperCase();
  const rand = crypto.randomBytes(4).toString('hex').toUpperCase();
  return `TK${stamp}${rand}`.slice(0, 32);
}

function mapOrderRow(row) {
  if (!row) return null;
  return {
    id: row.id,
    userId: row.user_id,
    amountCents: Number(row.amount_cents),
    channel: row.channel,
    status: row.status,
    payMode: row.pay_mode,
    outTradeNo: row.out_trade_no,
    providerTxn: row.provider_txn,
    paidAt: row.paid_at == null ? null : Number(row.paid_at),
    expireAt: Number(row.expire_at),
    createdAt: Number(row.created_at),
    codeUrl: row.code_url ?? null,
    h5Url: row.h5_url ?? null,
  };
}

export function createRechargeService(pool, { userAuth, wechatPay, config = loadRechargeConfig() } = {}) {
  const expireStaleOrders = async (userId) => {
    const now = Date.now();
    await pool.query(
      `UPDATE h5_payment_orders
       SET status = 'expired', updated_at = ?
       WHERE user_id = ? AND status = 'pending' AND expire_at <= ?`,
      [now, userId, now],
    );
  };

  const countPendingOrders = async (userId) => {
    const now = Date.now();
    const [rows] = await pool.query(
      `SELECT COUNT(*) AS total
       FROM h5_payment_orders
       WHERE user_id = ? AND status = 'pending' AND expire_at > ?`,
      [userId, now],
    );
    return Number(rows[0]?.total ?? 0);
  };

  const sumPaidToday = async (userId) => {
    const start = new Date();
    start.setHours(0, 0, 0, 0);
    const [rows] = await pool.query(
      `SELECT COALESCE(SUM(amount_cents), 0) AS total
       FROM h5_payment_orders
       WHERE user_id = ? AND status = 'paid' AND paid_at >= ?`,
      [userId, start.getTime()],
    );
    return Number(rows[0]?.total ?? 0);
  };

  const getOrderById = async (orderId) => {
    const [rows] = await pool.query(`SELECT * FROM h5_payment_orders WHERE id = ? LIMIT 1`, [
      orderId,
    ]);
    return mapOrderRow(rows[0]);
  };

  const getOrderByOutTradeNo = async (outTradeNo) => {
    const [rows] = await pool.query(
      `SELECT * FROM h5_payment_orders WHERE out_trade_no = ? LIMIT 1`,
      [outTradeNo],
    );
    return mapOrderRow(rows[0]);
  };

  const getBillingConfig = async (userId) => {
    const user = await userAuth.getUserById(userId);
    return {
      wechatEnabled: Boolean(wechatPay?.enabled),
      tiersCents: config.tiersCents,
      minRechargeCents: config.minRechargeCents,
      balanceCents: user ? Number(user.balance_cents ?? 0) : 0,
    };
  };

  const createOrder = async ({ userId, amountCents, payScene, clientIp }) => {
    const amount = Number(amountCents);
    if (!isAllowedRechargeAmount(amount, config)) {
      return { ok: false, message: '充值金额不在允许档位内' };
    }
    if (!wechatPay?.enabled) {
      return { ok: false, message: '微信支付尚未配置，请联系管理员' };
    }

    const user = await userAuth.getUserById(userId);
    if (!user) return { ok: false, message: '用户不存在' };
    if (user.status === 'disabled') return { ok: false, message: '账户已禁用' };

    await expireStaleOrders(userId);
    const pending = await countPendingOrders(userId);
    if (pending >= config.maxPendingOrders) {
      return { ok: false, message: '待支付订单过多，请先完成或等待过期后再试' };
    }

    const paidToday = await sumPaidToday(userId);
    if (paidToday + amount > config.dailyLimitCents) {
      return { ok: false, message: '已超过今日充值上限，请明日再试' };
    }

    const now = Date.now();
    const orderId = crypto.randomUUID();
    const outTradeNo = createOutTradeNo();
    const expireAt = now + config.orderTtlMs;
    const description = `TKMind账户充值¥${(amount / 100).toFixed(2)}`;
    const mode =
      payScene === 'jsapi' ? 'jsapi' : payScene === 'h5' ? 'h5' : 'native';

    let codeUrl = null;
    let h5Url = null;
    let jsapiParams = null;
    try {
      if (mode === 'jsapi') {
        const appId = wechatPay.appId;
        if (!appId) {
          return { ok: false, message: '微信支付 AppID 未配置' };
        }
        const openid = await userAuth.getWechatOpenidForUser(userId, appId);
        if (!openid) {
          return {
            ok: false,
            message: '请先用微信登录并绑定账号后再充值',
          };
        }
        const result = await wechatPay.createJsapiOrder({
          outTradeNo,
          description,
          amountCents: amount,
          clientIp,
          openid,
        });
        jsapiParams = result.jsapiParams;
      } else if (mode === 'h5') {
        const result = await wechatPay.createH5Order({
          outTradeNo,
          description,
          amountCents: amount,
          clientIp,
        });
        h5Url = result.h5Url;
      } else {
        const result = await wechatPay.createNativeOrder({
          outTradeNo,
          description,
          amountCents: amount,
          clientIp,
        });
        codeUrl = result.codeUrl;
      }
    } catch (err) {
      return {
        ok: false,
        message: err instanceof Error ? err.message : '创建支付订单失败',
      };
    }

    await pool.query(
      `INSERT INTO h5_payment_orders
       (id, user_id, amount_cents, channel, status, pay_mode, out_trade_no,
        code_url, h5_url, expire_at, client_ip, created_at, updated_at)
       VALUES (?, ?, ?, 'wechat', 'pending', ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        orderId,
        userId,
        amount,
        mode,
        outTradeNo,
        codeUrl,
        h5Url,
        expireAt,
        clientIp ?? null,
        now,
        now,
      ],
    );

    return {
      ok: true,
      order: {
        id: orderId,
        amountCents: amount,
        status: 'pending',
        payMode: mode,
        expireAt,
        codeUrl,
        h5Url,
        jsapiParams,
      },
    };
  };

  const fulfillOrder = async (order, transaction) => {
    if (!order || order.status === 'paid') {
      return { ok: true, alreadyPaid: true };
    }
    if (order.status !== 'pending') {
      return { ok: false, message: '订单状态不可支付' };
    }

    const tradeState =
      transaction?.trade_state ??
      (transaction?.result_code === 'SUCCESS' ? 'SUCCESS' : transaction?.result_code) ??
      transaction?.trade_state_desc;
    if (tradeState && tradeState !== 'SUCCESS') {
      return { ok: false, message: `支付未成功: ${tradeState}` };
    }

    const paidAmount = Number(
      transaction?.amount?.total ??
        transaction?.amount?.payer_total ??
        transaction?.total_fee ??
        0,
    );
    if (paidAmount !== order.amountCents) {
      return { ok: false, message: '支付金额与订单不一致' };
    }

    const now = Date.now();
    const conn = await pool.getConnection();
    try {
      await conn.beginTransaction();
      const [rows] = await conn.query(
        `SELECT * FROM h5_payment_orders WHERE id = ? FOR UPDATE`,
        [order.id],
      );
      const locked = mapOrderRow(rows[0]);
      if (!locked) {
        await conn.rollback();
        return { ok: false, message: '订单不存在' };
      }
      if (locked.status === 'paid') {
        await conn.commit();
        return { ok: true, alreadyPaid: true };
      }
      if (locked.status !== 'pending') {
        await conn.rollback();
        return { ok: false, message: '订单状态不可支付' };
      }

      const recharge = await userAuth.recharge(locked.userId, locked.amountCents, null, '用户自助充值', {
        paymentOrderId: locked.id,
        conn,
      });
      if (!recharge.ok) {
        await conn.rollback();
        return recharge;
      }

      await conn.query(
        `UPDATE h5_payment_orders
         SET status = 'paid', provider_txn = ?, paid_at = ?, updated_at = ?
         WHERE id = ?`,
        [transaction.transaction_id ?? transaction.out_trade_no ?? null, now, now, locked.id],
      );
      await conn.commit();
      return { ok: true, user: recharge.user, balanceCents: recharge.user.balanceCents };
    } catch (err) {
      await conn.rollback();
      throw err;
    } finally {
      conn.release();
    }
  };

  const handleWechatNotify = async ({ headers, body }) => {
    const { transaction } = wechatPay.verifyNotify({ headers, body });
    const outTradeNo = transaction?.out_trade_no;
    if (!outTradeNo) {
      throw new Error('回调缺少商户订单号');
    }
    const order = await getOrderByOutTradeNo(outTradeNo);
    if (!order) {
      throw new Error('订单不存在');
    }
    return fulfillOrder(order, transaction);
  };

  const getOrderForUser = async (userId, orderId) => {
    const order = await getOrderById(orderId);
    if (!order || order.userId !== userId) return null;
    if (order.status === 'pending' && order.expireAt <= Date.now()) {
      await pool.query(
        `UPDATE h5_payment_orders SET status = 'expired', updated_at = ? WHERE id = ? AND status = 'pending'`,
        [Date.now(), orderId],
      );
      return { ...order, status: 'expired' };
    }
    return order;
  };

  return {
    config,
    getBillingConfig,
    createOrder,
    getOrderForUser,
    handleWechatNotify,
    fulfillOrder,
    getOrderByOutTradeNo,
  };
}
