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

use app\service\domain\storage\StorageConfigService;
use think\App;
use think\facade\Log;


/**
 * 阿里云 OSS存储网关服务
 * @package app\service\domain\storage\gateway
 */
class AliyunOssGateway extends BaseStorageGateway
{
    private App $app;
    private StorageConfigService $config;

    /**
     * 构造函数
     * @param App $app 应用实例
     * @param StorageConfigService $config 存储配置服务
     */
    public function __construct(App $app, StorageConfigService $config)
    {
        $this->app = $app;
        $this->config = $config;
    }

    /**
     * 上传文件到阿里云 OSS
     * @param string $localPath 本地文件路径
     * @param string $remotePath 远程文件路径
     * @param array $options 上传选项
     * @return array{url:string, path:string}
     */
    public function upload(string $localPath, string $remotePath, array $options = []): array
    {
        $object = $this->normalizePath($remotePath);
        $endpoint = $this->config->aliyunEndpoint();
        $bucket = $this->config->aliyunBucket();
        $host = $bucket . '.' . $endpoint;
        $date = gmdate('D, d M Y H:i:s \G\M\T');
        $contentType = $this->resolveContentType($this->ext($object));
        $content = @file_get_contents($localPath);

        if ($content === false) {
            throw new \RuntimeException('');
        }

        $contentLength = strlen($content);
        $stringToSign = "PUT\n\n{$contentType}\n{$date}\n/{$bucket}/{$object}";
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->config->aliyunAccessKeySecret(), true));
        $auth = "OSS {$this->config->aliyunAccessKeyId()}:{$signature}";

        $client = new \GuzzleHttp\Client(['timeout' => 30]);
        $response = $client->put("https://{$host}/{$object}", [
            'headers' => [
                'Authorization' => $auth,
                'Date' => $date,
                'Content-Type' => $contentType,
                'Content-Length' => (string)$contentLength,
            ],
            'body' => $content,
        ]);

        if ($response->getStatusCode() !== 200) {
            Log::error('aliyun_oss upload failed: ' . $response->getStatusCode());
            throw new \RuntimeException('' . $response->getStatusCode());
        }

        return [
            'url' => "https://{$host}/{$object}",
            'path' => $object,
        ];
    }

    /**
     * 删除文件从阿里云 OSS
     * @param string $remotePath 远程文件路径
     * @return bool
     */
    public function delete(string $remotePath): bool
    {
        $object = $this->normalizePath($remotePath);
        $endpoint = $this->config->aliyunEndpoint();
        $bucket = $this->config->aliyunBucket();
        $host = $bucket . '.' . $endpoint;
        $date = gmdate('D, d M Y H:i:s \G\M\T');

        $stringToSign = "DELETE\n\n\n{$date}\n/{$bucket}/{$object}";
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->config->aliyunAccessKeySecret(), true));
        $auth = "OSS {$this->config->aliyunAccessKeyId()}:{$signature}";

        $client = new \GuzzleHttp\Client(['timeout' => 10]);
        $response = $client->delete("https://{$host}/{$object}", [
            'headers' => [
                'Authorization' => $auth,
                'Date' => $date,
            ],
        ]);

        return in_array($response->getStatusCode(), [200, 204], true);
    }

    /**
     * 获取文件的临时URL
     * @param string $remotePath 远程文件路径
     * @param int $expireSeconds 过期时间（秒）
     * @return string
     */
    public function url(string $remotePath, int $expireSeconds = 0): string
    {
        $object = $this->normalizePath($remotePath);
        $endpoint = $this->config->aliyunEndpoint();
        $bucket = $this->config->aliyunBucket();

        if ($expireSeconds > 0) {
            $expires = time() + $expireSeconds;
            $stringToSign = "GET\n\n\n{$expires}\n/{$bucket}/{$object}";
            $signature = urlencode(base64_encode(hash_hmac('sha1', $stringToSign, $this->config->aliyunAccessKeySecret(), true)));
            return "https://{$bucket}.{$endpoint}/{$object}?OSSAccessKeyId={$this->config->aliyunAccessKeyId()}&Expires={$expires}&Signature={$signature}";
        }

        return "https://{$bucket}.{$endpoint}/{$object}";
    }

    /**
     * 获取存储提供程序名称
     * @return string
     */
    public function provider(): string
    {
        return 'aliyun_oss';
    }

    /**
     * 获取存储网关名称
     * @return string
     */
    public function name(): string
    {
        return '';
    }

    /**
     * 检查存储网关是否已配置
     * @return bool
     */
    public function isConfigured(): bool
    {
        return $this->config->aliyunAccessKeyId() !== ''
            && $this->config->aliyunAccessKeySecret() !== ''
            && $this->config->aliyunEndpoint() !== ''
            && $this->config->aliyunBucket() !== '';
    }

    /**
     * 解析文件扩展名对应的 Content-Type
     * @param string $ext 文件扩展名
     * @return string
     */
    private function resolveContentType(string $ext): string
    {
        $map = [
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            'mp4' => 'video/mp4',
            'webm' => 'video/webm',
            'pdf' => 'application/pdf',
            'zip' => 'application/zip',
        ];
        return $map[$ext] ?? 'application/octet-stream';
    }
}
