using InfluxDB.Client;
using Managing.Infrastructure.Databases.InfluxDb.Abstractions;

namespace Managing.Infrastructure.Databases.InfluxDb;

public class InfluxDbRepository : IInfluxDbRepository, IDisposable
{
    private readonly string _token;
    private readonly string _url;
    private readonly InfluxDBClient _client;
    private readonly WriteApi _writeApi;
    private readonly QueryApi _queryApi;
    private readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
    private readonly SemaphoreSlim _querySemaphore = new SemaphoreSlim(1, 1);
    private bool _disposed = false;

    public string Organization { get; set; }

    public InfluxDbRepository(IInfluxDbSettings settings)
    {
        _token = settings.Token;
        _url = settings.Url;
        Organization = settings.Organization;
        
        // Create a single client instance that will be reused
        _client = new InfluxDBClient(_url, _token);
        _writeApi = _client.GetWriteApi();
        _queryApi = _client.GetQueryApi();
    }

    public void Write(Action<WriteApi> action)
    {
        if (_disposed) throw new ObjectDisposedException(nameof(InfluxDbRepository));
        
        _writeSemaphore.Wait();
        try
        {
            action(_writeApi);
        }
        finally
        {
            _writeSemaphore.Release();
        }
    }

    public async Task WriteAsync(Func<WriteApi, Task> action)
    {
        if (_disposed) throw new ObjectDisposedException(nameof(InfluxDbRepository));
        
        await _writeSemaphore.WaitAsync();
        try
        {
            await action(_writeApi);
        }
        finally
        {
            _writeSemaphore.Release();
        }
    }

    public async Task<T> QueryAsync<T>(Func<QueryApi, Task<T>> action)
    {
        if (_disposed) throw new ObjectDisposedException(nameof(InfluxDbRepository));
        
        await _querySemaphore.WaitAsync();
        try
        {
            return await action(_queryApi);
        }
        finally
        {
            _querySemaphore.Release();
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            try
            {
                // Give the WriteApi time to flush any pending writes
                _writeApi?.Dispose();
            }
            catch (Exception)
            {
                // Ignore disposal errors
            }
            
            try
            {
                _client?.Dispose();
            }
            catch (Exception)
            {
                // Ignore disposal errors
            }
            
            _writeSemaphore?.Dispose();
            _querySemaphore?.Dispose();
            
            _disposed = true;
        }
    }
}