using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;

namespace Inspectron.Epson.Queue;

public class PrintServer
{
    private readonly IPrintService _printService;
    private readonly ILogger _logger;
    private readonly IJobStatusReporter _statusReporter;
    private readonly ConcurrentDictionary<string, PrinterQueue> _printerQueues;
    private readonly SemaphoreSlim _printDiscoveryLock = new(1, 1);
    private int _readerCount = 0;
    private readonly object _readerCountLock = new();

    public PrintServer(IPrintService printService, ILogger logger, IJobStatusReporter statusReporter)
    {
        _printService = printService;
        _logger = logger;
        _statusReporter = statusReporter;
        _printerQueues = new ConcurrentDictionary<string, PrinterQueue>();

    }

    /// <summary>
    /// Acquires a read lock for print operations. Multiple print operations can run concurrently.
    /// </summary>
    public void EnterPrintLock()
    {
        _logger.LogDebug("Entering print lock");
        lock (_readerCountLock)
        {
            _readerCount++;
            if (_readerCount == 1)
            {
                _printDiscoveryLock.Wait();
            }
        }
        _logger.LogDebug("Entered print lock");

    }

    /// <summary>
    /// Releases the read lock after a print operation completes.
    /// </summary>
    public void ExitPrintLock()
    {
        _logger.LogDebug("Exiting print lock");
        lock (_readerCountLock)
        {
            _readerCount--;
            if (_readerCount == 0)
            {
                _printDiscoveryLock.Release();
            }
        }
        _logger.LogDebug("Exited print lock");
    }

    /// <summary>
    /// Acquires a write lock for discovery operations. This blocks until all print operations complete
    /// and prevents new print operations from starting.
    /// </summary>
    public async Task EnterDiscoveryLockAsync()
    {
        _logger.LogDebug("Entering discovery lock");
        await _printDiscoveryLock.WaitAsync();
        _logger.LogDebug("Entered discovery lock");
    }

    /// <summary>
    /// Releases the write lock after discovery completes.
    /// </summary>
    public void ExitDiscoveryLock()
    {
        _logger.LogDebug("Exiting discovery lock");
        _printDiscoveryLock.Release();
        _logger.LogDebug("Exited discovery lock");
    }

    public void RegisterPrinter(string printerIp)
    {
        var queue = new PrinterQueue(printerIp, _printService, _logger, this, _statusReporter);
        if (_printerQueues.TryAdd(printerIp, queue))
        {
            queue.Start();
            Console.WriteLine($"Printer {printerIp} registered");
        }
    }

    public void UnregisterPrinter(string printerIp)
    {
        if (_printerQueues.TryRemove(printerIp, out var queue))
        {
            queue.StopAsync().Wait();
            Console.WriteLine($"Printer {printerIp} unregistered");
        }
    }

    public void SubmitJob(string printerIp, PrintJob job)
    {
        if (_printerQueues.TryGetValue(printerIp, out var queue))
        {
            queue.Enqueue(job);
        }
        else
        {
            _logger.LogWarning( $"Printer {printerIp} not found. Job cannot be submitted.");
        }
    }

    public Dictionary<string, int> GetQueueStatus()
    {
        var status = new Dictionary<string, int>();
        foreach (var kvp in _printerQueues)
        {
            status[kvp.Key] = kvp.Value.QueueLength;
        }
        return status;
    }

    public async Task ShutdownAsync()
    {
        var stopTasks = _printerQueues.Values.Select(q => q.StopAsync());
        await Task.WhenAll(stopTasks);
        Console.WriteLine("Print server shut down");
    }
}