using Inspectron.Epson.PrintServer.Hooks;
using Inspectron.Epson.PrintServer.PrintServices;
using Inspectron.Epson.Queue;
using Microsoft.Extensions.Logging;

namespace Inspectron.Epson.PrintServer;

public class PrintLoop
{
    private readonly global::Inspectron.Epson.Queue.PrintServer _printServer;
    private readonly IPrintService _printService;
    private readonly IPrintJobSource _jobSource;
    private readonly IReadOnlyList<PrintJobArrivedHandler> _arrivedHandlers;

    private readonly ILogger _logger;
    private CancellationTokenSource? _cancellationSource;
    private CancellationToken _cancellationToken;

    public PrintLoop(global::Inspectron.Epson.Queue.PrintServer printServer, IPrintService printService, IPrintJobSource jobSource, ILogger logger, IEnumerable<PrintJobArrivedHandler>? arrivedHandlers = null)
    {
        _printServer = printServer;
        _printService = printService;
        _jobSource = jobSource;
        _arrivedHandlers = arrivedHandlers?.ToList() ?? (IReadOnlyList<PrintJobArrivedHandler>)Array.Empty<PrintJobArrivedHandler>();

        _logger = logger;
    }

    public Task StartAsync()
    {
        _cancellationSource = new CancellationTokenSource();
        _cancellationToken = _cancellationSource.Token;
        _= Task.Run(Loop);
        return Task.CompletedTask;
    }
    public async Task Loop()
    {
        while (!_cancellationSource!.Token.IsCancellationRequested)
        {
            var job = await _jobSource.GetNextJobAsync(_cancellationToken);

            if (_arrivedHandlers.Count > 0)
            {
                var ctx = new PrintJobArrivedContext(job);
                foreach (var handler in _arrivedHandlers)
                {
                    try
                    {
                        await handler(ctx, _cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Print job arrived hook threw (job {JobId})", job.JobId);
                    }
                }
            }

            _printServer.SubmitJob(job.IP, job);
        }
    }

    public Task StopAsync()
    {
        _cancellationSource!.Cancel();
        return Task.CompletedTask;
    }
}