using System.Collections.Concurrent;
using System.IO.Pipes;
using Microsoft.Extensions.Logging;
using TagashiFileHub.Communication.Abstractions;

namespace TagashiFileHub.Communication.IPC;

public class TagashiServer : ITagashiServer
{
    private readonly ConcurrentBag<StreamWriter> _clients = new();
    private readonly ILogger<TagashiServer>? _logger;

    public TagashiServer(ILogger<TagashiServer>? logger = null)
    {
        _logger = logger;
    }

    public async Task StartAsync(string pipeName, CancellationToken cancellationToken)
    {
        await Task.Run(async () =>
        {
            _logger?.LogInformation($"Starting up tagashi server for pipe \"{pipeName}\"");
            while (!cancellationToken.IsCancellationRequested)
            {
                await using var serverPipe = new NamedPipeServerStream(IpcUtils.PipeName(pipeName), PipeDirection.InOut,
                    1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
                await serverPipe.WaitForConnectionAsync(cancellationToken);

                using var reader = new StreamReader(serverPipe);
                await using var writer = new StreamWriter(serverPipe) { AutoFlush = true };

                _clients.Add(writer);

                await HandleClientInternalAsync(reader, writer, cancellationToken).ConfigureAwait(false);
            }

            _logger?.LogInformation($"Shut down tagashi server for pipe \"{pipeName}\"");
        }, cancellationToken).ConfigureAwait(false);
    }

    private async Task HandleClientInternalAsync(StreamReader reader, StreamWriter writer,
        CancellationToken cancellationToken)
    {
        try
        {
            _logger?.LogInformation("New client connected");

            while (!cancellationToken.IsCancellationRequested)
            {
                var message = await reader.ReadLineAsync(cancellationToken);
                if (message == null)
                    break;
            }
        }
        catch
        {
            // TODO
        }
        finally
        {
            _clients.TryTake(out writer!);
            writer.Close();
            _logger?.LogInformation("Client disconnected");
        }
    }
}