SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

IF OBJECT_ID(N'dbo.Scenarios', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.Scenarios
    (
        ScenarioID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Scenarios PRIMARY KEY,
        ProjectID int NOT NULL,
        BookID int NULL,
        ScenarioName nvarchar(200) NOT NULL,
        Description nvarchar(max) NULL,
        BaseCreatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_BaseCreatedDate DEFAULT SYSUTCDATETIME(),
        IsAppliedToMain bit NOT NULL CONSTRAINT DF_Scenarios_IsAppliedToMain DEFAULT 0,
        IsArchived bit NOT NULL CONSTRAINT DF_Scenarios_IsArchived DEFAULT 0,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_CreatedDate DEFAULT SYSUTCDATETIME(),
        UpdatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_UpdatedDate DEFAULT SYSUTCDATETIME(),
        CONSTRAINT FK_Scenarios_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
        CONSTRAINT FK_Scenarios_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID)
    );
END;
GO

IF OBJECT_ID(N'dbo.ScenarioSceneOrder', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.ScenarioSceneOrder
    (
        ScenarioSceneOrderID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioSceneOrder PRIMARY KEY,
        ScenarioID int NOT NULL,
        SceneID int NOT NULL,
        ProposedChapterID int NULL,
        ProposedSortOrder int NOT NULL,
        Notes nvarchar(max) NULL,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioSceneOrder_CreatedDate DEFAULT SYSUTCDATETIME(),
        UpdatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioSceneOrder_UpdatedDate DEFAULT SYSUTCDATETIME(),
        CONSTRAINT FK_ScenarioSceneOrder_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID),
        CONSTRAINT FK_ScenarioSceneOrder_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
        CONSTRAINT FK_ScenarioSceneOrder_Chapters FOREIGN KEY (ProposedChapterID) REFERENCES dbo.Chapters(ChapterID),
        CONSTRAINT UQ_ScenarioSceneOrder_ScenarioScene UNIQUE (ScenarioID, SceneID)
    );
END;
GO

IF OBJECT_ID(N'dbo.ScenarioValidationRuns', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.ScenarioValidationRuns
    (
        ScenarioValidationRunID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioValidationRuns PRIMARY KEY,
        ScenarioID int NOT NULL,
        StartedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioValidationRuns_StartedDate DEFAULT SYSUTCDATETIME(),
        CompletedDate datetime2 NULL,
        WarningCount int NOT NULL CONSTRAINT DF_ScenarioValidationRuns_WarningCount DEFAULT 0,
        CONSTRAINT FK_ScenarioValidationRuns_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID)
    );
END;
GO

IF OBJECT_ID(N'dbo.ScenarioWarnings', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.ScenarioWarnings
    (
        ScenarioWarningID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioWarnings PRIMARY KEY,
        ScenarioValidationRunID int NOT NULL,
        ScenarioID int NOT NULL,
        SceneID int NULL,
        EntityType nvarchar(80) NOT NULL,
        EntityID int NULL,
        WarningTypeName nvarchar(150) NOT NULL,
        SeverityName nvarchar(50) NOT NULL,
        Message nvarchar(500) NOT NULL,
        Details nvarchar(max) NULL,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioWarnings_CreatedDate DEFAULT SYSUTCDATETIME(),
        CONSTRAINT FK_ScenarioWarnings_Runs FOREIGN KEY (ScenarioValidationRunID) REFERENCES dbo.ScenarioValidationRuns(ScenarioValidationRunID),
        CONSTRAINT FK_ScenarioWarnings_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID),
        CONSTRAINT FK_ScenarioWarnings_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID)
    );
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_List
    @ProjectID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
        sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate,
        p.ProjectName, b.BookTitle,
        COUNT(DISTINCT sso.SceneID) AS SceneCount,
        COUNT(DISTINCT sw.ScenarioWarningID) AS WarningCount
    FROM dbo.Scenarios sc
    INNER JOIN dbo.Projects p ON p.ProjectID = sc.ProjectID
    LEFT JOIN dbo.Books b ON b.BookID = sc.BookID
    LEFT JOIN dbo.ScenarioSceneOrder sso ON sso.ScenarioID = sc.ScenarioID
    LEFT JOIN dbo.ScenarioWarnings sw ON sw.ScenarioID = sc.ScenarioID
    WHERE sc.ProjectID = @ProjectID AND sc.IsArchived = 0
    GROUP BY sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
        sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate, p.ProjectName, b.BookTitle
    ORDER BY sc.UpdatedDate DESC, sc.ScenarioName;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Get
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
        sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate,
        p.ProjectName, b.BookTitle,
        (SELECT COUNT(*) FROM dbo.ScenarioSceneOrder WHERE ScenarioID = sc.ScenarioID) AS SceneCount,
        (SELECT COUNT(*) FROM dbo.ScenarioWarnings WHERE ScenarioID = sc.ScenarioID) AS WarningCount
    FROM dbo.Scenarios sc
    INNER JOIN dbo.Projects p ON p.ProjectID = sc.ProjectID
    LEFT JOIN dbo.Books b ON b.BookID = sc.BookID
    WHERE sc.ScenarioID = @ScenarioID AND sc.IsArchived = 0;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Create
    @ProjectID int,
    @BookID int = NULL,
    @ScenarioName nvarchar(200),
    @Description nvarchar(max) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    INSERT dbo.Scenarios (ProjectID, BookID, ScenarioName, Description)
    VALUES (@ProjectID, @BookID, @ScenarioName, @Description);

    DECLARE @ScenarioID int = CAST(SCOPE_IDENTITY() AS int);

    INSERT dbo.ScenarioSceneOrder (ScenarioID, SceneID, ProposedChapterID, ProposedSortOrder)
    SELECT @ScenarioID, nso.SceneID, nso.ChapterID,
        ROW_NUMBER() OVER (ORDER BY nso.GlobalSceneIndex) * 10
    FROM dbo.NarrativeSceneOrder nso
    WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID)
    ORDER BY nso.GlobalSceneIndex;

    SELECT @ScenarioID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Timeline
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Scenarios WHERE ScenarioID = @ScenarioID);
    DECLARE @BookID int = (SELECT BookID FROM dbo.Scenarios WHERE ScenarioID = @ScenarioID);

    SELECT BookID, ProjectID, BookTitle, BookNumber, Description, SortOrder, CreatedDate, UpdatedDate, IsArchived
    FROM dbo.Books
    WHERE ProjectID = @ProjectID AND IsArchived = 0 AND (@BookID IS NULL OR BookID = @BookID)
    ORDER BY SortOrder, BookNumber;

    SELECT c.ChapterID, c.BookID, c.ChapterNumber, c.ChapterTitle, c.POVCharacterID, c.Summary, c.SortOrder,
        c.RevisionStatusID, rs.StatusName AS RevisionStatusName, c.CreatedDate, c.UpdatedDate, c.IsArchived
    FROM dbo.Chapters c
    INNER JOIN dbo.Books b ON b.BookID = c.BookID
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID
    WHERE b.ProjectID = @ProjectID AND c.IsArchived = 0 AND b.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID)
    ORDER BY b.SortOrder, c.SortOrder, c.ChapterNumber;

    SELECT sso.ScenarioSceneOrderID, sso.ScenarioID, sso.SceneID, s.ChapterID AS OriginalChapterID,
        ISNULL(sso.ProposedChapterID, s.ChapterID) AS ProposedChapterID, sso.ProposedSortOrder, sso.Notes,
        ch.ChapterNumber, ch.ChapterTitle, s.SceneNumber, s.SceneTitle, s.Summary, rs.StatusName AS RevisionStatusName,
        COALESCE(NULLIF(s.RelativeTimeText, N''), CONVERT(nvarchar(20), s.StartDateTime, 106), tm.TimeModeName) AS TimeLabel,
        threadData.ThreadLabels, assetData.AssetLabels, characterData.CharacterLabels, loc.LocationPath AS LocationLabel,
        (SELECT COUNT(*) FROM dbo.ScenarioWarnings sw WHERE sw.ScenarioID = @ScenarioID AND sw.SceneID = s.SceneID) AS WarningCount
    FROM dbo.ScenarioSceneOrder sso
    INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
    INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
    INNER JOIN dbo.TimeModes tm ON tm.TimeModeID = s.TimeModeID
    LEFT JOIN dbo.LocationPaths loc ON loc.LocationID = s.PrimaryLocationID
    OUTER APPLY
    (
        SELECT STRING_AGG(CONVERT(nvarchar(max), pt.ThreadTitle), N', ') AS ThreadLabels
        FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
        WHERE te.SceneID = s.SceneID
    ) threadData
    OUTER APPLY
    (
        SELECT STRING_AGG(CONVERT(nvarchar(max), sa.AssetName), N', ') AS AssetLabels
        FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
        WHERE ae.SceneID = s.SceneID
    ) assetData
    OUTER APPLY
    (
        SELECT STRING_AGG(CONVERT(nvarchar(max), c.CharacterName), N', ') AS CharacterLabels
        FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters c ON c.CharacterID = sc.CharacterID
        WHERE sc.SceneID = s.SceneID
    ) characterData
    WHERE sso.ScenarioID = @ScenarioID
    ORDER BY ch.SortOrder, ch.ChapterNumber, sso.ProposedSortOrder, s.SceneID;

    SELECT ScenarioWarningID, ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details, CreatedDate
    FROM dbo.ScenarioWarnings
    WHERE ScenarioID = @ScenarioID
    ORDER BY CASE SeverityName WHEN N'Critical' THEN 4 WHEN N'Warning' THEN 3 WHEN N'Info' THEN 2 ELSE 1 END DESC, CreatedDate DESC;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_MoveScene
    @ScenarioID int,
    @SceneID int,
    @AnchorSceneID int = NULL,
    @TargetChapterID int = NULL,
    @Position nvarchar(20)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ChapterID int;
    DECLARE @NewSortOrder int;
    DECLARE @AnchorSortOrder int;

    IF @AnchorSceneID IS NOT NULL
    BEGIN
        SELECT @ChapterID = ISNULL(ProposedChapterID, s.ChapterID), @AnchorSortOrder = ProposedSortOrder
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        WHERE sso.ScenarioID = @ScenarioID AND sso.SceneID = @AnchorSceneID;

        IF @Position = N'Before'
            SELECT @NewSortOrder = ISNULL(MAX(ProposedSortOrder), @AnchorSortOrder - 20) + ((@AnchorSortOrder - ISNULL(MAX(ProposedSortOrder), @AnchorSortOrder - 20)) / 2)
            FROM dbo.ScenarioSceneOrder
            WHERE ScenarioID = @ScenarioID AND ISNULL(ProposedChapterID, 0) = ISNULL(@ChapterID, 0) AND ProposedSortOrder < @AnchorSortOrder;
        ELSE
            SELECT @NewSortOrder = (@AnchorSortOrder + ISNULL(MIN(ProposedSortOrder), @AnchorSortOrder + 20)) / 2
            FROM dbo.ScenarioSceneOrder
            WHERE ScenarioID = @ScenarioID AND ISNULL(ProposedChapterID, 0) = ISNULL(@ChapterID, 0) AND ProposedSortOrder > @AnchorSortOrder;
    END
    ELSE
    BEGIN
        SET @ChapterID = @TargetChapterID;
        SELECT @NewSortOrder = ISNULL(MAX(ProposedSortOrder), 0) + 10
        FROM dbo.ScenarioSceneOrder
        WHERE ScenarioID = @ScenarioID AND ProposedChapterID = @ChapterID;
    END;

    UPDATE dbo.ScenarioSceneOrder
    SET ProposedChapterID = @ChapterID,
        ProposedSortOrder = ISNULL(@NewSortOrder, ProposedSortOrder),
        UpdatedDate = SYSUTCDATETIME()
    WHERE ScenarioID = @ScenarioID AND SceneID = @SceneID;

    ;WITH ordered AS
    (
        SELECT ScenarioSceneOrderID,
            ROW_NUMBER() OVER (PARTITION BY ProposedChapterID ORDER BY ProposedSortOrder, ScenarioSceneOrderID) * 10 AS NextSortOrder
        FROM dbo.ScenarioSceneOrder
        WHERE ScenarioID = @ScenarioID
    )
    UPDATE sso
    SET ProposedSortOrder = ordered.NextSortOrder,
        UpdatedDate = SYSUTCDATETIME()
    FROM dbo.ScenarioSceneOrder sso
    INNER JOIN ordered ON ordered.ScenarioSceneOrderID = sso.ScenarioSceneOrderID;

    UPDATE dbo.Scenarios SET UpdatedDate = SYSUTCDATETIME() WHERE ScenarioID = @ScenarioID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Validate
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;

    DELETE FROM dbo.ScenarioWarnings WHERE ScenarioID = @ScenarioID;

    INSERT dbo.ScenarioValidationRuns (ScenarioID)
    VALUES (@ScenarioID);

    DECLARE @RunID int = CAST(SCOPE_IDENTITY() AS int);

    ;WITH ScenarioOrder AS
    (
        SELECT sso.SceneID, sso.ScenarioID, ISNULL(sso.ProposedChapterID, s.ChapterID) AS ChapterID,
            ch.BookID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
            s.SceneTitle, s.StartDateTime
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    )
    INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
    SELECT @RunID, @ScenarioID, targetScene.SceneID, N'SceneDependency', sd.SceneDependencyID, N'Scene Dependency Broken', N'Warning',
        N'Scenario warning: a required scene dependency is out of order.',
        CONCAT(sourceScene.SceneTitle, N' should occur before ', targetScene.SceneTitle, N'.')
    FROM dbo.SceneDependencies sd
    INNER JOIN dbo.SceneDependencyTypes sdt ON sdt.SceneDependencyTypeID = sd.SceneDependencyTypeID
    INNER JOIN ScenarioOrder sourceScene ON sourceScene.SceneID = sd.SourceSceneID
    INNER JOIN ScenarioOrder targetScene ON targetScene.SceneID = sd.TargetSceneID
    WHERE sd.IsArchived = 0 AND sdt.TypeName = N'Must Occur Before' AND sourceScene.ScenarioIndex >= targetScene.ScenarioIndex;

    ;WITH ScenarioOrder AS
    (
        SELECT sso.SceneID, sso.ScenarioID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
            s.SceneTitle, s.StartDateTime
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    )
    INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
    SELECT @RunID, @ScenarioID, later.SceneID, N'Scene', later.SceneID, N'Date / Order Conflict', N'Info',
        N'Scenario warning: an exact scene date appears earlier than a previous scenario scene.',
        CONCAT(later.SceneTitle, N' is dated before ', earlier.SceneTitle, N'.')
    FROM ScenarioOrder earlier
    INNER JOIN ScenarioOrder later ON later.ScenarioIndex = earlier.ScenarioIndex + 1
    WHERE earlier.StartDateTime IS NOT NULL AND later.StartDateTime IS NOT NULL AND later.StartDateTime < earlier.StartDateTime;

    ;WITH ScenarioOrder AS
    (
        SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    ),
    ThreadEventsOrdered AS
    (
        SELECT te.PlotThreadID, te.ThreadEventID, te.SceneID, pt.ThreadTitle, tet.TypeName,
            so.ScenarioIndex,
            MIN(CASE WHEN tet.TypeName LIKE N'%Setup%' THEN so.ScenarioIndex END) OVER (PARTITION BY te.PlotThreadID) AS FirstSetupIndex
        FROM dbo.ThreadEvents te
        INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
        INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
        INNER JOIN ScenarioOrder so ON so.SceneID = te.SceneID
    )
    INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
    SELECT @RunID, @ScenarioID, SceneID, N'PlotThread', PlotThreadID, N'Plot Thread Order', N'Warning',
        N'Scenario warning: a reveal or payoff may occur before its setup.',
        CONCAT(ThreadTitle, N' has ', TypeName, N' before the first setup beat.')
    FROM ThreadEventsOrdered
    WHERE (TypeName LIKE N'%Reveal%' OR TypeName LIKE N'%Payoff%')
        AND FirstSetupIndex IS NOT NULL AND ScenarioIndex < FirstSetupIndex;

    ;WITH ScenarioOrder AS
    (
        SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    ),
    AssetFirstSeen AS
    (
        SELECT ae.StoryAssetID, MIN(so.ScenarioIndex) AS FirstSeenIndex
        FROM dbo.AssetEvents ae
        INNER JOIN ScenarioOrder so ON so.SceneID = ae.SceneID
        GROUP BY ae.StoryAssetID
    )
    INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
    SELECT @RunID, @ScenarioID, NULL, N'AssetDependency', ad.AssetDependencyID, N'Asset Dependency Order', N'Info',
        N'Scenario warning: an asset appears before a validating dependency source appears.',
        CONCAT(target.AssetName, N' appears before ', source.AssetName, N'.')
    FROM dbo.AssetDependencies ad
    INNER JOIN dbo.AssetDependencyTypes adt ON adt.AssetDependencyTypeID = ad.AssetDependencyTypeID
    INNER JOIN dbo.StoryAssets source ON source.StoryAssetID = ad.SourceAssetID
    INNER JOIN dbo.StoryAssets target ON target.StoryAssetID = ad.TargetAssetID
    INNER JOIN AssetFirstSeen sourceSeen ON sourceSeen.StoryAssetID = ad.SourceAssetID
    INNER JOIN AssetFirstSeen targetSeen ON targetSeen.StoryAssetID = ad.TargetAssetID
    WHERE ad.IsArchived = 0 AND adt.IsValidatingDependency = 1 AND targetSeen.FirstSeenIndex < sourceSeen.FirstSeenIndex;

    ;WITH ScenarioOrder AS
    (
        SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    ),
    MetricSteps AS
    (
        SELECT smv.SceneID, smt.MetricName, smv.Value, so.ScenarioIndex,
            LAG(smv.Value) OVER (PARTITION BY smt.MetricName ORDER BY so.ScenarioIndex) AS PreviousValue
        FROM dbo.SceneMetricValues smv
        INNER JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID
        INNER JOIN ScenarioOrder so ON so.SceneID = smv.SceneID
        WHERE smt.MetricName IN (N'Overall Intensity', N'Tension', N'Emotional Weight')
    )
    INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
    SELECT @RunID, @ScenarioID, SceneID, N'Scene', SceneID, N'Pacing Jump', N'Info',
        N'Scenario warning: a pacing/emotional metric changes sharply after this move.',
        CONCAT(MetricName, N' changes from ', PreviousValue, N' to ', Value, N'.')
    FROM MetricSteps
    WHERE PreviousValue IS NOT NULL AND ABS(Value - PreviousValue) >= 6;

    DECLARE @WarningCount int = (SELECT COUNT(*) FROM dbo.ScenarioWarnings WHERE ScenarioValidationRunID = @RunID);
    UPDATE dbo.ScenarioValidationRuns SET CompletedDate = SYSUTCDATETIME(), WarningCount = @WarningCount WHERE ScenarioValidationRunID = @RunID;
    SELECT @WarningCount;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Compare
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;

    ;WITH MainOrder AS
    (
        SELECT nso.SceneID, nso.GlobalSceneIndex,
            CONCAT(nso.BookTitle, N' / Ch ', nso.ChapterNumber, N' / Scene ', nso.SceneNumber) AS MainPosition
        FROM dbo.NarrativeSceneOrder nso
        INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = nso.SceneID AND sso.ScenarioID = @ScenarioID
    ),
    ScenarioOrder AS
    (
        SELECT sso.SceneID,
            ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
            CONCAT(b.BookTitle, N' / Ch ', ch.ChapterNumber, N' / Scene ', s.SceneNumber) AS ScenarioPosition,
            s.SceneTitle
        FROM dbo.ScenarioSceneOrder sso
        INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
        INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
        INNER JOIN dbo.Books b ON b.BookID = ch.BookID
        WHERE sso.ScenarioID = @ScenarioID
    )
    SELECT so.SceneID, so.SceneTitle, mo.MainPosition, so.ScenarioPosition,
        mo.GlobalSceneIndex AS MainIndex, so.ScenarioIndex, CAST(CASE WHEN mo.GlobalSceneIndex <> so.ScenarioIndex OR mo.MainPosition <> so.ScenarioPosition THEN 1 ELSE 0 END AS bit) AS HasMoved
    FROM ScenarioOrder so
    INNER JOIN MainOrder mo ON mo.SceneID = so.SceneID
    WHERE mo.GlobalSceneIndex <> so.ScenarioIndex OR mo.MainPosition <> so.ScenarioPosition
    ORDER BY so.ScenarioIndex;

    SELECT ScenarioWarningID, ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details, CreatedDate
    FROM dbo.ScenarioWarnings
    WHERE ScenarioID = @ScenarioID
    ORDER BY CASE SeverityName WHEN N'Critical' THEN 4 WHEN N'Warning' THEN 3 WHEN N'Info' THEN 2 ELSE 1 END DESC, CreatedDate DESC;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_ApplyToMain
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE s
    SET ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID),
        SortOrder = sso.ProposedSortOrder,
        UpdatedDate = SYSUTCDATETIME()
    FROM dbo.Scenes s
    INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = s.SceneID
    WHERE sso.ScenarioID = @ScenarioID;

    ;WITH affected AS
    (
        SELECT DISTINCT ChapterID FROM dbo.Scenes s INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = s.SceneID WHERE sso.ScenarioID = @ScenarioID
    ),
    ordered AS
    (
        SELECT s.SceneID, ROW_NUMBER() OVER (PARTITION BY s.ChapterID ORDER BY s.SortOrder, s.SceneID) * 10 AS NextSortOrder
        FROM dbo.Scenes s
        INNER JOIN affected a ON a.ChapterID = s.ChapterID
        WHERE s.IsArchived = 0
    )
    UPDATE s
    SET SortOrder = ordered.NextSortOrder,
        UpdatedDate = SYSUTCDATETIME()
    FROM dbo.Scenes s
    INNER JOIN ordered ON ordered.SceneID = s.SceneID;

    UPDATE dbo.Scenarios
    SET IsAppliedToMain = 1,
        UpdatedDate = SYSUTCDATETIME()
    WHERE ScenarioID = @ScenarioID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Scenario_Archive
    @ScenarioID int
AS
BEGIN
    SET NOCOUNT ON;
    UPDATE dbo.Scenarios SET IsArchived = 1, UpdatedDate = SYSUTCDATETIME() WHERE ScenarioID = @ScenarioID;
END;
GO
