<?php

declare(strict_types=1);


namespace Plugins\Neatstudio\Payment\Services;

use App\Models\Setting;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Plugins\Neatstudio\Payment\Models\Payment;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Provider\Alipay;
use Yansongda\Pay\Provider\Wechat;

class PaymentManager
{
    /**
     * 创建订单。$payable 为可购买实体（文章/套餐/皮肤），支付成功后经 payment.paid 钩子分发。
     */
    public function createOrder(User $user, string $subject, int $amountCents, ?Model $payable = null, string $channel = 'alipay', string $description = null): Payment
    {
        $orderNo = date('YmdHis').Str::random(8);

        return Payment::create([
            'order_no' => $orderNo,
            'user_id' => $user->id,
            'subject' => $subject,
            'description' => $description,
            'amount' => $amountCents,
            'channel' => $channel,
            'status' => 'pending',
            'payable_type' => $payable?->getMorphClass(),
            'payable_id' => $payable?->getKey(),
        ]);
    }

    /**
     * 发起支付，返回跳转地址（沙箱模式返回模拟支付页）。
     */
    public function pay(Payment $payment): string
    {
        $this->assertPending($payment);

        if ($this->sandboxEnabled()) {
            return route('pay.sandbox', $payment->order_no);
        }

        try {
            return match ($payment->channel) {
                'alipay' => $this->alipay()->wap([
                    'out_trade_no' => $payment->order_no,
                    'subject' => $payment->subject,
                    'total_amount' => number_format($payment->amount / 100, 2),
                ])->toArray()['h5_url'] ?? throw new \RuntimeException('支付宝未返回支付链接'),
                'wechat' => $this->wechat()->wap([
                    'out_trade_no' => $payment->order_no,
                    'description' => $payment->subject,
                    'amount' => ['total' => $payment->amount],
                ])->toArray()['h5_url'] ?? throw new \RuntimeException('微信未返回支付链接'),
                default => throw new \InvalidArgumentException('未知支付渠道'),
            };
        } catch (\Throwable $e) {
            if (str_contains($e->getMessage(), '未返回支付链接')) {
                throw $e;
            }

            throw new \RuntimeException('支付网关调用失败，请检查渠道配置或开启沙箱模式：'.$e->getMessage());
        }
    }

    /**
     * 沙箱模拟支付：直接标记订单已支付。
     */
    public function simulatePay(Payment $payment): void
    {
        $this->assertPending($payment);
        $payment->markPaid('sandbox_'.Str::random(12));
    }

    public function handleNotify(string $channel, array $params): string
    {
        if ($this->sandboxEnabled()) {
            return 'fail';
        }

        try {
            $result = match ($channel) {
                'alipay' => $this->alipay()->callback($params),
                'wechat' => $this->wechat()->callback($params),
                default => throw new \InvalidArgumentException('未知回调渠道'),
            };

            $orderNo = $result['out_trade_no'] ?? null;
            $tradeNo = $result['trade_no'] ?? null;

            if ($orderNo) {
                $payment = Payment::where('order_no', $orderNo)->first();
                $payment?->markPaid($tradeNo);
            }

            return 'success';
        } catch (\Throwable) {
            return 'fail';
        }
    }

    public function sandboxEnabled(): bool
    {
        return (int) Setting::get('pay_sandbox', 1) === 1;
    }

    public function alipay(): Alipay
    {
        $mode = Setting::get('pay_alipay_mode', 'normal') === 'sandbox' ? Pay::MODE_SANDBOX : Pay::MODE_NORMAL;

        return Pay::alipay([
            '_force' => true,
            'alipay' => [
                'default' => [
                    'mode' => $mode,
                    'notify_url' => route('pay.notify', ['channel' => 'alipay']),
                    'return_url' => route('pay.return', ['channel' => 'alipay']),
                    'app_id' => Setting::get('pay_alipay_app_id', ''),
                    'app_secret_cert' => Setting::get('pay_alipay_app_secret', ''),
                    'app_public_cert_path' => null,
                    'alipay_public_cert_path' => Setting::get('pay_alipay_public_key', ''),
                    'alipay_root_cert_path' => null,
                ],
            ],
        ]);
    }

    public function wechat(): Wechat
    {
        $mode = Setting::get('pay_wechat_mode', 'normal') === 'sandbox' ? Pay::MODE_SANDBOX : Pay::MODE_NORMAL;

        return Pay::wechat([
            '_force' => true,
            'wechat' => [
                'default' => [
                    'mode' => $mode,
                    'notify_url' => route('pay.notify', ['channel' => 'wechat']),
                    'return_url' => route('pay.return', ['channel' => 'wechat']),
                    'mch_id' => Setting::get('pay_wechat_mch_id', ''),
                    'mch_secret_key' => Setting::get('pay_wechat_mch_secret', ''),
                    'mch_secret_cert' => Setting::get('pay_wechat_mch_cert', ''),
                    'mch_public_cert_path' => Setting::get('pay_wechat_platform_cert', ''),
                    'mp_app_id' => Setting::get('pay_wechat_app_id', ''),
                ],
            ],
        ]);
    }

    private function assertPending(Payment $payment): void
    {
        if ($payment->status !== 'pending') {
            throw new \RuntimeException('订单状态不是待支付');
        }
    }
}
