using Kavenegar;
using Kavenegar.Exceptions;
using Meezi.Core.Interfaces;
using Meezi.Infrastructure.Services.Platform;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Meezi.Infrastructure.ExternalServices;

/// <summary>
/// Kavenegar SMS gateway implementation using the official Kavenegar .NET SDK.
/// Reads config from DB (via IPlatformRuntimeConfig) first, then falls back
/// to IConfiguration ("Kavenegar:ApiKey", "Kavenegar:SenderNumber", etc.).
/// </summary>
public class KavenegarSmsService : ISmsService
{
    // ── DB config keys ────────────────────────────────────────────────────────
    private const string DbKeyApiKey      = "integrations.kavenegar.apiKey";
    private const string DbKeyEnabled     = "integrations.kavenegar.enabled";
    private const string DbKeySender      = "integrations.kavenegar.senderNumber";
    private const string DbKeyOtpTemplate = "integrations.kavenegar.otpTemplate";

    private const int MaxBatchSize = 200;

    private readonly IConfiguration _configuration;
    private readonly IPlatformRuntimeConfig _platform;
    private readonly IHostEnvironment _environment;
    private readonly ILogger<KavenegarSmsService> _logger;

    public KavenegarSmsService(
        IConfiguration configuration,
        IPlatformRuntimeConfig platform,
        IHostEnvironment environment,
        ILogger<KavenegarSmsService> logger)
    {
        _configuration = configuration;
        _platform = platform;
        _environment = environment;
        _logger = logger;
    }

    // ── Public interface ──────────────────────────────────────────────────────

    public async Task SendOtpAsync(string phone, string otp, CancellationToken cancellationToken = default)
    {
        if (_environment.IsDevelopment())
        {
            _logger.LogWarning("[DEV OTP] {Phone}: {Otp}", phone, otp);
            return; // Skip real SMS in development — read OTP from logs
        }

        var (apiKey, _, template) = await GetConfigAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(apiKey))
        {
            _logger.LogInformation("Kavenegar not configured — OTP for {Phone} not sent", phone);
            return;
        }

        var receptor = NormalizePhone(phone);
        await RunSdkAsync(apiKey, api =>
        {
            api.VerifyLookup(receptor, otp, null, null, template);
        }, "OTP");
    }

    public async Task SendMessageAsync(string phone, string message, CancellationToken cancellationToken = default)
    {
        var (apiKey, sender, _) = await GetConfigAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(apiKey))
        {
            _logger.LogInformation("Kavenegar not configured — SMS to {Phone}: {Message}", phone, message);
            return;
        }

        var receptor = NormalizePhone(phone);
        await RunSdkAsync(apiKey, api =>
        {
            api.Send(sender, receptor, message);
        }, "Send");
    }

    public async Task<BulkSendResult> SendBulkAsync(
        IReadOnlyList<string> phones,
        string message,
        CancellationToken cancellationToken = default)
    {
        if (phones.Count == 0) return new BulkSendResult(0, 0);

        var (apiKey, sender, _) = await GetConfigAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(apiKey))
        {
            _logger.LogInformation("Kavenegar not configured — bulk SMS skipped ({Count} recipients)", phones.Count);
            return new BulkSendResult(0, phones.Count);
        }

        int sent = 0, failed = 0;

        foreach (var batch in phones.Chunk(MaxBatchSize))
        {
            try
            {
                var receptors = batch.Select(NormalizePhone).ToList();
                await RunSdkAsync(apiKey, api =>
                {
                    api.Send(sender, receptors, message);
                }, "BulkSend");

                sent += batch.Length;
                _logger.LogInformation("Kavenegar bulk batch: {Count} sent", batch.Length);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Kavenegar bulk batch failed ({Count} recipients)", batch.Length);
                failed += batch.Length;
            }
        }

        return new BulkSendResult(sent, failed);
    }

    public async Task<KavenegarAccountInfo?> GetAccountInfoAsync(CancellationToken cancellationToken = default)
    {
        var (apiKey, _, _) = await GetConfigAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(apiKey)) return null;

        try
        {
            return await Task.Run(() =>
            {
                var api = new KavenegarApi(apiKey);
                var info = api.AccountInfo();
                return new KavenegarAccountInfo(info.RemainCredit, info.Type ?? "master");
            }, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to fetch Kavenegar account info");
            return null;
        }
    }

    // ── SDK runner ────────────────────────────────────────────────────────────

    /// <summary>
    /// Runs a synchronous Kavenegar SDK call on the thread pool.
    /// Translates SDK exceptions to logged InvalidOperationException.
    /// </summary>
    private async Task RunSdkAsync(string apiKey, Action<KavenegarApi> action, string operation)
    {
        await Task.Run(() =>
        {
            try
            {
                var api = new KavenegarApi(apiKey);
                action(api);
            }
            catch (ApiException ex)
            {
                _logger.LogWarning(
                    "Kavenegar {Op} API error {Code}: {Message}",
                    operation, ex.Code, ex.Message);
                throw new InvalidOperationException(
                    $"Kavenegar {operation} failed (code {ex.Code}): {ex.Message}", ex);
            }
            catch (HttpException ex)
            {
                _logger.LogWarning(
                    "Kavenegar {Op} HTTP error {Code}: {Message}",
                    operation, ex.Code, ex.Message);
                throw new InvalidOperationException(
                    $"Kavenegar {operation} HTTP error (code {ex.Code}): {ex.Message}", ex);
            }
        });
    }

    // ── Helpers ───────────────────────────────────────────────────────────────

    // Strip leading 0 from Iranian mobile numbers (09xxxxxxxxx → 9xxxxxxxxx)
    private static string NormalizePhone(string phone)
    {
        var p = phone.Trim();
        if (p.StartsWith("0") && p.Length == 11) return p[1..];
        return p;
    }

    private async Task<(string? ApiKey, string Sender, string OtpTemplate)> GetConfigAsync(CancellationToken ct)
    {
        var enabled = await _platform.GetAsync(DbKeyEnabled, ct);
        // If explicitly disabled in DB, short-circuit
        if (enabled is "false")
            return (null, string.Empty, "verify");

        var apiKey = await _platform.GetAsync(DbKeyApiKey, ct);
        if (string.IsNullOrWhiteSpace(apiKey))
            apiKey = _configuration["Kavenegar:ApiKey"];

        var sender = await _platform.GetAsync(DbKeySender, ct);
        if (string.IsNullOrWhiteSpace(sender))
            sender = _configuration["Kavenegar:SenderNumber"] ?? string.Empty;

        var template = await _platform.GetAsync(DbKeyOtpTemplate, ct);
        if (string.IsNullOrWhiteSpace(template))
            template = _configuration["Kavenegar:OtpTemplate"] ?? "verify";

        return (apiKey, sender, template);
    }
}
