<?php

declare(strict_types=1);

namespace Kylin\Pay;

use Illuminate\Support\Facades\Log;
use Kylin\Pay\Artful\Event\ArtfulEnd;
use Kylin\Pay\Artful\Event\ArtfulStart;
use Kylin\Pay\Artful\Event\HttpEnd;
use Kylin\Pay\Artful\Event\HttpStart;
use Kylin\Pay\Event\PayEnd;
use Kylin\Pay\Event\PayStart;
use Throwable;

/**
 * 支付事件监听器
 *
 * 监听 Artful 框架的底层事件，转发为 Kylin Pay 业务事件，
 * 并记录结构化日志便于审计追溯。
 *
 * 法律合规：
 *  - 《非银行支付机构网络支付业务管理办法》第二十条：交易信息真实性、完整性、可追溯性
 *  - 《网络安全法》第二十一条：网络运行状态、网络安全事件记录
 *  - 银发〔2016〕261号第七条：加强交易监测
 *
 * 注意：底层 SDK 仅记录结构化日志，业务级审计日志（含用户、IP 等）
 * 应由上层 kylin/payment 包通过监听 PayStart/PayEnd 事件实现。
 */
class PayListener
{
    public static function artfulStart(ArtfulStart $event): void
    {
        Event::dispatch(new PayStart($event->plugins, $event->params));

        // 记录支付发起日志（银发〔2016〕261号第七条：交易监测）
        try {
            $params = $event->params;
            $outTradeNo = $params['out_trade_no']
                ?? $params['outTradeNo']
                ?? ($params['order']['out_trade_no'] ?? null);

            Log::channel(config('kylin-pay.log_channel', 'default'))->info('支付请求发起', [
                'out_trade_no' => $outTradeNo,
                'plugins' => self::simplifyPlugins($event->plugins),
                'tag' => 'pay',
            ]);
        } catch (Throwable $e) {
            // 日志记录失败不应影响支付流程
        }
    }

    public static function artfulEnd(ArtfulEnd $event): void
    {
        Event::dispatch(new PayEnd($event->rocket));

        // 记录支付完成日志
        try {
            $rocket = $event->rocket;
            $destination = $rocket->getDestination();

            // destination 可能是 Collection、ResponseInterface 或数组
            if ($destination instanceof \Yansongda\Supports\Collection) {
                $destArray = $destination->all();
            } elseif (is_array($destination)) {
                $destArray = $destination;
            } else {
                $destArray = [];
            }

            $outTradeNo = $destArray['out_trade_no']
                ?? $destArray['outTradeNo']
                ?? null;
            $tradeNo = $destArray['trade_no']
                ?? $destArray['transaction_id']
                ?? null;
            $tradeState = $destArray['trade_state']
                ?? $destArray['trade_status']
                ?? null;

            Log::channel(config('kylin-pay.log_channel', 'default'))->info('支付请求完成', [
                'out_trade_no' => $outTradeNo,
                'trade_no' => $tradeNo,
                'trade_state' => $tradeState,
                'tag' => 'pay',
            ]);
        } catch (Throwable $e) {
            // 日志记录失败不应影响支付流程
        }
    }

    public static function httpStart(HttpStart $event): void
    {
        Event::dispatch(new Event\HttpStart($event->rocket));

        // 记录 HTTP 请求发起日志（不含敏感数据）
        try {
            $rocket = $event->rocket;
            $radar = $rocket->getRadar();
            $uri = $radar?->getUri();
            $url = $uri?->__toString() ?? '';
            $method = $radar?->getMethod() ?? '';

            Log::channel(config('kylin-pay.log_channel', 'default'))->debug('支付 HTTP 请求', [
                'method' => $method,
                'url' => self::sanitizeUrl($url),
                'tag' => 'pay',
            ]);
        } catch (Throwable $e) {
            // 日志记录失败不应影响支付流程
        }
    }

    public static function httpEnd(HttpEnd $event): void
    {
        Event::dispatch(new Event\HttpEnd($event->rocket));

        // 记录 HTTP 响应日志
        try {
            $rocket = $event->rocket;
            $destination = $rocket->getDestination();
            $statusCode = null;

            if ($destination instanceof \Psr\Http\Message\ResponseInterface) {
                $statusCode = $destination->getStatusCode();
            } elseif ($destination instanceof \Yansongda\Supports\Collection) {
                $statusCode = $destination->get('code');
            }

            Log::channel(config('kylin-pay.log_channel', 'default'))->debug('支付 HTTP 响应', [
                'status_code' => $statusCode,
                'tag' => 'pay',
            ]);
        } catch (Throwable $e) {
            // 日志记录失败不应影响支付流程
        }
    }

    /**
     * 简化插件列表用于日志记录（避免记录过多内部细节）
     *
     * @param array<int, mixed> $plugins
     * @return array<int, string>
     */
    private static function simplifyPlugins(array $plugins): array
    {
        $result = [];
        foreach ($plugins as $plugin) {
            if (is_object($plugin)) {
                $result[] = get_class($plugin);
            } elseif (is_string($plugin)) {
                $result[] = $plugin;
            }
        }
        return $result;
    }

    /**
     * 脱敏 URL（移除查询参数中的敏感信息）
     */
    private static function sanitizeUrl(string $url): string
    {
        // 移除查询参数，仅保留路径
        $parts = parse_url($url);
        if ($parts === false) {
            return $url;
        }

        $scheme = $parts['scheme'] ?? 'https';
        $host = $parts['host'] ?? '';
        $path = $parts['path'] ?? '';

        return "{$scheme}://{$host}{$path}";
    }
}
