<?php

namespace Kylin\Payment\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Kylin\Payment\Facades\Payment;
use Illuminate\Contracts\Container\Container;

/**
 * 支付预创建服务类
 */
class PaymentPreCreateService
{
    /**
     * 默认预创建配置
     */
    const DEFAULT_PRE_CREATE_CONFIG = [
        'expire_minutes' => 15, // 预创建订单过期时间（分钟）
        'cache_prefix' => 'payment_pre_create:', // 缓存前缀
    ];

    /**
     * 构造函数
     */
    public function __construct(
        protected readonly Container $app
    ) {
    }

    /**
     * 预创建支付订单
     */
    public function preCreate(string $paymentType, array $params, array $config = []): array
    {
        // 合并默认配置
        $config = array_merge(self::DEFAULT_PRE_CREATE_CONFIG, $config);

        // 生成预创建订单号
        $preCreateNo = $this->generatePreCreateNo();

        // 构建预创建数据
        $preCreateData = [
            'pre_create_no' => $preCreateNo,
            'payment_type' => $paymentType,
            'params' => $params,
            'expired_at' => now()->addMinutes($config['expire_minutes']),
            'created_at' => now(),
        ];

        // 存储预创建订单
        $preCreateId = DB::table('payment_pre_creates')->insertGetId($preCreateData);

        // 缓存预创建订单信息
        $cacheKey = "{$config['cache_prefix']}{$preCreateNo}";
        Cache::put($cacheKey, $preCreateData, $config['expire_minutes']);

        // 调用支付网关预创建接口
        try {
            $gateway = Payment::manager()->gateway($paymentType);
            $result = $gateway->preCreate($params);

            // 更新预创建订单状态
            DB::table('payment_pre_creates')
                ->where('id', $preCreateId)
                ->update([
                    'status' => 'success',
                    'result' => $result,
                    'updated_at' => now(),
                ]);

            // 更新缓存
            $preCreateData['status'] = 'success';
            $preCreateData['result'] = $result;
            Cache::put($cacheKey, $preCreateData, $config['expire_minutes']);

            return [
                'pre_create_no' => $preCreateNo,
                'result' => $result,
                'expired_at' => $preCreateData['expired_at'],
            ];
        } catch (\Exception $e) {
            // 更新预创建订单状态
            DB::table('payment_pre_creates')
                ->where('id', $preCreateId)
                ->update([
                    'status' => 'failed',
                    'error' => $e->getMessage(),
                    'updated_at' => now(),
                ]);

            // 更新缓存
            $preCreateData['status'] = 'failed';
            $preCreateData['error'] = $e->getMessage();
            Cache::put($cacheKey, $preCreateData, $config['expire_minutes']);

            throw $e;
        }
    }

    /**
     * 获取预创建订单信息
     */
    public function getPreCreateInfo(string $preCreateNo, array $config = []): ?array
    {
        // 合并默认配置
        $config = array_merge(self::DEFAULT_PRE_CREATE_CONFIG, $config);

        // 尝试从缓存获取
        $cacheKey = "{$config['cache_prefix']}{$preCreateNo}";
        $preCreateData = Cache::get($cacheKey);

        if ($preCreateData) {
            return $preCreateData;
        }

        // 从数据库获取
        $preCreate = DB::table('payment_pre_creates')
            ->where('pre_create_no', $preCreateNo)
            ->first();

        if (!$preCreate) {
            return null;
        }

        // 转换为数组
        $preCreateData = (array) $preCreate;

        // 检查是否过期
        if (now() > $preCreateData['expired_at']) {
            $preCreateData['status'] = 'expired';
        }

        // 缓存结果
        Cache::put($cacheKey, $preCreateData, $config['expire_minutes']);

        return $preCreateData;
    }

    /**
     * 验证预创建订单状态
     */
    public function validatePreCreate(string $preCreateNo): bool
    {
        $preCreateData = $this->getPreCreateInfo($preCreateNo);

        if (!$preCreateData) {
            return false;
        }

        // 检查是否过期
        if (now() > $preCreateData['expired_at']) {
            return false;
        }

        // 检查状态
        if ($preCreateData['status'] !== 'success') {
            return false;
        }

        return true;
    }

    /**
     * 使用预创建订单进行支付
     */
    public function payWithPreCreate(string $preCreateNo, array $params = []): mixed
    {
        // 获取预创建订单信息
        $preCreateData = $this->getPreCreateInfo($preCreateNo);

        if (!$preCreateData) {
            throw new \Exception('Pre-create order not found');
        }

        // 验证预创建订单
        if (!$this->validatePreCreate($preCreateNo)) {
            throw new \Exception('Pre-create order is invalid or expired');
        }

        // 合并参数
        $paymentParams = array_merge($preCreateData['params'], $params);

        // 执行支付
        return Payment::pay($preCreateData['payment_type'], $paymentParams);
    }

    /**
     * 处理过期预创建订单
     */
    public function processExpiredPreCreates(int $batchSize = 100): int
    {
        // 获取过期预创建订单
        $expiredPreCreates = DB::table('payment_pre_creates')
            ->where('status', 'success')
            ->where('expired_at', '<=', now())
            ->limit($batchSize)
            ->get();

        $count = 0;

        foreach ($expiredPreCreates as $preCreate) {
            try {
                // 更新过期状态
                DB::table('payment_pre_creates')
                    ->where('id', $preCreate->id)
                    ->update([
                        'status' => 'expired',
                        'updated_at' => now(),
                    ]);

                // 清除缓存
                Cache::forget(self::DEFAULT_PRE_CREATE_CONFIG['cache_prefix'] . $preCreate->pre_create_no);

                $count++;
            } catch (\Exception $e) {
                // 记录错误日志
                Payment::log('pre_create', 'expire_failed', ['pre_create_id' => $preCreate->id], $e->getMessage());
            }
        }

        return $count;
    }

    /**
     * 生成预创建订单号
     */
    protected function generatePreCreateNo(): string
    {
        return 'PRE' . date('YmdHis') . substr(implode('', array_map(fn ($v) => dechex($v), str_split(uniqid(), 4))), 0, 8);
    }

    /**
     * 取消预创建订单
     */
    public function cancelPreCreate(string $preCreateNo): bool
    {
        // 获取预创建订单
        $preCreate = DB::table('payment_pre_creates')
            ->where('pre_create_no', $preCreateNo)
            ->first();

        if (!$preCreate) {
            throw new \Exception('Pre-create order not found');
        }

        // 检查状态
        if ($preCreate->status !== 'success') {
            throw new \Exception('Pre-create order status cannot be cancelled');
        }

        // 更新状态
        DB::table('payment_pre_creates')
            ->where('pre_create_no', $preCreateNo)
            ->update([
                'status' => 'cancelled',
                'updated_at' => now(),
            ]);

        // 清除缓存
        Cache::forget(self::DEFAULT_PRE_CREATE_CONFIG['cache_prefix'] . $preCreateNo);

        return true;
    }

    /**
     * 获取预创建订单统计信息
     */
    public function getPreCreateStats(int $days = 7): array
    {
        $startDate = now()->subDays($days);

        return [
            'total' => DB::table('payment_pre_creates')
                ->where('created_at', '>=', $startDate)
                ->count(),
            'success' => DB::table('payment_pre_creates')
                ->where('created_at', '>=', $startDate)
                ->where('status', 'success')
                ->count(),
            'failed' => DB::table('payment_pre_creates')
                ->where('created_at', '>=', $startDate)
                ->where('status', 'failed')
                ->count(),
            'expired' => DB::table('payment_pre_creates')
                ->where('created_at', '>=', $startDate)
                ->where('status', 'expired')
                ->count(),
            'cancelled' => DB::table('payment_pre_creates')
                ->where('created_at', '>=', $startDate)
                ->where('status', 'cancelled')
                ->count(),
            'by_payment_type' => DB::table('payment_pre_creates')
                ->select(
                    'payment_type',
                    DB::raw('COUNT(*) as count')
                )
                ->where('created_at', '>=', $startDate)
                ->groupBy('payment_type')
                ->orderBy('count', 'desc')
                ->get()
                ->toArray(),
        ];
    }
}
