SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

/*
    Timeline performance pass for imported/large projects.

    Focus:
    - Keep indexes narrow.
    - Support the actual timeline/project load path.
    - Avoid duplicating existing core indexes on Books, Chapters, Scenes,
      PlotLines, StoryAssets, SceneCharacters(SceneID), AssetEvents(SceneID),
      and ContinuityWarnings(ProjectID, BookID, SceneID...).
*/

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ThreadEvents_PlotThreadID_SceneID' AND object_id = OBJECT_ID(N'dbo.ThreadEvents'))
BEGIN
    -- Supports plot-thread filters and thread-event reads starting from a thread.
    CREATE INDEX IX_ThreadEvents_PlotThreadID_SceneID
    ON dbo.ThreadEvents(PlotThreadID, SceneID, EventTypeID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_AssetEvents_StoryAssetID_SceneID' AND object_id = OBJECT_ID(N'dbo.AssetEvents'))
BEGIN
    -- Supports asset focus/filter lanes and asset-event reads starting from an asset.
    CREATE INDEX IX_AssetEvents_StoryAssetID_SceneID
    ON dbo.AssetEvents(StoryAssetID, SceneID, AssetEventTypeID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneCharacters_CharacterID_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneCharacters'))
BEGIN
    -- Supports character focus/filter lanes and character detail appearance lookups.
    CREATE INDEX IX_SceneCharacters_CharacterID_SceneID
    ON dbo.SceneCharacters(CharacterID, SceneID, RoleInSceneTypeID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Characters_ProjectID_Importance_Name' AND object_id = OBJECT_ID(N'dbo.Characters'))
BEGIN
    -- Supports CharacterTimeline_GetByProject ordering by importance, then name.
    CREATE INDEX IX_Characters_ProjectID_Importance_Name
    ON dbo.Characters(ProjectID, IsArchived, CharacterImportance DESC, CharacterName);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Locations_Project_Parent_Name' AND object_id = OBJECT_ID(N'dbo.Locations'))
BEGIN
    -- Supports project location lists and LocationPaths roots by project.
    CREATE INDEX IX_Locations_Project_Parent_Name
    ON dbo.Locations(ProjectID, IsArchived, ParentLocationID, LocationName);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_Locations_ParentLocationID' AND object_id = OBJECT_ID(N'dbo.Locations'))
BEGIN
    -- Supports recursive LocationPaths expansion from parent to child locations.
    CREATE INDEX IX_Locations_ParentLocationID
    ON dbo.Locations(ParentLocationID, IsArchived, LocationName);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneCharacters_LocationID_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneCharacters'))
BEGIN
    -- Supports timeline location filters that include character appearances at a location.
    CREATE INDEX IX_SceneCharacters_LocationID_SceneID
    ON dbo.SceneCharacters(LocationID, SceneID, CharacterID)
    WHERE LocationID IS NOT NULL;
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneAssetLocations_LocationID_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneAssetLocations'))
BEGIN
    -- Supports timeline location filters that include assets placed at a location.
    CREATE INDEX IX_SceneAssetLocations_LocationID_SceneID
    ON dbo.SceneAssetLocations(LocationID, SceneID, StoryAssetID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneAssetLocations_SceneID_LocationID' AND object_id = OBJECT_ID(N'dbo.SceneAssetLocations'))
BEGIN
    -- Supports scene-level location/asset reads from the scene inspector and timeline focus.
    CREATE INDEX IX_SceneAssetLocations_SceneID_LocationID
    ON dbo.SceneAssetLocations(SceneID, LocationID, StoryAssetID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ContinuityWarnings_ActiveSceneCounts' AND object_id = OBJECT_ID(N'dbo.ContinuityWarnings'))
BEGIN
    -- Supports active warning counts shown on timeline scene cards.
    CREATE INDEX IX_ContinuityWarnings_ActiveSceneCounts
    ON dbo.ContinuityWarnings(ProjectID, BookID, SceneID)
    WHERE SceneID IS NOT NULL AND IsDismissed = 0 AND IsIntentional = 0;
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneDependencies_Project_SourceScene' AND object_id = OBJECT_ID(N'dbo.SceneDependencies'))
BEGIN
    -- Supports dependency counts and source-scene dependency lookups.
    CREATE INDEX IX_SceneDependencies_Project_SourceScene
    ON dbo.SceneDependencies(ProjectID, IsArchived, SourceSceneID, TargetSceneID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneDependencies_Project_TargetScene' AND object_id = OBJECT_ID(N'dbo.SceneDependencies'))
BEGIN
    -- Supports dependency counts and target-scene dependency lookups.
    CREATE INDEX IX_SceneDependencies_Project_TargetScene
    ON dbo.SceneDependencies(ProjectID, IsArchived, TargetSceneID, SourceSceneID);
END;
GO

CREATE OR ALTER PROCEDURE dbo.SceneDependency_Counts
    @ProjectID int,
    @BookID int = NULL
AS
BEGIN
    SET NOCOUNT ON;

    ;WITH DependencyScenes AS
    (
        SELECT sd.SceneDependencyID, sd.SourceSceneID AS SceneID
        FROM dbo.SceneDependencies sd
        WHERE sd.ProjectID = @ProjectID AND sd.IsArchived = 0

        UNION ALL

        SELECT sd.SceneDependencyID, sd.TargetSceneID AS SceneID
        FROM dbo.SceneDependencies sd
        WHERE sd.ProjectID = @ProjectID
            AND sd.IsArchived = 0
            AND sd.TargetSceneID <> sd.SourceSceneID
    )
    SELECT scene.SceneID, COUNT(*) AS DependencyCount
    FROM dbo.NarrativeSceneOrder scene
    INNER JOIN DependencyScenes dependencyScene ON dependencyScene.SceneID = scene.SceneID
    WHERE scene.ProjectID = @ProjectID AND (@BookID IS NULL OR scene.BookID = @BookID)
    GROUP BY scene.SceneID;
END;
GO
