using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
{
    /// <summary>
    ///     Phase 7 follow-up (task #241) — extends <c>dbo.sp_ComputeGenerationDiff</c> to emit
    ///     Script / VirtualTag / ScriptedAlarm rows alongside the existing Namespace /
    ///     DriverInstance / Equipment / Tag / NodeAcl output. Admin DiffViewer now shows
    ///     Phase 7 changes between generations.
    /// </summary>
    /// <remarks>
    ///     Logical ids: ScriptId, VirtualTagId, ScriptedAlarmId — stable across generations
    ///     so a Script whose source changes surfaces as Modified (CHECKSUM picks up the
    ///     SourceHash delta) while a renamed script surfaces as Modified on Name alone.
    ///     ScriptedAlarmState is deliberately excluded — it's not generation-scoped, so
    ///     diffing it between generations is meaningless.
    /// </remarks>
    /// <inheritdoc />
    public partial class ExtendComputeGenerationDiffWithPhase7 : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(Procs.ComputeGenerationDiffV3);
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(Procs.ComputeGenerationDiffV2);
        }

        private static class Procs
        {
            /// <summary>V3 — adds Script / VirtualTag / ScriptedAlarm sections.</summary>
            public const string ComputeGenerationDiffV3 = @"
CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff
    @FromGenerationId bigint,
    @ToGenerationId   bigint
AS
BEGIN
    SET NOCOUNT ON;

    CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(128), ChangeKind nvarchar(16));

    WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId),
         t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Namespace', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId),
         t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'DriverInstance', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId),
         t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Equipment', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId),
         t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Tag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (
            SELECT  CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
                    CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
            FROM    dbo.NodeAcl WHERE GenerationId = @FromGenerationId),
         t AS (
            SELECT  CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
                    CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
            FROM    dbo.NodeAcl WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'NodeAcl', COALESCE(f.LogicalId, t.LogicalId),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    -- Phase 7 — Script section. CHECKSUM picks up source changes via SourceHash + rename
    -- via Name; Language future-proofs for non-C# engines. Same Name + same Source =
    -- Unchanged (identical hash).
    WITH f AS (SELECT ScriptId AS LogicalId, CHECKSUM(Name, SourceHash, Language) AS Sig FROM dbo.Script WHERE GenerationId = @FromGenerationId),
         t AS (SELECT ScriptId AS LogicalId, CHECKSUM(Name, SourceHash, Language) AS Sig FROM dbo.Script WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Script', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    -- Phase 7 — VirtualTag section.
    WITH f AS (SELECT VirtualTagId AS LogicalId, CHECKSUM(EquipmentId, Name, DataType, ScriptId, ChangeTriggered, TimerIntervalMs, Historize, Enabled) AS Sig FROM dbo.VirtualTag WHERE GenerationId = @FromGenerationId),
         t AS (SELECT VirtualTagId AS LogicalId, CHECKSUM(EquipmentId, Name, DataType, ScriptId, ChangeTriggered, TimerIntervalMs, Historize, Enabled) AS Sig FROM dbo.VirtualTag WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'VirtualTag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    -- Phase 7 — ScriptedAlarm section. ScriptedAlarmState (operator ack trail) is
    -- logical-id keyed outside the generation scope + intentionally excluded here —
    -- diffing ack state between generations is semantically meaningless.
    WITH f AS (SELECT ScriptedAlarmId AS LogicalId, CHECKSUM(EquipmentId, Name, AlarmType, Severity, MessageTemplate, PredicateScriptId, HistorizeToAveva, Retain, Enabled) AS Sig FROM dbo.ScriptedAlarm WHERE GenerationId = @FromGenerationId),
         t AS (SELECT ScriptedAlarmId AS LogicalId, CHECKSUM(EquipmentId, Name, AlarmType, Severity, MessageTemplate, PredicateScriptId, HistorizeToAveva, Retain, Enabled) AS Sig FROM dbo.ScriptedAlarm WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'ScriptedAlarm', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    SELECT TableName, LogicalId, ChangeKind FROM #diff;
    DROP TABLE #diff;
END
";

            /// <summary>V2 — restores the pre-Phase-7 proc on Down().</summary>
            public const string ComputeGenerationDiffV2 = @"
CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff
    @FromGenerationId bigint,
    @ToGenerationId   bigint
AS
BEGIN
    SET NOCOUNT ON;

    CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(128), ChangeKind nvarchar(16));

    WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId),
         t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Namespace', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId),
         t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'DriverInstance', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId),
         t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Equipment', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId),
         t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'Tag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    WITH f AS (
            SELECT  CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
                    CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
            FROM    dbo.NodeAcl WHERE GenerationId = @FromGenerationId),
         t AS (
            SELECT  CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
                    CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
            FROM    dbo.NodeAcl WHERE GenerationId = @ToGenerationId)
    INSERT #diff
    SELECT 'NodeAcl', COALESCE(f.LogicalId, t.LogicalId),
           CASE WHEN f.LogicalId IS NULL THEN 'Added'
                WHEN t.LogicalId IS NULL THEN 'Removed'
                WHEN f.Sig <> t.Sig       THEN 'Modified'
                ELSE 'Unchanged' END
    FROM   f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
    WHERE  f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;

    SELECT TableName, LogicalId, ChangeKind FROM #diff;
    DROP TABLE #diff;
END
";
        }
    }
}
