using GEUTEBRUECK.GeViSoftSDKNET.ActionsWrapper;
using GEUTEBRUECK.GeViSoftSDKNET.ActionsWrapper.ActionDispatcher;
using Serilog;

namespace GeViScopeBridge.SDK
{
    /// <summary>
    /// Wrapper around GeViDatabase providing connection lifecycle management and retry logic
    /// </summary>
    public class GeViDatabaseWrapper : IDisposable
    {
        private readonly string _hostname;
        private readonly string _username;
        private readonly string _password;
        private readonly ILogger _logger;
        private GeViDatabase? _database;
        private bool _isConnected;
        private readonly object _lockObject = new object();

        // Retry configuration
        private const int MaxRetries = 3;
        private const int InitialRetryDelayMs = 1000;

        public bool IsConnected => _isConnected;
        public GeViDatabase? Database => _database;

        public GeViDatabaseWrapper(string hostname, string username, string password, ILogger logger)
        {
            _hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
            _username = username ?? throw new ArgumentNullException(nameof(username));
            _password = password ?? throw new ArgumentNullException(nameof(password));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// Create and connect to GeViServer with retry logic
        /// </summary>
        public async Task<bool> ConnectAsync()
        {
            lock (_lockObject)
            {
                if (_isConnected && _database != null)
                {
                    _logger.Information("Already connected to GeViServer");
                    return true;
                }
            }

            int attempt = 0;
            int delayMs = InitialRetryDelayMs;

            while (attempt < MaxRetries)
            {
                attempt++;
                _logger.Information("Attempting to connect to GeViServer (attempt {Attempt}/{MaxRetries})",
                    attempt, MaxRetries);

                try
                {
                    // Create GeViDatabase instance
                    var db = new GeViDatabase();

                    _logger.Debug("Creating connection: Host={Host}, User={User}", _hostname, _username);
                    db.Create(_hostname, _username, _password);

                    _logger.Debug("Registering callback handlers");
                    db.RegisterCallback();

                    _logger.Debug("Connecting to GeViServer...");
                    GeViConnectResult result = db.Connect();

                    if (result == GeViConnectResult.connectOk)
                    {
                        lock (_lockObject)
                        {
                            _database = db;
                            _isConnected = true;
                        }

                        _logger.Information("Successfully connected to GeViServer at {Host}", _hostname);
                        return true;
                    }
                    else
                    {
                        _logger.Warning("Connection failed with result: {Result}", result);
                        db.Dispose();

                        if (attempt < MaxRetries)
                        {
                            _logger.Information("Waiting {DelayMs}ms before retry...", delayMs);
                            await Task.Delay(delayMs);
                            delayMs *= 2; // Exponential backoff
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "Exception during connection attempt {Attempt}", attempt);

                    if (attempt < MaxRetries)
                    {
                        _logger.Information("Waiting {DelayMs}ms before retry...", delayMs);
                        await Task.Delay(delayMs);
                        delayMs *= 2; // Exponential backoff
                    }
                }
            }

            _logger.Error("Failed to connect to GeViServer after {MaxRetries} attempts", MaxRetries);
            return false;
        }

        /// <summary>
        /// Disconnect from GeViServer
        /// </summary>
        public void Disconnect()
        {
            lock (_lockObject)
            {
                if (_database != null && _isConnected)
                {
                    try
                    {
                        _logger.Information("Disconnecting from GeViServer");
                        _database.Disconnect();
                        _database.Dispose();
                        _isConnected = false;
                        _database = null;
                        _logger.Information("Disconnected successfully");
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(ex, "Error during disconnect");
                    }
                }
            }
        }

        /// <summary>
        /// Check if connection is still alive and reconnect if needed
        /// </summary>
        public async Task<bool> EnsureConnectedAsync()
        {
            lock (_lockObject)
            {
                if (_isConnected && _database != null)
                {
                    return true;
                }
            }

            _logger.Warning("Connection lost, attempting to reconnect...");
            return await ConnectAsync();
        }

        /// <summary>
        /// Send a message/action to GeViServer
        /// </summary>
        public async Task<bool> SendMessageAsync(string message)
        {
            if (!await EnsureConnectedAsync())
            {
                throw new InvalidOperationException("Not connected to GeViServer");
            }

            try
            {
                _logger.Debug("Sending message: {Message}", message);
                _database!.SendMessage(message);
                return true;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed to send message: {Message}", message);
                return false;
            }
        }

        public void Dispose()
        {
            Disconnect();
        }
    }
}
