using COSXML;
using COSXML.Auth;
using COSXML.Model.Object;
using HoneyBox.Admin.Business.Models.Config;
using HoneyBox.Admin.Business.Models.Upload;
using HoneyBox.Admin.Business.Services.Interfaces;
using Microsoft.Extensions.Logging;
using System.Security.Cryptography;
using System.Text;

namespace HoneyBox.Admin.Business.Services.Storage;

/// <summary>
/// 腾讯云COS存储提供者
/// 将文件上传到腾讯云COS对象存储
/// </summary>
public class TencentCosProvider : IStorageProvider
{
    private readonly ILogger<TencentCosProvider> _logger;
    private readonly Func<UploadSetting?> _getUploadSetting;
    private const string UploadBasePath = "uploads";

    public TencentCosProvider(
        ILogger<TencentCosProvider> logger,
        Func<UploadSetting?> getUploadSetting)
    {
        _logger = logger;
        _getUploadSetting = getUploadSetting;
    }

    /// <inheritdoc />
    public string StorageType => "3";

    /// <inheritdoc />
    public bool SupportsDirectUpload => true;

    /// <inheritdoc />
    public async Task<UploadResult> UploadAsync(Stream fileStream, string fileName, string contentType)
    {
        try
        {
            var setting = _getUploadSetting();
            if (setting == null)
            {
                return UploadResult.Fail("存储配置无效，请检查上传配置");
            }

            // 验证必要的配置参数
            var validationError = ValidateConfig(setting);
            if (validationError != null)
            {
                return UploadResult.Fail(validationError);
            }

            // 生成日期目录路径: uploads/2026/01/19/
            var now = DateTime.Now;
            var datePath = $"{now.Year}/{now.Month:D2}/{now.Day:D2}";

            // 生成唯一文件名
            var uniqueFileName = GenerateUniqueFileName(fileName);

            // 构建COS对象路径
            var objectKey = $"{UploadBasePath}/{datePath}/{uniqueFileName}";

            // 创建COS客户端
            var cosXml = CreateCosXmlServer(setting);

            // 将流转换为字节数组
            byte[] fileBytes;
            using (var memoryStream = new MemoryStream())
            {
                await fileStream.CopyToAsync(memoryStream);
                fileBytes = memoryStream.ToArray();
            }

            // 上传文件
            var putObjectRequest = new PutObjectRequest(setting.Bucket!, objectKey, fileBytes);
            putObjectRequest.SetRequestHeader("Content-Type", contentType);

            var result = cosXml.PutObject(putObjectRequest);

            if (result.IsSuccessful())
            {
                // 生成访问URL
                var url = GenerateAccessUrl(setting.Domain!, objectKey);
                _logger.LogInformation("腾讯云COS上传成功: {FileName} -> {Url}", fileName, url);
                return UploadResult.Ok(url);
            }
            else
            {
                var errorMessage = $"上传到云存储失败: {result.httpMessage}";
                _logger.LogError("腾讯云COS上传失败: {FileName}, 错误: {Error}", fileName, errorMessage);
                return UploadResult.Fail(errorMessage);
            }
        }
        catch (COSXML.CosException.CosClientException clientEx)
        {
            var errorMessage = $"上传到云存储失败: 客户端错误 - {clientEx.Message}";
            _logger.LogError(clientEx, "腾讯云COS客户端错误: {FileName}", fileName);
            return UploadResult.Fail(errorMessage);
        }
        catch (COSXML.CosException.CosServerException serverEx)
        {
            var errorMessage = $"上传到云存储失败: 服务端错误 - {serverEx.GetInfo()}";
            _logger.LogError(serverEx, "腾讯云COS服务端错误: {FileName}", fileName);
            return UploadResult.Fail(errorMessage);
        }
        catch (Exception ex)
        {
            var errorMessage = $"上传到云存储失败: {ex.Message}";
            _logger.LogError(ex, "腾讯云COS上传异常: {FileName}", fileName);
            return UploadResult.Fail(errorMessage);
        }
    }

    /// <inheritdoc />
    public Task<bool> DeleteAsync(string fileUrl)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(fileUrl))
            {
                return Task.FromResult(false);
            }

            var setting = _getUploadSetting();
            if (setting == null)
            {
                _logger.LogWarning("腾讯云COS删除失败，配置无效");
                return Task.FromResult(false);
            }

            // 从URL中提取对象路径
            var objectKey = ExtractObjectKeyFromUrl(fileUrl, setting.Domain);
            if (string.IsNullOrEmpty(objectKey))
            {
                _logger.LogWarning("腾讯云COS删除失败，无法解析对象路径: {Url}", fileUrl);
                return Task.FromResult(false);
            }

            var cosXml = CreateCosXmlServer(setting);
            var deleteObjectRequest = new DeleteObjectRequest(setting.Bucket!, objectKey);
            var result = cosXml.DeleteObject(deleteObjectRequest);

            if (result.IsSuccessful())
            {
                _logger.LogInformation("腾讯云COS删除成功: {Url}", fileUrl);
                return Task.FromResult(true);
            }

            _logger.LogWarning("腾讯云COS删除失败: {Url}", fileUrl);
            return Task.FromResult(false);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "腾讯云COS删除异常: {Url}", fileUrl);
            return Task.FromResult(false);
        }
    }

    /// <inheritdoc />
    public Task<PresignedUrlResponse?> GetPresignedUploadUrlAsync(string fileName, string contentType, int expiresInSeconds = 600)
    {
        try
        {
            var setting = _getUploadSetting();
            if (setting == null)
            {
                _logger.LogWarning("获取预签名URL失败，配置无效");
                return Task.FromResult<PresignedUrlResponse?>(null);
            }

            // 验证必要的配置参数
            var validationError = ValidateConfig(setting);
            if (validationError != null)
            {
                _logger.LogWarning("获取预签名URL失败: {Error}", validationError);
                return Task.FromResult<PresignedUrlResponse?>(null);
            }

            // 生成日期目录路径: uploads/2026/01/19/
            var now = DateTime.Now;
            var datePath = $"{now.Year}/{now.Month:D2}/{now.Day:D2}";

            // 生成唯一文件名
            var uniqueFileName = GenerateUniqueFileName(fileName);

            // 构建COS对象路径
            var objectKey = $"{UploadBasePath}/{datePath}/{uniqueFileName}";

            // 手动生成预签名URL
            var presignedUrl = GeneratePresignedUrl(setting, objectKey, "PUT", contentType, expiresInSeconds);

            // 生成访问URL
            var fileUrl = GenerateAccessUrl(setting.Domain!, objectKey);

            _logger.LogInformation("生成预签名URL成功: {ObjectKey}, 有效期: {ExpiresIn}秒", objectKey, expiresInSeconds);

            return Task.FromResult<PresignedUrlResponse?>(new PresignedUrlResponse
            {
                UploadUrl = presignedUrl,
                FileUrl = fileUrl,
                ObjectKey = objectKey,
                ExpiresIn = expiresInSeconds,
                StorageType = StorageType
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成预签名URL异常: {FileName}", fileName);
            return Task.FromResult<PresignedUrlResponse?>(null);
        }
    }

    /// <summary>
    /// 手动生成腾讯云COS预签名URL
    /// </summary>
    private static string GeneratePresignedUrl(UploadSetting setting, string objectKey, string httpMethod, string contentType, int expiresInSeconds)
    {
        var startTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        var endTime = startTime + expiresInSeconds;
        var keyTime = $"{startTime};{endTime}";

        // 构建COS主机名
        var host = $"{setting.Bucket}.cos.{setting.Region}.myqcloud.com";
        var urlPath = $"/{objectKey}";

        // 1. 生成 SignKey
        var signKey = HmacSha1(setting.AccessKeySecret!, keyTime);

        // 2. 生成 HttpString
        var httpString = $"{httpMethod.ToLowerInvariant()}\n{urlPath}\n\nhost={host.ToLowerInvariant()}\n";

        // 3. 生成 StringToSign
        var sha1HttpString = Sha1Hash(httpString);
        var stringToSign = $"sha1\n{keyTime}\n{sha1HttpString}\n";

        // 4. 生成 Signature
        var signature = HmacSha1(signKey, stringToSign);

        // 5. 构建 Authorization
        var authorization = $"q-sign-algorithm=sha1&q-ak={setting.AccessKeyId}&q-sign-time={keyTime}&q-key-time={keyTime}&q-header-list=host&q-url-param-list=&q-signature={signature}";

        // 6. 构建完整URL
        var presignedUrl = $"https://{host}{urlPath}?{authorization}";

        return presignedUrl;
    }

    /// <summary>
    /// HMAC-SHA1 签名
    /// </summary>
    private static string HmacSha1(string key, string data)
    {
        using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key));
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }

    /// <summary>
    /// SHA1 哈希
    /// </summary>
    private static string Sha1Hash(string data)
    {
        var hash = SHA1.HashData(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }

    /// <summary>
    /// 验证配置参数
    /// </summary>
    private static string? ValidateConfig(UploadSetting setting)
    {
        if (string.IsNullOrWhiteSpace(setting.Bucket))
            return "存储配置无效: Bucket不能为空";
        if (string.IsNullOrWhiteSpace(setting.Region))
            return "存储配置无效: Region不能为空";
        if (string.IsNullOrWhiteSpace(setting.AccessKeyId))
            return "存储配置无效: AccessKeyId不能为空";
        if (string.IsNullOrWhiteSpace(setting.AccessKeySecret))
            return "存储配置无效: AccessKeySecret不能为空";
        if (string.IsNullOrWhiteSpace(setting.Domain))
            return "存储配置无效: Domain不能为空";
        return null;
    }

    /// <summary>
    /// 创建COS客户端
    /// </summary>
    private static CosXml CreateCosXmlServer(UploadSetting setting)
    {
        var config = new CosXmlConfig.Builder()
            .IsHttps(true)
            .SetRegion(setting.Region!)
            .Build();

        var credentialProvider = new DefaultQCloudCredentialProvider(
            setting.AccessKeyId!,
            setting.AccessKeySecret!,
            600); // 临时密钥有效期600秒

        return new CosXmlServer(config, credentialProvider);
    }

    /// <summary>
    /// 生成唯一文件名
    /// 格式: {timestamp}_{guid}{extension}
    /// </summary>
    public static string GenerateUniqueFileName(string originalFileName)
    {
        var extension = Path.GetExtension(originalFileName).ToLowerInvariant();
        var timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
        var guid = Guid.NewGuid().ToString("N")[..8]; // 取GUID前8位
        return $"{timestamp}_{guid}{extension}";
    }

    /// <summary>
    /// 生成访问URL
    /// </summary>
    public static string GenerateAccessUrl(string domain, string objectKey)
    {
        // 确保domain以https://开头，不以/结尾
        var normalizedDomain = domain.TrimEnd('/');
        if (!normalizedDomain.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
            !normalizedDomain.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        {
            normalizedDomain = $"https://{normalizedDomain}";
        }

        // 确保objectKey以/开头
        var normalizedKey = objectKey.StartsWith('/') ? objectKey : $"/{objectKey}";

        return $"{normalizedDomain}{normalizedKey}";
    }

    /// <summary>
    /// 从URL中提取对象路径
    /// </summary>
    private static string? ExtractObjectKeyFromUrl(string fileUrl, string? domain)
    {
        if (string.IsNullOrWhiteSpace(domain))
            return null;

        try
        {
            var uri = new Uri(fileUrl);
            return uri.AbsolutePath.TrimStart('/');
        }
        catch
        {
            return null;
        }
    }
}
