using EonaCat.Json;
using Microsoft.Extensions.Logging;
using System;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Net.Http;

namespace EonaCat.Logger.EonaCatCoreLogger
{
    public sealed class TelegramLogger : ILogger, IDisposable
    {
        private readonly string _categoryName;
        private readonly TelegramLoggerOptions _options;
        private readonly HttpClient _httpClient;
        private readonly Channel<string> _logChannel;
        private readonly CancellationTokenSource _cts = new CancellationTokenSource();
        private readonly Task _processingTask;

        public event EventHandler<Exception> OnException;

        public TelegramLogger(string categoryName, TelegramLoggerOptions options)
        {
            _categoryName = categoryName;
            _options = options;

            _httpClient = new HttpClient();

            // Bounded channel to prevent memory leaks
            _logChannel = Channel.CreateBounded<string>(new BoundedChannelOptions(1000)
            {
                FullMode = BoundedChannelFullMode.DropOldest,
                SingleReader = true,
                SingleWriter = false
            });

            _processingTask = Task.Run(() => ProcessLogQueueAsync(_cts.Token));
        }

        public IDisposable BeginScope<TState>(TState state) => null;
        public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
            Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!IsEnabled(logLevel) || formatter == null)
            {
                return;
            }

            try
            {
                var message = $"[{DateTime.UtcNow:u}] [{logLevel}] {_categoryName}: {formatter(state, exception)}";
                if (exception != null)
                {
                    message += $"\nException: {exception}";
                }

                // Non-blocking, drop oldest if full
                _logChannel.Writer.TryWrite(message);
            }
            catch (Exception ex)
            {
                OnException?.Invoke(this, ex);
            }
        }

        private async Task ProcessLogQueueAsync(CancellationToken token)
        {
            try
            {
                await foreach (var message in _logChannel.Reader.ReadAllAsync(token))
                {
                    try
                    {
                        var url = $"https://api.telegram.org/bot{_options.BotToken}/sendMessage";
                        var payload = new { chat_id = _options.ChatId, text = message };
                        var content = new StringContent(JsonHelper.ToJson(payload), Encoding.UTF8, "application/json");

                        await _httpClient.PostAsync(url, content, token);
                    }
                    catch (Exception ex)
                    {
                        OnException?.Invoke(this, ex);
                    }
                }
            }
            catch (OperationCanceledException) { }
        }

        public void Dispose()
        {
            _cts.Cancel();
            _logChannel.Writer.Complete();

            try
            {
                _processingTask.Wait();
            }
            catch { /* ignore */ }

            _cts.Dispose();
            _httpClient.Dispose();
        }
    }
}
