using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.OtOpcUa.Configuration.LocalCache;

namespace ZB.MOM.WW.OtOpcUa.Server;

/// <summary>
///     Bootstraps a node: fetches the current generation from the central DB via
///     <c>sp_GetCurrentGenerationForCluster</c>. If the DB is unreachable and a LiteDB cache entry
///     exists, falls back to cached config per decision #79 (degraded-but-running).
/// </summary>
public sealed class NodeBootstrap(
    NodeOptions options,
    ILocalConfigCache localCache,
    ILogger<NodeBootstrap> logger)
{
    public async Task<BootstrapResult> LoadCurrentGenerationAsync(CancellationToken ct)
    {
        try
        {
            await using var conn = new SqlConnection(options.ConfigDbConnectionString);
            await conn.OpenAsync(ct);

            await using var cmd = conn.CreateCommand();
            cmd.CommandText = "EXEC dbo.sp_GetCurrentGenerationForCluster @NodeId=@n, @ClusterId=@c";
            cmd.Parameters.AddWithValue("@n", options.NodeId);
            cmd.Parameters.AddWithValue("@c", options.ClusterId);

            await using var reader = await cmd.ExecuteReaderAsync(ct);
            if (!await reader.ReadAsync(ct))
            {
                logger.LogWarning("Cluster {Cluster} has no Published generation yet", options.ClusterId);
                return BootstrapResult.EmptyFromDb();
            }

            var generationId = reader.GetInt64(0);
            logger.LogInformation("Bootstrapped from central DB: generation {GenerationId}", generationId);
            return BootstrapResult.FromDb(generationId);
        }
        catch (Exception ex) when (ex is SqlException or InvalidOperationException or TimeoutException)
        {
            logger.LogWarning(ex, "Central DB unreachable; trying LiteDB cache fallback (decision #79)");
            var cached = await localCache.GetMostRecentAsync(options.ClusterId, ct);
            if (cached is null)
                throw new BootstrapException(
                    "Central DB unreachable and no local cache available — cannot bootstrap.", ex);

            logger.LogWarning("Bootstrapping from cache: generation {GenerationId} cached at {At}",
                cached.GenerationId, cached.CachedAt);
            return BootstrapResult.FromCache(cached.GenerationId);
        }
    }
}

public sealed record BootstrapResult(long? GenerationId, BootstrapSource Source)
{
    public static BootstrapResult FromDb(long g) => new(g, BootstrapSource.CentralDb);
    public static BootstrapResult FromCache(long g) => new(g, BootstrapSource.LocalCache);
    public static BootstrapResult EmptyFromDb() => new(null, BootstrapSource.CentralDb);
}

public enum BootstrapSource { CentralDb, LocalCache }

public sealed class BootstrapException(string message, Exception inner) : Exception(message, inner);
