using Api.Services.Contracts.Models;
using Api.Services.Contracts;
using Microsoft.Extensions.Options;
using Models.Settings;

namespace Api.Services
{
    /// <summary>
    /// Verifies reCAPTCHA v2/v3 tokens by calling the Google site-verify API.
    /// </summary>
    public sealed class RecaptchaVerifier : ICaptchaVerifier
    {
        private readonly HttpClient _http;
        private readonly CaptchaSettings _opt;
        private readonly ILogger<RecaptchaVerifier> _log;

        public RecaptchaVerifier(HttpClient http, IOptions<CaptchaSettings> options, ILogger<RecaptchaVerifier> log)
        {
            _http = http;
            _opt = options.Value;
            _log = log;
        }

        /// <inheritdoc />
        public async Task<CaptchaVerdictModel> VerifyAsync(string token, string? userIp, string? expectedAction, CancellationToken ct)
        {
            _log.LogDebug("Verifying captcha token for IP {Ip}", userIp ?? "unknown");

            if (string.IsNullOrWhiteSpace(_opt.SecretKey))
            {
                _log.LogWarning("Captcha verification attempted but SecretKey is not configured");
                return new CaptchaVerdictModel(false, "Captcha not configured", null);
            }

            var form = new Dictionary<string, string>
            {
                ["secret"] = _opt.SecretKey,
                ["response"] = token
            };
            if (!string.IsNullOrWhiteSpace(userIp))
                form["remoteip"] = userIp;

            using var resp = await _http.PostAsync(
                "https://www.google.com/recaptcha/api/siteverify",
                new FormUrlEncodedContent(form),
                ct
            );

            if (!resp.IsSuccessStatusCode)
            {
                _log.LogWarning("Captcha HTTP request failed with status {StatusCode} for IP {Ip}",
                    (int)resp.StatusCode, userIp ?? "unknown");
                return new CaptchaVerdictModel(false, $"Captcha HTTP {(int)resp.StatusCode}", null);
            }

            var data = await resp.Content.ReadFromJsonAsync<RecaptchaResponse>(cancellationToken: ct);
            if (data is null)
            {
                _log.LogError("Failed to parse captcha response for IP {Ip}", userIp ?? "unknown");
                return new CaptchaVerdictModel(false, "Captcha parse error", null);
            }

            if (!data.success)
            {
                _log.LogWarning("Captcha verification failed for IP {Ip}. Score={Score}", 
                    userIp ?? "unknown", data.score);
                return new CaptchaVerdictModel(false, "Captcha failed", data.score);
            }

            // v3 score check (score is typically null for v2)
            if (data.score is double score && score < _opt.MinimumScore)
            {
                _log.LogWarning("Captcha score {Score} below minimum {MinScore} for IP {Ip}",
                    score, _opt.MinimumScore, userIp ?? "unknown");
                return new CaptchaVerdictModel(false, "Captcha score too low", score);
            }

            // Optional strictness (usually v3): action/hostname checks
            var actionToCheck = !string.IsNullOrWhiteSpace(expectedAction) ? expectedAction : _opt.ExpectedAction;
            if (!string.IsNullOrWhiteSpace(actionToCheck) &&
                !string.Equals(actionToCheck, data.action, StringComparison.Ordinal))
            {
                _log.LogWarning("Captcha action mismatch. Expected={Expected}, Actual={Actual}, IP={Ip}",
                    actionToCheck, data.action, userIp ?? "unknown");
                return new CaptchaVerdictModel(false, "Captcha action mismatch", data.score);
            }

            if (!string.IsNullOrWhiteSpace(_opt.ExpectedHostname) &&
                !string.Equals(_opt.ExpectedHostname, data.hostname, StringComparison.OrdinalIgnoreCase))
            {
                _log.LogWarning("Captcha hostname mismatch. Expected={Expected}, Actual={Actual}, IP={Ip}",
                    _opt.ExpectedHostname, data.hostname, userIp ?? "unknown");
                return new CaptchaVerdictModel(false, "Captcha hostname mismatch", data.score);
            }

            _log.LogInformation("Captcha verified successfully for IP {Ip}. Score={Score}", 
                userIp ?? "unknown", data.score);
            return new CaptchaVerdictModel(true, null, data.score);
        }

        private sealed class RecaptchaResponse
        {
            public bool success { get; set; }
            public double? score { get; set; }      // v3
            public string? action { get; set; }     // v3
            public string? hostname { get; set; }
            public DateTimeOffset? challenge_ts { get; set; }
        }
    }
}
