using System.Collections.Concurrent;
using System.Diagnostics;
using Universal;

namespace EEMSClientCore;

public class ClientProvider(IClientCaller clientCaller, IClientBaseProvider baseProvider) : IClientProvider
{
    private static readonly ConcurrentDictionary<FileType, MemoryStream> _streamBuffer = [];
    public static readonly ConcurrentDictionary<FileType, string> _savePath = [];

    //Receive Files from Server
    public Task<bool> PushFile(Guid guid, FileType fileType, byte[] buffer, int current, int total)
    {
        if (baseProvider is null)
            return Task.FromResult(false);

        if (!_streamBuffer.TryGetValue(fileType, out MemoryStream? stream) || stream is null)
        {
            if (current != 1)
                return Task.FromResult(false);

            if (!baseProvider.AllowReceiveFile())
                return Task.FromResult(false);

            baseProvider.StartFileReceiveNotify(fileType);
            stream = new();
            _streamBuffer[fileType] = stream;
            Timer timer = new(FileCleanerTimerCallback, fileType, 600000, Timeout.Infinite);
        }

        stream.Write(buffer);

        if (current != total)
            return Task.FromResult(true);

        string? tempPath = fileType switch
        {
            FileType.Recipe => baseProvider.RecipePath,
            FileType.Config => baseProvider.ConfigPath,
            _ => "Undefined"
        };
        tempPath ??= "Undefined";

        string path = Path.Combine(tempPath, "Receive");
        if (!Compressor.DecompressZipFile(new(path), stream))
            return Task.FromResult(false);

        if (_streamBuffer.TryRemove(fileType, out MemoryStream? memoryStream) && memoryStream is not null)
            memoryStream.Dispose();
        baseProvider?.FileReceivedNotify(fileType, path);
        return Task.FromResult(true);
    }

    public Task<bool> RequestFile(Guid guid, FileType fileType)
    {
        Debug.WriteLine($"RequestFile {guid} {fileType}");

        string? filePath = fileType switch
        {
            FileType.Config => baseProvider.ConfigPath,
            FileType.Recipe => baseProvider.RecipePath,
            _ => default
        };

        if (string.IsNullOrEmpty(filePath))
            return Task.FromResult(false);

        if (!Directory.Exists(filePath))
            return Task.FromResult(false);

        using MemoryStream stream = new();
        Compressor.CompressZipFileDirectory(new(filePath), stream);

        return SendFile(guid, stream);
    }

    private async Task<bool> SendFile(Guid guid, MemoryStream stream)
    {
        if (!stream.Split(out List<byte[]>? buffer, 8192) || buffer is null)
            return false;

        for (int i = 0; i < buffer.Count; i++)
        {
            try
            {
                if (!await clientCaller.FilePack(guid, buffer[i], i + 1, buffer.Count))
                    return false;
            }
            catch
            {
                return false;
            }
        }
        return true;
    }

    private void FileCleanerTimerCallback(object? state)
    {
        if (state is not FileType fileType)
            return;

        if (_streamBuffer.TryRemove(fileType, out MemoryStream? memoryStream) && memoryStream is not null)
            memoryStream.Dispose();
    }

    public Task<bool> UpdateDeviceInfo(Guid guid, DeviceInfo deviceInfo)
    {
        baseProvider.UpdateDeviceInfo(deviceInfo);
        return Task.FromResult(true);
    }
}

public interface IClientBaseProvider
{
    string? RecipePath { get; set; }
    string? ConfigPath { get; set; }

    bool AllowReceiveFile();
    void FileReceivedNotify(FileType fileType, string path);
    void StartFileReceiveNotify(FileType fileType);

    void UpdateDeviceInfo(DeviceInfo deviceInfo);
}