<?php

namespace Kylin\Sms\Gateways;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Kylin\Sms\Contracts\GatewayInterface;
use Kylin\Sms\Exceptions\GatewayException;
use Kylin\Sms\Exceptions\InvalidPhoneNumberException;

/**
 * 短信网关基类
 * 
 * 所有短信服务商网关的抽象基类，提供通用的功能方法：
 * - HTTP 客户端创建
 * - 手机号验证与格式化
 * - 响应构建
 * - 请求发送
 */
abstract class BaseGateway implements GatewayInterface
{
    /**
     * 网关配置数组
     * 包含 API 密钥、签名、端点等配置信息
     */
    protected array $config = [];

    /**
     * Guzzle HTTP 客户端实例
     */
    protected ?Client $client = null;

    /**
     * 请求超时时间（秒）
     */
    protected int $timeout = 30;

    /**
     * 网关名称标识
     * 子类必须重写此属性
     */
    protected static string $name = '';

    /**
     * 构造函数
     * 
     * @param array $config 网关配置数组
     */
    public function __construct(array $config = [])
    {
        $this->setConfig($config);
        $this->client = $this->createClient();
    }

    /**
     * 获取网关名称
     * 
     * @return string 网关标识名称
     */
    public static function getName(): string
    {
        return static::$name;
    }

    /**
     * 设置网关配置
     * 
     * @param array $config 配置数组
     * @return self 支持链式调用
     */
    public function setConfig(array $config): self
    {
        $this->config = array_merge($this->config, $config);
        return $this;
    }

    /**
     * 获取当前网关配置
     * 
     * @return array 配置数组
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * 创建 HTTP 客户端
     * 
     * 创建一个配置好的 Guzzle 客户端实例
     * 
     * @return Client HTTP 客户端实例
     */
    protected function createClient(): Client
    {
        return new Client([
            'timeout' => $this->timeout,
            'verify' => false,
        ]);
    }

    /**
     * 验证手机号格式
     * 
     * 验证手机号是否符合中国大陆手机号格式（1开头，11位数字）
     * 
     * @param string $phone 手机号码
     * @return bool 验证通过返回 true
     * @throws InvalidPhoneNumberException 手机号格式无效时抛出异常
     */
    protected function validatePhone(string $phone): bool
    {
        $pattern = '/^1[3-9]\d{9}$/';
        if (!preg_match($pattern, $phone)) {
            throw new InvalidPhoneNumberException($phone);
        }
        return true;
    }

    /**
     * 格式化手机号
     * 
     * 验证并返回格式化后的手机号
     * 
     * @param string $phone 手机号码
     * @return string 格式化后的手机号
     * @throws InvalidPhoneNumberException 手机号格式无效时抛出异常
     */
    protected function formatPhone(string $phone): string
    {
        $this->validatePhone($phone);
        return $phone;
    }

    /**
     * 格式化多个手机号
     * 
     * 将多个手机号格式化为逗号分隔的字符串
     * 
     * @param array $phones 手机号数组
     * @return string 逗号分隔的手机号字符串
     */
    protected function formatPhones(array $phones): string
    {
        return implode(',', array_map(fn($phone) => $this->formatPhone($phone), $phones));
    }

    /**
     * 发送 HTTP 请求
     * 
     * 使用 Guzzle 发送 HTTP 请求并返回解析后的 JSON 响应
     * 
     * @param string $method HTTP 方法（GET、POST、PUT 等）
     * @param string $url 请求 URL
     * @param array $options 请求选项（headers、body、form_params 等）
     * @return array 解析后的响应数组
     * @throws GatewayException 请求失败时抛出异常
     */
    protected function request(string $method, string $url, array $options = []): array
    {
        try {
            $response = $this->client->request($method, $url, $options);
            $body = $response->getBody()->getContents();
            return json_decode($body, true) ?: [];
        } catch (GuzzleException $e) {
            throw new GatewayException(
                static::getName(),
                $e->getMessage(),
                $e->getCode(),
                ['exception' => $e->getMessage()]
            );
        }
    }

    /**
     * 构建成功响应
     * 
     * @param array $data 响应数据
     * @return array 标准化的成功响应数组
     */
    protected function buildSuccessResponse(array $data = []): array
    {
        return [
            'success' => true,
            'gateway' => static::getName(),
            'data' => $data,
        ];
    }

    /**
     * 构建错误响应
     * 
     * @param string $message 错误消息
     * @param int $code 错误代码
     * @param array $context 错误上下文信息
     * @return array 标准化的错误响应数组
     */
    protected function buildErrorResponse(string $message, int $code = 500, array $context = []): array
    {
        return [
            'success' => false,
            'gateway' => static::getName(),
            'error' => [
                'message' => $message,
                'code' => $code,
                'context' => $context,
            ],
        ];
    }

    /**
     * 生成随机字符串
     * 
     * 用于生成签名所需的随机字符串
     * 
     * @param int $length 字符串长度
     * @return string 随机字符串
     */
    protected function generateRandomString(int $length = 32): string
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[random_int(0, strlen($chars) - 1)];
        }
        return $str;
    }

    /**
     * 生成时间戳（毫秒）
     * 
     * @return int 毫秒级时间戳
     */
    protected function getTimestamp(): int
    {
        return (int) (microtime(true) * 1000);
    }

    /**
     * URL 安全 Base64 编码
     * 
     * @param string $data 待编码数据
     * @return string 编码后的字符串
     */
    protected function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
}
