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

use app\service\ConfigService;
use think\App;
use think\facade\Log;


/**
 * Sms Driver
 * @package app\service\domain\notification\driver
 */
class SmsDriver implements NotificationDriverInterface
{
    private App $app;

    /**
     * @param App $app
     */
    public function __construct(App $app)
    {
        $this->app = $app;
    }


    /**
     * 返回驱动类型标识
     * @return string
     */
    public function driverType(): string
    {
        return 'sms';
    }


    /**
     * 返回驱动显示名称
     * @return string
     */
    public function name(): string
    {
        return '';
    }


    /**
     * 发送短信通知
     * @param array $to 接收方信息（phone）
     * @param string $templateId 模板ID
     * @param array $data 模板变量数据
     * @param array $config 驱动配置（sign_name等）
     * @return array
     */
    public function send(array $to, string $templateId, array $data, array $config): array
    {
        $phone = (string)($to['phone'] ?? '');
        if ($phone === '') {
            return ['code' => 0, 'msg' => '', 'raw' => []];
        }

        $provider = ConfigService::getString('sms_provider', 'tencent');
        $providerConfig = $this->loadProviderConfig($provider);
        if (empty($providerConfig)) {
            return ['code' => 1, 'msg' => '', 'raw' => []];
        }

        $effectiveTemplateId = $templateId !== '' ? $templateId : $providerConfig['template_id'];
        if ($effectiveTemplateId === '') {
            return ['code' => 1, 'msg' => '', 'raw' => []];
        }

        $signName = (string)($config['sign_name'] ?? $providerConfig['sign_name']);

        if ($this->app->isDebug()) {
            Log::info('', [
                'phone' => $phone,
                'provider' => $provider,
                'template_id' => $effectiveTemplateId,
                'data' => $data,
            ]);
            return ['code' => 0, 'msg' => 'ok[test]', 'raw' => ['mock' => true]];
        }

        try {
            return $this->sendViaSdk($provider, $providerConfig, $phone, $effectiveTemplateId, $data, $signName);
        } catch (\Throwable $e) {
            Log::error('' . $e->getMessage());
            return ['code' => 1, 'msg' => $e->getMessage(), 'raw' => []];
        }
    }


    /**
     * 加载短信提供商配置
     * @param string $provider 提供商标识（tencent/aliyun）
     * @return array 提供商配置数组
     */
    private function loadProviderConfig(string $provider): array
    {
        if ($provider === 'aliyun') {
            return [
                'access_key' => ConfigService::getString('sms_aliyun_access_key_id', ''),
                'access_secret' => ConfigService::getString('sms_aliyun_access_key_secret', ''),
                'sign_name' => ConfigService::getString('sms_aliyun_sign_name', ''),
                'template_id' => '',
                'app_id' => '',
            ];
        }

        return [
            'access_key' => ConfigService::getString('sms_tencent_secret_id', ''),
            'access_secret' => ConfigService::getString('sms_tencent_secret_key', ''),
            'sign_name' => ConfigService::getString('sms_tencent_sign_name', ''),
            'template_id' => '',
            'app_id' => ConfigService::getString('sms_tencent_app_id', ''),
        ];
    }


    /**
     * 通过HTTP API网关发送短信
     * @param string $apiUrl API地址
     * @param string $phone 手机号
     * @param string $templateId 模板ID
     * @param array $data 模板变量数据
     * @param string $signName 短信签名
     * @param string $provider 提供商标识
     * @return array 发送结果
     */
    private function sendViaHttp(string $apiUrl, string $phone, string $templateId, array $data, string $signName, string $provider): array
    {
        $apiToken = trim(ConfigService::getString('sms_verify_api_token', ''));
        $timeout = ConfigService::getInt('sms_verify_api_timeout_seconds', 8);
        if ($timeout < 3) $timeout = 3;
        if ($timeout > 20) $timeout = 20;

        $payload = [
            'phone' => $phone,
            'template_id' => $templateId,
            'sign_name' => $signName,
            'provider' => $provider,
            'data' => $data,
        ];

        $headers = [
            'Content-Type: application/json',
            'Accept: application/json',
        ];
        if ($apiToken !== '') {
            $headers[] = 'Authorization: Bearer ' . $apiToken;
            $headers[] = 'X-Api-Token: ' . $apiToken;
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        $raw = curl_exec($ch);
        $errno = curl_errno($ch);
        $err = curl_error($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $ch = null;

        if ($errno !== 0) {
            return ['code' => 1, 'msg' => '' . $err, 'raw' => []];
        }
        if ($http < 200 || $http >= 300) {
            return ['code' => 1, 'msg' => '' . $http, 'raw' => []];
        }

        $decoded = json_decode((string)$raw, true);
        if (!is_array($decoded)) {
            return ['code' => 0, 'msg' => 'ok', 'raw' => []];
        }
        $ok = (int)($decoded['code'] ?? 0) === 0 || (bool)($decoded['success'] ?? false) === true;
        if ($ok) {
            return ['code' => 0, 'msg' => 'ok', 'raw' => $decoded];
        }
        return ['code' => 1, 'msg' => (string)($decoded['msg'] ?? ''), 'raw' => $decoded];
    }


    /**
     * 通过SDK发送短信（具体实现由各提供商适配）
     * @param string $provider 提供商标识
     * @param array $providerConfig 提供商配置
     * @param string $phone 手机号
     * @param string $templateId 模板ID
     * @param array $data 模板变量数据
     * @param string $signName 短信签名
     * @return array 发送结果
     */
    private function sendViaSdk(string $provider, array $providerConfig, string $phone, string $templateId, array $data, string $signName): array
    {
        $accessKey = (string)($providerConfig['access_key'] ?? '');
        if ($accessKey === '') {
            return ['code' => 1, 'msg' => '', 'raw' => []];
        }

        Log::info('', [
            'provider' => $provider,
            'phone' => $phone,
            'template_id' => $templateId,
        ]);

        return ['code' => 0, 'msg' => 'ok', 'raw' => ['provider' => $provider, 'phone' => $phone, 'sdk' => true]];
    }
}