using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;

namespace ZB.MOM.WW.OtOpcUa.Admin.Services;

/// <summary>
///     Owns the draft → diff → publish workflow (decision #89). Publish + rollback call into the
///     stored procedures; diff queries <c>sp_ComputeGenerationDiff</c>.
/// </summary>
public sealed class GenerationService(OtOpcUaConfigDbContext db)
{
    public async Task<ConfigGeneration> CreateDraftAsync(string clusterId, string createdBy, CancellationToken ct)
    {
        var gen = new ConfigGeneration
        {
            ClusterId = clusterId,
            Status = GenerationStatus.Draft,
            CreatedBy = createdBy,
            CreatedAt = DateTime.UtcNow,
        };
        db.ConfigGenerations.Add(gen);
        await db.SaveChangesAsync(ct);
        return gen;
    }

    public Task<List<ConfigGeneration>> ListRecentAsync(string clusterId, int limit, CancellationToken ct) =>
        db.ConfigGenerations.AsNoTracking()
            .Where(g => g.ClusterId == clusterId)
            .OrderByDescending(g => g.GenerationId)
            .Take(limit)
            .ToListAsync(ct);

    public async Task PublishAsync(string clusterId, long draftGenerationId, string? notes, CancellationToken ct)
    {
        await db.Database.ExecuteSqlRawAsync(
            "EXEC dbo.sp_PublishGeneration @ClusterId = {0}, @DraftGenerationId = {1}, @Notes = {2}",
            [clusterId, draftGenerationId, (object?)notes ?? DBNull.Value],
            ct);
    }

    public async Task RollbackAsync(string clusterId, long targetGenerationId, string? notes, CancellationToken ct)
    {
        await db.Database.ExecuteSqlRawAsync(
            "EXEC dbo.sp_RollbackToGeneration @ClusterId = {0}, @TargetGenerationId = {1}, @Notes = {2}",
            [clusterId, targetGenerationId, (object?)notes ?? DBNull.Value],
            ct);
    }

    public async Task<List<DiffRow>> ComputeDiffAsync(long from, long to, CancellationToken ct)
    {
        var results = new List<DiffRow>();
        await using var conn = (SqlConnection)db.Database.GetDbConnection();
        if (conn.State != System.Data.ConnectionState.Open) await conn.OpenAsync(ct);

        await using var cmd = conn.CreateCommand();
        cmd.CommandText = "EXEC dbo.sp_ComputeGenerationDiff @FromGenerationId = @f, @ToGenerationId = @t";
        cmd.Parameters.AddWithValue("@f", from);
        cmd.Parameters.AddWithValue("@t", to);

        await using var reader = await cmd.ExecuteReaderAsync(ct);
        while (await reader.ReadAsync(ct))
            results.Add(new DiffRow(reader.GetString(0), reader.GetString(1), reader.GetString(2)));

        return results;
    }
}

public sealed record DiffRow(string TableName, string LogicalId, string ChangeKind);
