using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text;
using TakeoutSaaS.Application.Sms.Abstractions;
using TakeoutSaaS.Application.Sms.Contracts;
using TakeoutSaaS.Application.Sms.Options;
using TakeoutSaaS.Module.Sms.Abstractions;
using TakeoutSaaS.Module.Sms.Models;
using TakeoutSaaS.Module.Sms.Options;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.Sms.Services;

/// <summary>
/// 短信验证码服务实现。
/// </summary>
public sealed class VerificationCodeService(
    ISmsSenderResolver senderResolver,
    IOptionsMonitor<SmsOptions> smsOptionsMonitor,
    IOptionsMonitor<VerificationCodeOptions> codeOptionsMonitor,
    ITenantProvider tenantProvider,
    IDistributedCache cache,
    ILogger<VerificationCodeService> logger) : IVerificationCodeService
{
    /// <inheritdoc />
    public async Task<SendVerificationCodeResponse> SendAsync(SendVerificationCodeRequest request, CancellationToken cancellationToken = default)
    {
        // 1. 参数校验
        if (string.IsNullOrWhiteSpace(request.PhoneNumber))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "手机号不能为空");
        }

        if (string.IsNullOrWhiteSpace(request.Scene))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "场景不能为空");
        }

        // 2. 解析模板与缓存键
        var smsOptions = smsOptionsMonitor.CurrentValue;
        var codeOptions = codeOptionsMonitor.CurrentValue;
        var templateCode = ResolveTemplate(request.Scene, smsOptions);
        var phone = NormalizePhoneNumber(request.PhoneNumber);
        var tenantKey = tenantProvider.GetCurrentTenantId() == 0 ? "platform" : tenantProvider.GetCurrentTenantId().ToString();
        var cacheKey = $"{codeOptions.CachePrefix}:{tenantKey}:{request.Scene}:{phone}";
        var cooldownKey = $"{cacheKey}:cooldown";

        // 3. 检查冷却期
        await EnsureCooldownAsync(cooldownKey, codeOptions.CooldownSeconds, cancellationToken).ConfigureAwait(false);

        // 4. 生成验证码并发送短信
        var code = GenerateCode(codeOptions.CodeLength);
        var variables = new Dictionary<string, string> { { "code", code } };
        var sender = senderResolver.Resolve(request.Provider);

        var smsRequest = new SmsSendRequest(phone, templateCode, variables, smsOptions.DefaultSignName);
        var smsResult = await sender.SendAsync(smsRequest, cancellationToken).ConfigureAwait(false);
        if (!smsResult.Success)
        {
            throw new BusinessException(ErrorCodes.InternalServerError, $"短信发送失败：{smsResult.Message}");
        }

        // 5. 写入验证码与冷却缓存
        var expiresAt = DateTimeOffset.UtcNow.AddMinutes(codeOptions.ExpireMinutes);
        await cache.SetStringAsync(cacheKey, code, new DistributedCacheEntryOptions
        {
            AbsoluteExpiration = expiresAt
        }, cancellationToken).ConfigureAwait(false);

        await cache.SetStringAsync(cooldownKey, "1", new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(codeOptions.CooldownSeconds)
        }, cancellationToken).ConfigureAwait(false);

        logger.LogInformation("发送验证码成功，Phone:{Phone} Scene:{Scene} Tenant:{Tenant}", phone, request.Scene, tenantKey);
        return new SendVerificationCodeResponse
        {
            ExpiresAt = expiresAt,
            RequestId = smsResult.RequestId
        };
    }

    /// <inheritdoc />
    public async Task<bool> VerifyAsync(VerifyVerificationCodeRequest request, CancellationToken cancellationToken = default)
    {
        // 1. 基础校验
        if (string.IsNullOrWhiteSpace(request.Code))
        {
            return false;
        }

        // 2. 读取验证码
        var codeOptions = codeOptionsMonitor.CurrentValue;
        var phone = NormalizePhoneNumber(request.PhoneNumber);
        var tenantKey = tenantProvider.GetCurrentTenantId() == 0 ? "platform" : tenantProvider.GetCurrentTenantId().ToString();
        var cacheKey = $"{codeOptions.CachePrefix}:{tenantKey}:{request.Scene}:{phone}";

        var cachedCode = await cache.GetStringAsync(cacheKey, cancellationToken).ConfigureAwait(false);
        if (string.IsNullOrWhiteSpace(cachedCode))
        {
            return false;
        }

        // 3. 比对成功后清除缓存
        var success = string.Equals(cachedCode, request.Code, StringComparison.Ordinal);
        if (success)
        {
            await cache.RemoveAsync(cacheKey, cancellationToken).ConfigureAwait(false);
        }

        return success;
    }

    private static string ResolveTemplate(string scene, SmsOptions options)
    {
        if (options.SceneTemplates.TryGetValue(scene, out var template) && !string.IsNullOrWhiteSpace(template))
        {
            return template;
        }

        throw new BusinessException(ErrorCodes.BadRequest, $"未配置场景 {scene} 的短信模板");
    }

    private static string NormalizePhoneNumber(string phone)
    {
        var trimmed = phone.Trim();
        return trimmed.StartsWith("+", StringComparison.Ordinal) ? trimmed : $"+86{trimmed}";
    }

    private static string GenerateCode(int length)
    {
        var buffer = new byte[length];
        RandomNumberGenerator.Fill(buffer);
        var builder = new StringBuilder(length);
        foreach (var b in buffer)
        {
            builder.Append((b % 10).ToString());
        }

        return builder.ToString()[..length];
    }

    private async Task EnsureCooldownAsync(string cooldownKey, int cooldownSeconds, CancellationToken cancellationToken)
    {
        var existing = await cache.GetStringAsync(cooldownKey, cancellationToken).ConfigureAwait(false);
        if (!string.IsNullOrEmpty(existing))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "请求过于频繁，请稍后再试");
        }
    }
}
