<?php

namespace Kylin\Sms\Gateways;

use Kylin\Sms\Exceptions\GatewayException;

/**
 * 腾讯云短信网关
 * 
 * 支持腾讯云短信服务的发送、模板发送和批量发送功能
 * 文档：https://cloud.tencent.com/document/product/382
 */
class TencentGateway extends BaseGateway
{
    /**
     * 网关名称标识
     */
    protected static string $name = 'tencent';

    /**
     * API 请求地址
     */
    protected const API_URL = 'https://sms.tencentcloudapi.com';

    /**
     * API 版本号
     */
    protected const API_VERSION = '2021-01-11';

    /**
     * 腾讯云 Secret ID
     */
    protected string $secretId;

    /**
     * 腾讯云 Secret Key
     */
    protected string $secretKey;

    /**
     * 短信应用 ID
     */
    protected string $appId;

    /**
     * 短信签名
     */
    protected string $signName;

    /**
     * 地域
     */
    protected string $region = 'ap-guangzhou';

    /**
     * 构造函数
     * 
     * 初始化腾讯云短信网关配置
     * 
     * @param array $config 网关配置数组
     */
    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->secretId = $config['secret_id'] ?? '';
        $this->secretKey = $config['secret_key'] ?? '';
        $this->appId = $config['app_id'] ?? '';
        $this->signName = $config['sign_name'] ?? '';
        $this->region = $config['region'] ?? 'ap-guangzhou';
    }

    /**
     * 发送短信
     * 
     * @param string $to 接收手机号
     * @param string $content 短信内容
     * @param array $data 附加数据
     * @return array 发送结果
     */
    public function send(string $to, string $content, array $data = []): array
    {
        $params = [
            'SmsSdkAppId' => $this->appId,
            'SignName' => $data['sign_name'] ?? $this->signName,
            'PhoneNumberSet' => ['+86' . $this->formatPhone($to)],
            'TemplateId' => $data['template_id'] ?? '',
            'TemplateParamSet' => array_values($data['template_params'] ?? []),
        ];

        return $this->executeRequest('SendSms', $params);
    }

    /**
     * 发送模板短信
     * 
     * @param string $to 接收手机号
     * @param string $templateId 模板 ID
     * @param array $params 模板参数
     * @return array 发送结果
     */
    public function sendTemplate(string $to, string $templateId, array $params = []): array
    {
        $data = [
            'template_id' => $templateId,
            'template_params' => $params,
        ];
        return $this->send($to, '', $data);
    }

    /**
     * 批量发送短信
     * 
     * @param array $recipients 接收手机号数组
     * @param string $content 短信内容
     * @param array $data 附加数据
     * @return array 发送结果
     */
    public function sendBatch(array $recipients, string $content, array $data = []): array
    {
        $params = [
            'SmsSdkAppId' => $this->appId,
            'SignName' => $data['sign_name'] ?? $this->signName,
            'PhoneNumberSet' => array_map(fn($r) => '+86' . $this->formatPhone($r), $recipients),
            'TemplateId' => $data['template_id'] ?? '',
            'TemplateParamSet' => array_values($data['template_params'] ?? []),
        ];

        return $this->executeRequest('SendSms', $params);
    }

    /**
     * 执行 API 请求
     * 
     * @param string $action API 操作名称
     * @param array $params 请求参数
     * @return array 响应结果
     */
    protected function executeRequest(string $action, array $params): array
    {
        $timestamp = time();
        $date = gmdate('Y-m-d', $timestamp);

        $headers = $this->buildHeaders($action, $params, $timestamp, $date);

        try {
            $response = $this->request('POST', self::API_URL, [
                'headers' => $headers,
                'json' => $params,
            ]);
            return $this->parseResponse($response);
        } catch (GatewayException $e) {
            return $this->buildErrorResponse($e->getMessage(), $e->getCode(), $e->getContext());
        }
    }

    /**
     * 构建请求头
     * 
     * @param string $action API 操作名称
     * @param array $params 请求参数
     * @param int $timestamp 时间戳
     * @param string $date 日期字符串
     * @return array 请求头数组
     */
    protected function buildHeaders(string $action, array $params, int $timestamp, string $date): array
    {
        $payload = json_encode($params);
        $hashedPayload = hash('sha256', $payload);
        $canonicalRequest = "POST\n/\n\ncontent-type:application/json\nhost:sms.tencentcloudapi.com\n\ncontent-type;host\n" . $hashedPayload;

        $credentialScope = $date . '/sms/tc3_request';
        $hashedCanonicalRequest = hash('sha256', $canonicalRequest);
        $stringToSign = "TC3-HMAC-SHA256\n" . $timestamp . "\n" . $credentialScope . "\n" . $hashedCanonicalRequest;

        $secretDate = hash_hmac('sha256', $date, 'TC3' . $this->secretKey, true);
        $secretService = hash_hmac('sha256', 'sms', $secretDate, true);
        $secretSigning = hash_hmac('sha256', 'tc3_request', $secretService, true);
        $signature = hash_hmac('sha256', $stringToSign, $secretSigning);

        $authorization = 'TC3-HMAC-SHA256 Credential=' . $this->secretId . '/' . $credentialScope .
            ', SignedHeaders=content-type;host, Signature=' . $signature;

        return [
            'Authorization' => $authorization,
            'Content-Type' => 'application/json',
            'Host' => 'sms.tencentcloudapi.com',
            'X-TC-Action' => $action,
            'X-TC-Timestamp' => $timestamp,
            'X-TC-Version' => self::API_VERSION,
            'X-TC-Region' => $this->region,
        ];
    }

    /**
     * 解析响应结果
     * 
     * 将腾讯云 API 响应转换为标准化的结果格式
     * 
     * @param array $response API 响应
     * @return array 标准化的响应结果
     */
    protected function parseResponse(array $response): array
    {
        if (isset($response['Response']['SendStatusSet'])) {
            $statusSet = $response['Response']['SendStatusSet'];
            if (!empty($statusSet) && $statusSet[0]['Code'] === 'Ok') {
                return $this->buildSuccessResponse([
                    'request_id' => $response['Response']['RequestId'] ?? '',
                    'serial_no' => $statusSet[0]['SerialNo'] ?? '',
                ]);
            }

            $error = $statusSet[0] ?? [];
            return $this->buildErrorResponse(
                $error['Message'] ?? '发送失败',
                $error['Code'] ?? 500,
                $response
            );
        }

        if (isset($response['Response']['Error'])) {
            return $this->buildErrorResponse(
                $response['Response']['Error']['Message'] ?? '未知错误',
                $response['Response']['Error']['Code'] ?? 500,
                $response
            );
        }

        return $this->buildErrorResponse('未知响应格式', 500, $response);
    }
}
