using System.Net;
using System.Net.Http;
using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Core;
using Serilog.Events;

namespace KArtSell.Host.Infrastructure;

/// <summary>
/// Async Telegram sink for Serilog: non-blocking queue + exponential backoff
/// Processes ERROR/FATAL logs via background channel, prevents logging from blocking
/// </summary>
public sealed class TelegramSinkAsync : ILogEventSink, IAsyncDisposable
{
    private readonly string _telegramBotToken;
    private readonly string _telegramChatId;
    private readonly HttpClient _httpClient;
    private readonly Channel<LogEvent> _queue;
    private readonly Task _backgroundTask;
    private readonly CancellationTokenSource _cts;

    public TelegramSinkAsync(string telegramBotToken, string telegramChatId, HttpClient? httpClient = null)
    {
        _telegramBotToken = telegramBotToken;
        _telegramChatId = telegramChatId;
        _httpClient = httpClient ?? new HttpClient();
        _queue = Channel.CreateUnbounded<LogEvent>();
        _cts = new CancellationTokenSource();
        _backgroundTask = ProcessQueueAsync(_cts.Token);
    }

    public void Emit(LogEvent logEvent)
    {
        // Only queue ERROR and FATAL
        if (logEvent.Level < LogEventLevel.Error)
            return;

        // Non-blocking: enqueue only
        _queue.Writer.TryWrite(logEvent);
    }

    private async Task ProcessQueueAsync(CancellationToken cancellationToken)
    {
        try
        {
            await foreach (var logEvent in _queue.Reader.ReadAllAsync(cancellationToken))
            {
                // Rate limit: 100ms spacer between messages
                await Task.Delay(100, cancellationToken);

                // Retry: 3x with exponential backoff
                var backoffMs = 100;
                for (int attempt = 0; attempt < 3; attempt++)
                {
                    try
                    {
                        await SendTelegramMessageAsync(logEvent, cancellationToken);
                        break;
                    }
                    catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
                    {
                        if (attempt < 2)
                        {
                            backoffMs *= 2;
                            await Task.Delay(backoffMs, cancellationToken);
                        }
                    }
                    catch
                    {
                        // Silently fail to prevent logging loops
                        if (attempt == 2) break;
                    }
                }
            }
        }
        catch (OperationCanceledException)
        {
            // Expected during shutdown
        }
    }

    private async Task SendTelegramMessageAsync(LogEvent logEvent, CancellationToken cancellationToken)
    {
        var emoji = logEvent.Level == LogEventLevel.Fatal ? "🔴" : "⚠️";
        var levelName = logEvent.Level.ToString().ToUpperInvariant();

        var message = $@"{emoji} *{levelName}* - K-ArtSell Aegis

{logEvent.MessageTemplate.Render(logEvent.Properties)}

_Timestamp: {logEvent.Timestamp:O}_";

        if (logEvent.Exception != null)
        {
            message += $@"

```
{logEvent.Exception.GetType().Name}: {logEvent.Exception.Message}
```";
        }

        var url = $"https://api.telegram.org/bot{_telegramBotToken}/sendMessage";
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            { "chat_id", _telegramChatId },
            { "text", message },
            { "parse_mode", "Markdown" }
        });

        var response = await _httpClient.PostAsync(url, content, cancellationToken);
        response.EnsureSuccessStatusCode();
    }

    public async ValueTask DisposeAsync()
    {
        _queue.Writer.Complete();
        _cts.Cancel();
        try
        {
            await _backgroundTask;
        }
        catch (OperationCanceledException) { }
        _cts.Dispose();
    }
}
