using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Channels;
using Inspectron.Epson.PrintServer.ConfigurationSources;
using Inspectron.Epson.Queue;
using Microsoft.Extensions.Logging;

namespace Inspectron.Epson.PrintServer.JobSources;

public class HttpPollingPrintJobSource : IPrintJobSource, IDisposable
{
    private readonly EpsonPrintServiceConfiguration _groupConfiguration;
    private readonly ILogger _logger;
    private readonly HttpClient _httpClient;
    private readonly Channel<PrintJob> _printJobChannel = Channel.CreateUnbounded<PrintJob>();
    private readonly CancellationTokenSource _pollingCts = new();
    private readonly TimeSpan _pollingInterval;

    public HttpPollingPrintJobSource(
        EpsonPrintServiceConfiguration groupConfiguration,
        ILogger logger
)
    {
        _groupConfiguration = groupConfiguration;
        _logger = logger;
        _pollingInterval = TimeSpan.FromSeconds(3);

        _httpClient = new HttpClient
        {
            BaseAddress = new Uri(groupConfiguration.ApiUrl?.TrimEnd('/') + "/")
        };
        _httpClient.DefaultRequestHeaders.Add("X-Printer-Server-Key", groupConfiguration.ApiKey);

    }

    private async Task StartPollingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Starting HTTP polling for print jobs at {BaseUrl}", _httpClient.BaseAddress);

        while (!cancellationToken.IsCancellationRequested)
        {
            try
            {
                await PollForJobsAsync(cancellationToken);
            }
            catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
            {
                break;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error polling for print jobs. Retrying in {Interval}...", _pollingInterval);
            }

            try
            {
                await Task.Delay(_pollingInterval, cancellationToken);
            }
            catch (OperationCanceledException)
            {
                break;
            }
        }

        _logger.LogInformation("HTTP polling stopped.");
    }

    private async Task PollForJobsAsync(CancellationToken cancellationToken)
    {
        var requestUrl = $"/api/printerServer/jobs";

        var response = await _httpClient.GetAsync(requestUrl, cancellationToken);

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning("Failed to fetch jobs. Status: {StatusCode}", response.StatusCode);
            return;
        }

        var jobs = await response.Content.ReadFromJsonAsync<List<PrintJobDto>>(cancellationToken: cancellationToken);

        if (jobs == null || jobs.Count == 0)
        {
            return;
        }

        _logger.LogInformation("Received {Count} print job(s) from HTTP endpoint.", jobs.Count);

        foreach (var job in jobs)
        {
            _logger.LogInformation("Queuing print job: {PrintJob}", JsonSerializer.Serialize(job));
            _printJobChannel.Writer.TryWrite(new PrintJob
            {
                JobId = job.JobId,
                IP = job.PrinterIp,
                Document = new SignalRPrintJobSource.PrintJobFromSignalR
                {
                    JobId = job.JobId,
                    PrinterIp = job.PrinterIp,
                    LogoUrl = job.LogoUrl,
                    ReceiptType = job.ReceiptType,
                    Content = job.Content
                }
            });
        }
    }

    public Task StartAsync()
    {
        return StartPollingAsync(_pollingCts.Token);
    }

    public async Task<PrintJob> GetNextJobAsync(CancellationToken cancellationToken)
    {
        return await _printJobChannel.Reader.ReadAsync(cancellationToken);
    }

    public void Dispose()
    {
        _pollingCts.Cancel();
        _pollingCts.Dispose();
        _httpClient.Dispose();
    }

    public class PrintJobDto
    {
        [JsonPropertyName("jobId")]
        public string JobId { get; set; } = "";

        [JsonPropertyName("printerIp")]
        public string PrinterIp { get; set; } = "";

        [JsonPropertyName("logoUrl")]
        public string? LogoUrl { get; set; }

        [JsonPropertyName("receiptType")]
        public int ReceiptType { get; set; }

        [JsonPropertyName("content")]
        public string Content { get; set; } = "";
    }
}
