#nullable enable
using System.Text;
using System.Text.Json;
using Baya.Application.Contracts.Common;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Baya.Infrastructure.CrossCutting.Seams.Real;

/// <summary>
/// <see cref="ISmsSender"/> over the standalone <b>Telegram OTP relay</b> (<c>telegram-otp-bot/</c>), selected by
/// <c>Seams:Sms:Provider = telegram</c>. Its two POST routes mirror this contract one-for-one, so each method is a
/// single JSON call: <c>POST /send_otp</c> and <c>POST /send</c>, authenticated with the shared
/// <c>X-Api-Key</c> secret.
///
/// <para><b>Broadcast, not per-user routing.</b> The relay sends every message to a fixed list of Telegram chat
/// ids — every recipient reads every code, whichever phone requested it. That makes it a shared-inbox channel for
/// a small trusted group, not an SMS gateway: it is the deliberate OTP rail for the pre-launch demo deployment
/// (no Iranian gateway contract yet), and must be replaced by <see cref="KavenegarSmsSender"/> before real
/// customers sign up (see <see cref="TelegramOptions"/>).</para>
///
/// <para><b>The OTP is never logged</b> — only the phone tail and the relay's HTTP outcome, exactly like
/// <see cref="KavenegarSmsSender"/>. A non-2xx (the relay answers <c>502</c> when <i>no</i> recipient got the
/// message) or an <c>ok != true</c> body is a delivery failure and throws, so <c>RequestOtpCommand</c> reports a
/// real send failure instead of silently "succeeding" on an undelivered code.</para>
/// </summary>
public sealed class TelegramSmsSender(
    HttpClient httpClient,
    IOptions<SeamOptions> options,
    ILogger<TelegramSmsSender> logger) : ISmsSender
{
    /// <summary>The example key published in <c>telegram-otp-bot/.env.example</c> and its README. Anyone
    /// reading the repo knows it, so it is a documentation sample, not a secret — treated as "not configured"
    /// so a deployment can never quietly authenticate the OTP rail with a publicly-known value.</summary>
    private const string SecretPlaceholder = "ab8984974bc1fe5ce514d0fd74f71c8738b3aed92a7e4d86";

    private readonly TelegramOptions _options = options.Value.Sms.Telegram;

    public Task SendOtpAsync(string phone, string code, CancellationToken cancellationToken = default)
        => PostAsync("send_otp", new { phone, code }, phone, cancellationToken);

    public Task SendAsync(string phone, string message, CancellationToken cancellationToken = default)
        => PostAsync("send", new { phone, message }, phone, cancellationToken);

    private async Task PostAsync(string path, object payload, string phone, CancellationToken cancellationToken)
    {
        // Fail with the config key rather than sending an unauthenticated request the relay answers with a bare
        // 401 — the cause of that 401 is invisible from this side.
        var apiKey = _options.ApiKey;
        if (string.IsNullOrWhiteSpace(apiKey) || apiKey == SecretPlaceholder)
        {
            throw new InvalidOperationException(
                "Seams:Sms:Telegram:ApiKey is not configured (unset, or still the published example key). Set it " +
                "(appsettings or environment) to the same value as the relay's API_KEY, or select another " +
                "Seams:Sms:Provider.");
        }

        using var request = new HttpRequestMessage(HttpMethod.Post, path)
        {
            // Snake-case keys (phone/code/message) are exactly what the relay reads off the body.
            Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"),
        };
        request.Headers.Add("X-Api-Key", apiKey);

        using var response = await httpClient.SendAsync(request, cancellationToken);
        var body = await response.Content.ReadAsStringAsync(cancellationToken);

        if (!response.IsSuccessStatusCode || !IsDelivered(body))
        {
            logger.LogWarning(
                "Telegram OTP relay delivery failed for phone ending {PhoneTail} — http {Http}",
                Tail(phone), (int)response.StatusCode);
            throw new InvalidOperationException(
                $"Telegram OTP relay delivery failed (http {(int)response.StatusCode}).");
        }

        logger.LogInformation("Telegram OTP relay accepted the message for phone ending {PhoneTail}", Tail(phone));
    }

    /// <summary>A 2xx still carries the per-recipient outcome in <c>ok</c>; anything but <c>true</c> is a failure.</summary>
    private static bool IsDelivered(string body)
    {
        try
        {
            using var doc = JsonDocument.Parse(body);
            return doc.RootElement.TryGetProperty("ok", out var ok) && ok.ValueKind == JsonValueKind.True;
        }
        catch (JsonException)
        {
            return false;
        }
    }

    private static string Tail(string phone) =>
        string.IsNullOrEmpty(phone) ? "????" : phone[^Math.Min(4, phone.Length)..];
}
