<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service\domain\pay;

use Core\service\BaseCoreService;
use app\service\domain\pay\gateway\BalanceGateway;
use app\service\domain\pay\gateway\OfflineGateway;
use app\service\domain\pay\gateway\PaymentGatewayInterface;
use app\service\domain\pay\gateway\WechatGateway;
use app\service\domain\pay\gateway\AlipayGateway;
use think\App;
use think\Request;


/**
 * 支付协调器
 * @package app\service\domain\pay
 */
class CorePayOrchestrator extends BaseCoreService
{

    private array $gateways = [];

    private CorePayConfigService $payConfig;

    /**
     * 构造函数
     * 注册默认支付网关
     * @param App $app
     * @param CorePayConfigService $payConfig
     */
    public function __construct(App $app, CorePayConfigService $payConfig)
    {
        parent::__construct($app);
        $this->payConfig = $payConfig;
        $this->registerDefaultGateways();
    }

    /**
     * 注册默认支付网关
     */
    private function registerDefaultGateways(): void
    {
        $this->registerAll([
            new BalanceGateway(),
            new OfflineGateway($this->payConfig),
            new WechatGateway($this->payConfig),
            new AlipayGateway($this->payConfig),
        ]);
    }

    /**
     * 注册支付网关
     * @param PaymentGatewayInterface $gateway
     * @return self
     */
    public function register(PaymentGatewayInterface $gateway): self
    {
        $this->gateways[$gateway->provider()] = $gateway;
        return $this;
    }


    /**
     * 注册多个支付网关
     * @param array $gateways
     * @return self
     */
    public function registerAll(array $gateways): self
    {
        foreach ($gateways as $gateway) {
            $this->register($gateway);
        }
        return $this;
    }

    /**
     * 获取支付网关
     * @param string $provider
     * @return PaymentGatewayInterface|null 支付网关
     * @param string $provider 支付网关提供者
     */
    public function gateway(string $provider): ?PaymentGatewayInterface
    {
        return $this->gateways[$provider] ?? null;
    }


    /**
     * 获取所有支付网关
     * @return array 支付网关数组
     */
    public function all(): array
    {
        return $this->gateways;
    }


    /**
     * 判断支付网关是否可分配
     * @param string $provider
     * @return bool 是否可分配
     * @param string $provider 支付网关提供者
     */
    public function isAllocatable(string $provider): bool
    {
        $gateway = $this->gateway($provider);
        return $gateway && $gateway->isAllocatable();
    }


    /**
     * 获取可用的支付网关
     * @param string $channel 渠道
     * @param string $terminal 终端
     * @return array 可用的支付网关数组
     */

    public function getAvailableProviders(string $channel, string $terminal): array
    {
        if (!$this->payConfig->isChannelEnabled($channel)) {
            return [];
        }

        $allocatedProviders = $this->loadAllocatedProviders($channel);
        $items = [];

        foreach ($this->gateways as $provider => $gateway) {
            if (!$gateway->supportsTerminal($terminal)) {
                continue;
            }

            if (!isset($allocatedProviders[$provider])) {
                continue;
            }

            $items[] = $this->providerItem($provider, $terminal, $allocatedProviders[$provider]);
        }

        usort($items, static function (array $a, array $b): int {
            return ((int)$a['sort']) <=> ((int)$b['sort']);
        });

        return $items;
    }


    /**
     * 创建支付订单
     * @param array $payment 支付信息
     * @param array $order 订单信息
     * @param string $provider 支付网关提供者
     * @param string $terminal 终端
     * @param array $context 上下文信息
     * @return array 支付订单信息
     */

    public function create(array $payment, array $order, string $provider, string $terminal, array $context): array
    {
        $gateway = $this->gateway($provider);
        if (!$gateway) {
            throw new \RuntimeException('' . $provider);
        }

        if (!$gateway->supportsTerminal($terminal)) {
            throw new \RuntimeException('' . $gateway->name() . '' . $terminal . ']');
        }

        return $gateway->create($payment, $order, $terminal, $context);
    }

    /**
     * 处理支付通知
     * @param string $provider 支付网关提供者
     * @param string $terminal 终端
     * @param Request $request 请求
     * @return array 支付通知信息
     */

    public function notify(string $provider, string $terminal, Request $request): array
    {
        $gateway = $this->gateway($provider);
        if (!$gateway) {
            throw new \RuntimeException('' . $provider);
        }

        return $gateway->notify($request, $terminal);
    }

    /**
     * 加载已分配的支付网关
     * @param string $channel 渠道
     * @return array 已分配的支付网关数组
     */
    private function loadAllocatedProviders(string $channel): array
    {
        $rows = $this->payConfig->getAvailableProviders($channel);
        $map = [];
        foreach ($rows as $row) {
            $map[(string)($row['key'] ?? '')] = $row;
        }
        return $map;
    }

    /**
     * 获取支付网关项
     * @param string $provider 支付网关提供者
     * @param string $terminal 终端
     * @param array $row 支付网关配置
     * @return array 支付网关项
     */
    private function providerItem(string $provider, string $terminal, array $row): array
    {
        $gateway = $this->gateway($provider);
        return [
            'key' => $provider,
            'name' => (string)($row['name'] ?? ($gateway ? $gateway->name() : $provider)),
            'description' => (string)($row['description'] ?? ''),
            'sort' => (int)($row['sort'] ?? 0),
        ];
    }

}