using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using TakeoutSaaS.Module.Sms.Abstractions;
using TakeoutSaaS.Module.Sms.Models;
using TakeoutSaaS.Module.Sms.Options;

namespace TakeoutSaaS.Module.Sms.Services;

/// <summary>
/// 腾讯云短信发送实现（TC3-HMAC 签名）。
/// </summary>
public sealed class TencentSmsSender(IHttpClientFactory httpClientFactory, IOptionsMonitor<SmsOptions> optionsMonitor, ILogger<TencentSmsSender> logger)
    : ISmsSender
{
    private const string Service = "sms";
    private const string Action = "SendSms";
    private const string Version = "2021-01-11";

    /// <inheritdoc />
    public SmsProviderKind Provider => SmsProviderKind.Tencent;

    /// <inheritdoc />
    public async Task<SmsSendResult> SendAsync(SmsSendRequest request, CancellationToken cancellationToken = default)
    {
        // 1. 读取配置并处理 Mock
        var options = optionsMonitor.CurrentValue;
        if (options.UseMock)
        {
            logger.LogInformation("Mock 发送短信到 {Phone}, Template:{Template}, Vars:{Vars}", request.PhoneNumber, request.TemplateCode, JsonSerializer.Serialize(request.Variables));
            return new SmsSendResult { Success = true, Message = "Mocked" };
        }

        // 2. 构建请求负载与签名所需字段
        var tencent = options.Tencent;
        var payload = BuildPayload(request, tencent);
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        var date = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);

        var host = new Uri(tencent.Endpoint).Host;
        var canonicalRequest = BuildCanonicalRequest(payload, host, tencent.Endpoint.StartsWith("https", StringComparison.OrdinalIgnoreCase));
        var stringToSign = BuildStringToSign(canonicalRequest, timestamp, date);
        var signature = Sign(stringToSign, tencent.SecretKey, date);

        // 3. 构建 HTTP 请求
        using var httpClient = httpClientFactory.CreateClient(nameof(TencentSmsSender));
        using var httpRequest = new HttpRequestMessage(HttpMethod.Post, tencent.Endpoint)
        {
            Content = new StringContent(payload, Encoding.UTF8, "application/json")
        };

        httpRequest.Headers.Add("Host", host);
        httpRequest.Headers.Add("X-TC-Action", Action);
        httpRequest.Headers.Add("X-TC-Version", Version);
        httpRequest.Headers.Add("X-TC-Timestamp", timestamp.ToString(CultureInfo.InvariantCulture));
        httpRequest.Headers.Add("X-TC-Region", tencent.Region);
        httpRequest.Headers.Add("Authorization",
            $"TC3-HMAC-SHA256 Credential={tencent.SecretId}/{date}/{Service}/tc3_request, SignedHeaders=content-type;host, Signature={signature}");

        // 4. 发送请求并读取响应
        var response = await httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
        var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

        if (!response.IsSuccessStatusCode)
        {
            logger.LogWarning("腾讯云短信发送失败：{Status} {Content}", response.StatusCode, content);
            return new SmsSendResult { Success = false, Message = content };
        }

        // 5. 解析响应
        using var doc = JsonDocument.Parse(content);
        var root = doc.RootElement.GetProperty("Response");
        var status = root.GetProperty("SendStatusSet")[0];
        var code = status.GetProperty("Code").GetString();
        var message = status.GetProperty("Message").GetString();
        var requestId = root.GetProperty("RequestId").GetString();

        var success = string.Equals(code, "Ok", StringComparison.OrdinalIgnoreCase);
        return new SmsSendResult
        {
            Success = success,
            RequestId = requestId,
            Message = message
        };
    }

    private static string BuildPayload(SmsSendRequest request, TencentSmsOptions options)
    {
        var payload = new
        {
            PhoneNumberSet = new[] { request.PhoneNumber },
            SmsSdkAppId = options.SdkAppId,
            SignName = request.SignName ?? options.SignName,
            TemplateId = request.TemplateCode,
            TemplateParamSet = request.Variables.Values.ToArray()
        };

        return JsonSerializer.Serialize(payload);
    }

    private static string BuildCanonicalRequest(string payload, string host, bool useHttps)
    {
        _ = useHttps;
        var hashedPayload = HashSha256(payload);
        var canonicalHeaders = $"content-type:application/json\nhost:{host}\n";
        return $"POST\n/\n\n{canonicalHeaders}\ncontent-type;host\n{hashedPayload}";
    }

    private static string BuildStringToSign(string canonicalRequest, long timestamp, string date)
    {
        var hashedRequest = HashSha256(canonicalRequest);
        return $"TC3-HMAC-SHA256\n{timestamp}\n{date}/{Service}/tc3_request\n{hashedRequest}";
    }

    private static string Sign(string stringToSign, string secretKey, string date)
    {
        static byte[] HmacSha256(byte[] key, string msg) => new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(msg));

        var secretDate = HmacSha256(Encoding.UTF8.GetBytes($"TC3{secretKey}"), date);
        var secretService = HmacSha256(secretDate, Service);
        var secretSigning = HmacSha256(secretService, "tc3_request");
        var signatureBytes = new HMACSHA256(secretSigning).ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
        return Convert.ToHexString(signatureBytes).ToLowerInvariant();
    }

    private static string HashSha256(string raw)
    {
        var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
        var builder = new StringBuilder(bytes.Length * 2);
        foreach (var b in bytes)
        {
            builder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
        }

        return builder.ToString();
    }
}
