<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class VolcEngineService
{
    /** @var Client */
    protected $client;

    public function __construct()
    {
        $this->client = new Client([
            'timeout' => 120.0,
        ]);
    }

    /**
     * 通用请求方法：内部完成火山引擎签名并发送 HTTP 请求
     *
     * @param string $method      HTTP 方法：GET / POST / PUT ...
     * @param string $host        目标 Host，例如：iam.volcengineapi.com
     * @param string $path        请求路径，例如："/" 或 "/"
     * @param array  $query       查询参数（不含 Action/Version）
     * @param string $body        请求 Body 字符串（GET 一般为空字符串）
     * @param string $ak          AccessKeyId
     * @param string $sk          SecretAccessKey
     * @param string $service     服务名，例如：iam / vod / sms 等
     * @param string $region      区域，例如：cn-north-1
     * @param string $action      公共参数 Action
     * @param string $version     公共参数 Version
     * @param string $contentType Content-Type，默认 application/json
     *
     * @return array [
     *     'status' => int,
     *     'headers' => array,
     *     'body' => string,
     * ]
     * @throws GuzzleException
     */
    public function request(
        string $method,
        string $host,
        string $path,
        array  $query,
        string $body,
        string $ak,
        string $sk,
        string $service,
        string $region,
        string $action,
        string $version,
        string $contentType = 'application/json'
    ): array {
        // 初始化身份证明结构体
        $credential = [
            'accessKeyId' => $ak,
            'secretKeyId' => $sk,
            'service' => $service,
            'region' => $region,
        ];
        
        // 合并公共参数，并按 key 排序
        $query = array_merge($query, [
            'Action' => $action,
            'Version' => $version
        ]);
        ksort($query);

        // 初始化请求参数
        $requestParam = [
            'body' => $body,
            'host' => $host,
            'path' => $path,
            'method' => $method,
            'contentType' => $contentType,
            'date' => gmdate('Ymd\\THis\\Z'),
            'query' => $query
        ];

        // 计算签名
        $xDate = $requestParam['date'];
        $shortXDate = substr($xDate, 0, 8);
        $xContentSha256 = hash('sha256', $requestParam['body']);
        
        $signResult = [
            'Host' => $requestParam['host'],
            'X-Content-Sha256' => $xContentSha256,
            'X-Date' => $xDate,
            'Content-Type' => $requestParam['contentType']
        ];

        // 计算 Signature 签名
        $signedHeaderStr = join(';', ['content-type', 'host', 'x-content-sha256', 'x-date']);
        $canonicalRequestStr = join("\n", [
            $requestParam['method'],
            $requestParam['path'],
            http_build_query($requestParam['query']),
            join("\n", [
                'content-type:'. $requestParam['contentType'], 
                'host:'. $requestParam['host'], 
                'x-content-sha256:'. $xContentSha256, 
                'x-date:'. $xDate
            ]),
            '',
            $signedHeaderStr,
            $xContentSha256
        ]);

        $hashedCanonicalRequest = hash("sha256", $canonicalRequestStr);
        $credentialScope = join('/', [$shortXDate, $credential['region'], $credential['service'], 'request']);
        $stringToSign = join("\n", [
            'HMAC-SHA256', 
            $xDate, 
            $credentialScope, 
            $hashedCanonicalRequest
        ]);
        
        $kDate = hash_hmac("sha256", $shortXDate, $credential['secretKeyId'], true);
        $kRegion = hash_hmac("sha256", $credential['region'], $kDate, true);
        $kService = hash_hmac("sha256", $credential['service'], $kRegion, true);
        $kSigning = hash_hmac("sha256", 'request', $kService, true);
        $signature = hash_hmac("sha256", $stringToSign, $kSigning);
        
        $signResult['Authorization'] = sprintf(
            "HMAC-SHA256 Credential=%s, SignedHeaders=%s, Signature=%s", 
            $credential['accessKeyId']. '/'. $credentialScope, 
            $signedHeaderStr, 
            $signature
        );

        $header = array_merge([], $signResult); // 空的$header数组

        // 发送 HTTP 请求
        $client = new Client([
            'base_uri' => 'https://'. $requestParam['host'],
            'timeout' => 120.0,
        ]);
        
        $response = $client->request($method, $requestParam['path'], [
            'headers' => $header,
            'query' => $requestParam['query'],
            'body' => $requestParam['body']
        ]);

        $responseContent = $response->getBody()->getContents();
        // 转换 \u0026 为 &
        $responseContent = str_replace('\u0026', '&', $responseContent);

        return [
            'status'  => $response->getStatusCode(),
            'headers' => $response->getHeaders(),
            'body'    => $responseContent,
        ];
    }
}
