<?php



declare(strict_types=1);



namespace app\common\service;



use support\think\Db;



/**

 * 充值待支付订单超时处理：

 * - 同用户最多允许 3 笔待支付订单

 * - 待支付订单创建后超过有效期未支付，自动标记为失败

 * - 有效秒数由 .env DEPOSIT_PENDING_EXPIRE_SECONDS 配置（默认 60），全渠道统一

 */

final class DepositOrderExpireService

{

    public const MAX_PENDING_DEPOSIT = 3;



    /**

     * 待支付充值单有效秒数（与 config app.deposit_pending_expire_seconds / .env 一致）

     */

    public static function pendingExpireSeconds(): int

    {

        $cfg = config('app.deposit_pending_expire_seconds', 60);

        if (is_numeric($cfg)) {

            $v = intval($cfg);

            if ($v > 0) {

                return $v;

            }

        }



        return 60;

    }



    /**

     * @param array<string, mixed> $order

     */

    public static function expireSecondsForOrder(array $order): int

    {

        return self::pendingExpireSeconds();

    }



    /**

     * @param int|null    $userId  仅处理某用户；null 表示不过滤用户

     * @param string|null $orderNo 仅处理某订单；null 表示不过滤订单

     *

     * @return int 本次转失败的订单数

     */

    public static function expirePendingOrders(?int $userId = null, ?string $orderNo = null): int

    {

        $query = Db::name('deposit_order')->where('status', 0);

        if ($userId !== null && $userId > 0) {

            $query->where('user_id', $userId);

        }

        if ($orderNo !== null && $orderNo !== '') {

            $query->where('order_no', $orderNo);

        }

        $rows = $query->field(['id', 'remark', 'pay_channel', 'create_time'])->select()->toArray();

        if ($rows === []) {

            return 0;

        }

        $now = time();

        $expireSec = self::pendingExpireSeconds();

        $affectedCount = 0;

        foreach ($rows as $row) {

            if (!is_array($row)) {

                continue;

            }

            $id = isset($row['id']) && is_numeric($row['id']) ? intval($row['id']) : 0;

            if ($id <= 0) {

                continue;

            }

            $createTime = isset($row['create_time']) && is_numeric($row['create_time']) ? intval($row['create_time']) : 0;

            if ($createTime > 0 && $createTime + $expireSec > $now) {

                continue;

            }

            $oldRemark = isset($row['remark']) && is_string($row['remark']) ? trim($row['remark']) : '';

            $reason = '[timeout] unpaid over ' . $expireSec . 's';

            $remark = $oldRemark === '' ? $reason : mb_substr($oldRemark . ' | ' . $reason, 0, 255);

            $affected = Db::name('deposit_order')

                ->where('id', $id)

                ->where('status', 0)

                ->update([

                    'status'      => 2,

                    'remark'      => $remark,

                    'update_time' => $now,

                ]);

            if (is_numeric($affected) && intval($affected) > 0) {

                $affectedCount++;

            }

        }



        return $affectedCount;

    }



    public static function pendingCountByUserId(int $userId): int

    {

        if ($userId <= 0) {

            return 0;

        }



        return Db::name('deposit_order')

            ->where('user_id', $userId)

            ->where('status', 0)

            ->count();

    }



    /**

     * 订单是否仍在待支付有效期内

     *

     * @param array<string, mixed> $order

     */

    public static function isPendingPaymentValid(array $order): bool

    {

        $status = isset($order['status']) && is_numeric($order['status']) ? intval($order['status']) : -1;

        if ($status !== 0) {

            return false;

        }

        $createTime = isset($order['create_time']) && is_numeric($order['create_time']) ? intval($order['create_time']) : 0;

        if ($createTime <= 0) {

            return false;

        }



        return $createTime + self::pendingExpireSeconds() > time();

    }

}


