using Microsoft.AspNetCore.DataProtection;

namespace JobsMedical.Web.Services;

/// <summary>
/// A built-in, stateless math CAPTCHA (no Google reCAPTCHA — it's blocked in Iran). It renders a
/// simple "a + b = ?" question plus a tamper-proof token (the answer + timestamp, data-protected).
/// Verification unprotects the token, checks it hasn't expired, and compares the answer. Because
/// the answer is encrypted in the token (not guessable/forgeable) and no server session is needed,
/// it works across the load-balanced/containerized deploy with zero extra storage.
/// </summary>
public class CaptchaService
{
    private readonly IDataProtector _protector;
    private static readonly TimeSpan Ttl = TimeSpan.FromMinutes(10);

    public CaptchaService(IDataProtectionProvider dp) => _protector = dp.CreateProtector("hamkadr.captcha.v1");

    /// <summary>Returns the question to show (e.g. "۳ + ۵") and an opaque token for a hidden field.</summary>
    public (string Question, string Token) Create()
    {
        var a = Random.Shared.Next(1, 10);
        var b = Random.Shared.Next(1, 10);
        var token = _protector.Protect($"{a + b}|{DateTime.UtcNow.Ticks}");
        return ($"{ToPersian(a)} + {ToPersian(b)}", token);
    }

    public bool Verify(string? token, string? answer)
    {
        if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(answer)) return false;
        try
        {
            var parts = _protector.Unprotect(token).Split('|');
            if (parts.Length != 2) return false;
            if (long.TryParse(parts[1], out var ticks))
            {
                var issued = new DateTime(ticks, DateTimeKind.Utc);
                if (DateTime.UtcNow - issued > Ttl) return false;   // expired
            }
            return parts[0] == ToLatin(answer).Trim();
        }
        catch { return false; }   // tampered / undecryptable
    }

    private static string ToPersian(int n)
        => new string(n.ToString().Select(c => (char)('۰' + (c - '0'))).ToArray());

    private static string ToLatin(string s)
    {
        var chars = s.ToCharArray();
        for (var i = 0; i < chars.Length; i++)
        {
            if (chars[i] >= '۰' && chars[i] <= '۹') chars[i] = (char)('0' + (chars[i] - '۰'));
            else if (chars[i] >= '٠' && chars[i] <= '٩') chars[i] = (char)('0' + (chars[i] - '٠'));
        }
        return new string(chars);
    }
}
