<?php

namespace Kylin\Sms\Gateways;

use Kylin\Sms\Exceptions\GatewayException;

/**
 * 阿里云短信网关
 * 
 * 支持阿里云短信服务的发送、模板发送和批量发送功能
 * 文档：https://help.aliyun.com/product/44282.html
 */
class AliyunGateway extends BaseGateway
{
    /**
     * 网关名称标识
     */
    protected static string $name = 'aliyun';

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

    /**
     * API 版本号
     */
    protected const API_VERSION = '2017-05-25';

    /**
     * 签名算法
     */
    protected const SIGNATURE_METHOD = 'HMAC-SHA1';

    /**
     * 签名版本
     */
    protected const SIGNATURE_VERSION = '1.0';

    /**
     * 响应格式
     */
    protected const FORMAT = 'JSON';

    /**
     * 阿里云 AccessKey ID
     */
    protected string $accessKeyId;

    /**
     * 阿里云 AccessKey Secret
     */
    protected string $accessKeySecret;

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

    /**
     * 地域 ID
     */
    protected string $regionId = 'cn-hangzhou';

    /**
     * 构造函数
     * 
     * 初始化阿里云短信网关配置
     * 
     * @param array $config 网关配置数组
     */
    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->accessKeyId = $config['access_key_id'] ?? '';
        $this->accessKeySecret = $config['access_key_secret'] ?? '';
        $this->signName = $config['sign_name'] ?? '';
        $this->regionId = $config['region_id'] ?? 'cn-hangzhou';
    }

    /**
     * 发送短信
     * 
     * @param string $to 接收手机号
     * @param string $content 短信内容（阿里云使用模板，此参数可忽略）
     * @param array $data 附加数据，包含 template_code 和 template_params
     * @return array 发送结果
     */
    public function send(string $to, string $content, array $data = []): array
    {
        // 构建公共请求参数
        $params = $this->buildCommonParams('SendSms');
        $params['PhoneNumbers'] = $this->formatPhone($to);
        $params['SignName'] = $data['sign_name'] ?? $this->signName;
        $params['TemplateCode'] = $data['template_code'] ?? '';
        $params['TemplateParam'] = json_encode($data['template_params'] ?? []);

        return $this->executeRequest($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_code' => $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 = $this->buildCommonParams('SendBatchSms');
        $params['PhoneNumberJson'] = json_encode(array_map(fn($r) => $this->formatPhone($r), $recipients));
        $params['SignNameJson'] = json_encode(array_fill(0, count($recipients), $data['sign_name'] ?? $this->signName));
        $params['TemplateCode'] = $data['template_code'] ?? '';
        $params['TemplateParamJson'] = json_encode(array_fill(0, count($recipients), $data['template_params'] ?? []));

        return $this->executeRequest($params);
    }

    /**
     * 构建公共请求参数
     * 
     * @param string $action API 操作名称
     * @return array 公共参数数组
     */
    protected function buildCommonParams(string $action): array
    {
        return [
            'Action' => $action,
            'Version' => self::API_VERSION,
            'Format' => self::FORMAT,
            'AccessKeyId' => $this->accessKeyId,
            'SignatureMethod' => self::SIGNATURE_METHOD,
            'SignatureVersion' => self::SIGNATURE_VERSION,
            'SignatureNonce' => uniqid(),
            'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
            'RegionId' => $this->regionId,
        ];
    }

    /**
     * 执行 API 请求
     * 
     * @param array $params 请求参数
     * @return array 响应结果
     */
    protected function executeRequest(array $params): array
    {
        $params['Signature'] = $this->computeSignature($params);
        $url = self::API_URL . '?' . http_build_query($params);

        try {
            $response = $this->request('GET', $url);
            return $this->parseResponse($response);
        } catch (GatewayException $e) {
            return $this->buildErrorResponse($e->getMessage(), $e->getCode(), $e->getContext());
        }
    }

    /**
     * 计算请求签名
     * 
     * @param array $params 请求参数
     * @return string 签名字符串
     */
    protected function computeSignature(array $params): string
    {
        ksort($params);
        $queryString = '';
        foreach ($params as $key => $value) {
            $queryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
        }
        $queryString = substr($queryString, 1);
        $stringToSign = 'GET&%2F&' . $this->percentEncode($queryString);
        return base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret . '&', true));
    }

    /**
     * URL 编码
     * 
     * @param string $str 待编码字符串
     * @return string 编码后的字符串
     */
    protected function percentEncode(string $str): string
    {
        return urlencode($str);
    }

    /**
     * 解析响应结果
     * 
     * @param array $response API 响应
     * @return array 标准化的响应结果
     */
    protected function parseResponse(array $response): array
    {
        $code = $response['Code'] ?? '';
        if ($code === 'OK') {
            return $this->buildSuccessResponse([
                'biz_id' => $response['BizId'] ?? '',
                'request_id' => $response['RequestId'] ?? '',
            ]);
        }

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