using System;
using System.Threading;
using ArchestrA;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Historian
{
    /// <summary>
    ///     Creates and opens Historian SDK connections. Extracted so tests can inject fakes that
    ///     control connection success, failure, and timeout behavior.
    /// </summary>
    internal interface IHistorianConnectionFactory
    {
        HistorianAccess CreateAndConnect(HistorianConfiguration config, HistorianConnectionType type);
    }

    /// <summary>Production implementation — opens real Historian SDK connections.</summary>
    internal sealed class SdkHistorianConnectionFactory : IHistorianConnectionFactory
    {
        public HistorianAccess CreateAndConnect(HistorianConfiguration config, HistorianConnectionType type)
        {
            var conn = new HistorianAccess();

            var args = new HistorianConnectionArgs
            {
                ServerName = config.ServerName,
                TcpPort = (ushort)config.Port,
                IntegratedSecurity = config.IntegratedSecurity,
                UseArchestrAUser = config.IntegratedSecurity,
                ConnectionType = type,
                ReadOnly = true,
                PacketTimeout = (uint)(config.CommandTimeoutSeconds * 1000)
            };

            if (!config.IntegratedSecurity)
            {
                args.UserName = config.UserName ?? string.Empty;
                args.Password = config.Password ?? string.Empty;
            }

            if (!conn.OpenConnection(args, out var error))
            {
                conn.Dispose();
                throw new InvalidOperationException(
                    $"Failed to open Historian SDK connection to {config.ServerName}:{config.Port}: {error.ErrorCode}");
            }

            var timeoutMs = config.CommandTimeoutSeconds * 1000;
            var elapsed = 0;
            while (elapsed < timeoutMs)
            {
                var status = new HistorianConnectionStatus();
                conn.GetConnectionStatus(ref status);

                if (status.ConnectedToServer)
                    return conn;

                if (status.ErrorOccurred)
                {
                    conn.Dispose();
                    throw new InvalidOperationException(
                        $"Historian SDK connection failed: {status.Error}");
                }

                Thread.Sleep(250);
                elapsed += 250;
            }

            conn.Dispose();
            throw new TimeoutException(
                $"Historian SDK connection to {config.ServerName}:{config.Port} timed out after {config.CommandTimeoutSeconds}s");
        }
    }
}
