using System.Text.Json;
using JobsMedical.Web.Models;

namespace JobsMedical.Web.Services;

public interface ISmsSender
{
    /// <summary>Send the OTP code. Returns false if not configured or the gateway call fails.</summary>
    Task<bool> SendOtpAsync(string phone, string code, AppSetting settings, CancellationToken ct = default);
}

/// <summary>
/// Kavenegar SMS gateway (Iran). Prefers the verify/lookup API (a pre-approved OTP template, no
/// dedicated line needed); falls back to plain sms/send if only a sender line is configured.
/// Credentials live in AppSetting (admin panel), so no redeploy to set them.
/// </summary>
public class KavenegarSmsSender : ISmsSender
{
    private readonly IHttpClientFactory _http;
    private readonly ILogger<KavenegarSmsSender> _log;

    public KavenegarSmsSender(IHttpClientFactory http, ILogger<KavenegarSmsSender> log)
    {
        _http = http;
        _log = log;
    }

    public async Task<bool> SendOtpAsync(string phone, string code, AppSetting s, CancellationToken ct = default)
    {
        if (!s.SmsEnabled || string.IsNullOrWhiteSpace(s.SmsApiKey)) return false;

        // The API key is part of the URL path; clean it so a stray space/newline/slash
        // doesn't turn the request into a 404 (a malformed path). It is NOT a query value,
        // so don't percent-encode it — just strip whitespace/control chars.
        var apiKey = new string(s.SmsApiKey.Where(c => !char.IsWhiteSpace(c)).ToArray());
        if (apiKey.Contains('/'))
        {
            _log.LogWarning("Kavenegar API key looks malformed (contains '/'). Check the value in admin settings.");
            return false;
        }

        try
        {
            var client = _http.CreateClient("sms");
            client.Timeout = TimeSpan.FromSeconds(15);
            string url; string method;
            if (!string.IsNullOrWhiteSpace(s.SmsTemplate))
            {
                method = "verify/lookup";
                // verify/lookup: template contains %token → the code
                url = $"https://api.kavenegar.com/v1/{apiKey}/verify/lookup.json" +
                      $"?receptor={Uri.EscapeDataString(phone)}&token={Uri.EscapeDataString(code)}" +
                      $"&template={Uri.EscapeDataString(s.SmsTemplate.Trim())}";
            }
            else
            {
                method = "sms/send";
                var msg = $"کد ورود همکادر: {code}";
                url = $"https://api.kavenegar.com/v1/{apiKey}/sms/send.json" +
                      $"?receptor={Uri.EscapeDataString(phone)}&message={Uri.EscapeDataString(msg)}" +
                      (string.IsNullOrWhiteSpace(s.SmsSender) ? "" : $"&sender={Uri.EscapeDataString(s.SmsSender.Trim())}");
            }

            using var resp = await client.GetAsync(url, ct);
            var body = await resp.Content.ReadAsStringAsync(ct);
            var (apiStatus, apiMessage) = ParseKavenegar(body);

            // Kavenegar success = HTTP 2xx AND return.status == 200. A wrong key/template
            // often comes back as HTTP 200 with an error status, so check both.
            if (resp.IsSuccessStatusCode && apiStatus == 200) return true;

            string hint = (int)resp.StatusCode == 404
                ? " — HTTP 404 معمولاً یعنی کلید API نادرست/خراب است یا متد اشتباه (مسیر آدرس معتبر نیست)."
                : apiStatus switch
                {
                    401 or 402 => " — کلید API نامعتبر یا حساب غیرفعال است.",
                    407 or 431 or 432 => " — متن/تمپلیت تأیید نشده یا نام تمپلیت اشتباه است.",
                    411 or 412 => " — شماره گیرنده یا فرستنده نامعتبر است.",
                    418 => " — اعتبار حساب کافی نیست.",
                    _ => ""
                };
            _log.LogWarning(
                "Kavenegar OTP failed for {Phone} via {Method}: HTTP {Http}, apiStatus={ApiStatus}, message={Message}{Hint} | body: {Body}",
                phone, method, (int)resp.StatusCode, apiStatus, apiMessage, hint, Truncate(body, 300));
            return false;
        }
        catch (Exception ex)
        {
            _log.LogWarning(ex, "Kavenegar OTP send failed for {Phone}", phone);
            return false;
        }
    }

    /// <summary>Parse Kavenegar's { "return": { "status": N, "message": "…" } } envelope.</summary>
    private static (int? status, string? message) ParseKavenegar(string body)
    {
        if (string.IsNullOrWhiteSpace(body)) return (null, null);
        try
        {
            using var doc = JsonDocument.Parse(body);
            if (doc.RootElement.TryGetProperty("return", out var ret))
            {
                int? status = ret.TryGetProperty("status", out var st) && st.TryGetInt32(out var n) ? n : null;
                string? msg = ret.TryGetProperty("message", out var m) ? m.GetString() : null;
                return (status, msg);
            }
        }
        catch (JsonException) { /* not JSON (e.g. a 404 HTML page) — fall through */ }
        return (null, null);
    }

    private static string Truncate(string s, int max) =>
        string.IsNullOrEmpty(s) ? "" : (s.Length <= max ? s : s[..max] + "…");
}
