SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

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

    SELECT ch.CharacterID,
        ch.CharacterName,
        ch.ShortName,
        ch.DefaultDescription AS Description,
        ch.EyeColour,
        ch.Height,
        ch.AgeAtSeriesStart,
        ISNULL(sceneCounts.SceneCount, 0) AS SceneCount,
        ISNULL(povCounts.PovSceneCount, 0) AS PovSceneCount,
        ISNULL(knowledge.KnowledgeCount, 0) AS KnowledgeCount,
        ISNULL(relationships.RelationshipCount, 0) AS RelationshipCount,
        ISNULL(sceneCounts.MissingLocationCount, 0) AS MissingLocationCount,
        firstScene.SceneID AS FirstSceneID,
        firstScene.SceneLabel AS FirstSceneLabel,
        lastScene.SceneID AS LastSceneID,
        lastScene.SceneLabel AS LastSceneLabel,
        locations.LocationHistory
    FROM dbo.Characters ch
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT sc.SceneID) AS SceneCount,
            COUNT(DISTINCT CASE WHEN sc.LocationID IS NULL THEN sc.SceneCharacterID END) AS MissingLocationCount
        FROM dbo.SceneCharacters sc
        INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND sc.CharacterID = ch.CharacterID
    ) sceneCounts
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT s.SceneID) AS PovSceneCount
        FROM dbo.Scenes s
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND s.IsArchived = 0 AND COALESCE(s.POVCharacterID, c.POVCharacterID) = ch.CharacterID
    ) povCounts
    OUTER APPLY
    (
        SELECT COUNT(*) AS KnowledgeCount
        FROM dbo.CharacterKnowledge ck
        INNER JOIN dbo.Scenes s ON s.SceneID = ck.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND ck.CharacterID = ch.CharacterID
    ) knowledge
    OUTER APPLY
    (
        SELECT COUNT(*) AS RelationshipCount
        FROM dbo.CharacterRelationships cr
        WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0
            AND (cr.CharacterAID = ch.CharacterID OR cr.CharacterBID = ch.CharacterID)
    ) relationships
    OUTER APPLY
    (
        SELECT TOP (1) s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.SceneCharacters sc
        INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND sc.CharacterID = ch.CharacterID
        ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID
    ) firstScene
    OUTER APPLY
    (
        SELECT TOP (1) s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.SceneCharacters sc
        INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND sc.CharacterID = ch.CharacterID
        ORDER BY b.SortOrder DESC, c.SortOrder DESC, s.SortOrder DESC, s.SceneID DESC
    ) lastScene
    OUTER APPLY
    (
        SELECT STRING_AGG(CONVERT(nvarchar(max), location.LocationPath), N', ') AS LocationHistory
        FROM
        (
            SELECT DISTINCT lp.LocationPath
            FROM dbo.SceneCharacters sc
            INNER JOIN dbo.LocationPaths lp ON lp.LocationID = sc.LocationID
            WHERE sc.CharacterID = ch.CharacterID AND lp.ProjectID = @ProjectID
        ) location
    ) locations
    WHERE ch.ProjectID = @ProjectID AND ch.IsArchived = 0
    ORDER BY ch.CharacterName;
END;
GO

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

    SELECT pt.PlotThreadID,
        pt.ThreadTitle,
        pl.PlotLineName,
        tt.TypeName AS ThreadTypeName,
        ts.StatusName AS ThreadStatusName,
        pt.Importance,
        ISNULL(events.EventCount, 0) AS EventCount,
        introduced.SceneID AS IntroducedSceneID,
        introduced.SceneLabel AS IntroducedSceneLabel,
        lastTouched.SceneID AS LastTouchedSceneID,
        lastTouched.SceneLabel AS LastTouchedSceneLabel,
        planned.SceneID AS PlannedResolutionSceneID,
        planned.SceneLabel AS PlannedResolutionSceneLabel,
        actual.SceneID AS ActualResolutionSceneID,
        actual.SceneLabel AS ActualResolutionSceneLabel,
        events.EventHistory,
        ISNULL(warnings.WarningCount, 0) AS WarningCount
    FROM dbo.PlotThreads pt
    INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.IsArchived = 0
    INNER JOIN dbo.ThreadTypes tt ON tt.ThreadTypeID = pt.ThreadTypeID
    INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
    OUTER APPLY
    (
        SELECT s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.Scenes s
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
        INNER JOIN dbo.Books b ON b.BookID = c.BookID
        WHERE s.SceneID = pt.IntroducedSceneID
    ) introduced
    OUTER APPLY
    (
        SELECT s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.Scenes s
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
        INNER JOIN dbo.Books b ON b.BookID = c.BookID
        WHERE s.SceneID = pt.PlannedResolutionSceneID
    ) planned
    OUTER APPLY
    (
        SELECT s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.Scenes s
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
        INNER JOIN dbo.Books b ON b.BookID = c.BookID
        WHERE s.SceneID = pt.ActualResolutionSceneID
    ) actual
    OUTER APPLY
    (
        SELECT COUNT(*) AS EventCount,
            STRING_AGG(CONVERT(nvarchar(max), CONCAT(tet.TypeName, N': ', te.EventTitle)), N' -> ') WITHIN GROUP (ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, te.ThreadEventID) AS EventHistory
        FROM dbo.ThreadEvents te
        INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
        INNER JOIN dbo.Scenes s ON s.SceneID = te.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE te.PlotThreadID = pt.PlotThreadID
    ) events
    OUTER APPLY
    (
        SELECT TOP (1) s.SceneID,
            CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.ThreadEvents te
        INNER JOIN dbo.Scenes s ON s.SceneID = te.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE te.PlotThreadID = pt.PlotThreadID
        ORDER BY b.SortOrder DESC, c.SortOrder DESC, s.SortOrder DESC, te.ThreadEventID DESC
    ) lastTouched
    OUTER APPLY
    (
        SELECT COUNT(*) AS WarningCount
        FROM dbo.ContinuityWarnings cw
        WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
            AND cw.EntityID = pt.PlotThreadID AND cw.EntityType IN (N'PlotThread', N'Thread')
    ) warnings
    WHERE pl.ProjectID = @ProjectID AND pt.IsArchived = 0
    ORDER BY pl.SortOrder, pt.Importance DESC, pt.ThreadTitle;
END;
GO

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

    SELECT sa.StoryAssetID,
        sa.AssetName,
        ak.KindName,
        sa.Importance,
        ast.StateName AS CurrentStateName,
        currentLocation.LocationPath AS CurrentLocationPath,
        sa.IsResolved,
        ISNULL(events.EventCount, 0) AS EventCount,
        ISNULL(dependencies.DependencyCount, 0) AS DependencyCount,
        ISNULL(custody.CustodyCount, 0) AS CustodyCount,
        ISNULL(sceneLocations.SceneCount, 0) AS SceneCount,
        events.EventHistory,
        dependencies.DependencySummary,
        custody.CustodySummary,
        ISNULL(warnings.WarningCount, 0) AS WarningCount
    FROM dbo.StoryAssets sa
    INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
    LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
    LEFT JOIN dbo.LocationPaths currentLocation ON currentLocation.LocationID = sa.CurrentLocationID
    OUTER APPLY
    (
        SELECT COUNT(*) AS EventCount,
            STRING_AGG(CONVERT(nvarchar(max), CONCAT(aet.TypeName, N': ', ae.EventTitle)), N' -> ') WITHIN GROUP (ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, ae.AssetEventID) AS EventHistory
        FROM dbo.AssetEvents ae
        INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
        INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE ae.StoryAssetID = sa.StoryAssetID
    ) events
    OUTER APPLY
    (
        SELECT COUNT(*) AS DependencyCount,
            STRING_AGG(CONVERT(nvarchar(max), CONCAT(source.AssetName, N' ', adt.TypeName, N' ', target.AssetName)), N'; ') AS DependencySummary
        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
        WHERE ad.IsArchived = 0 AND (ad.SourceAssetID = sa.StoryAssetID OR ad.TargetAssetID = sa.StoryAssetID)
    ) dependencies
    OUTER APPLY
    (
        SELECT COUNT(*) AS CustodyCount,
            STRING_AGG(CONVERT(nvarchar(max), CONCAT(acet.TypeName, N': ', ISNULL(chars.CharacterNames, N'No named holder'))), N'; ') AS CustodySummary
        FROM dbo.AssetCustodyEvents ace
        INNER JOIN dbo.AssetCustodyEventTypes acet ON acet.AssetCustodyEventTypeID = ace.AssetCustodyEventTypeID
        OUTER APPLY
        (
            SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(ch.CharacterName, acec.CharacterNameText)), N', ') AS CharacterNames
            FROM dbo.AssetCustodyEventCharacters acec
            LEFT JOIN dbo.Characters ch ON ch.CharacterID = acec.CharacterID
            WHERE acec.AssetCustodyEventID = ace.AssetCustodyEventID
        ) chars
        WHERE ace.StoryAssetID = sa.StoryAssetID
    ) custody
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT SceneID) AS SceneCount
        FROM
        (
            SELECT ae.SceneID FROM dbo.AssetEvents ae WHERE ae.StoryAssetID = sa.StoryAssetID
            UNION
            SELECT sal.SceneID FROM dbo.SceneAssetLocations sal WHERE sal.StoryAssetID = sa.StoryAssetID
        ) x
    ) sceneLocations
    OUTER APPLY
    (
        SELECT COUNT(*) AS WarningCount
        FROM dbo.ContinuityWarnings cw
        WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
            AND cw.EntityID = sa.StoryAssetID AND cw.EntityType IN (N'StoryAsset', N'Asset')
    ) warnings
    WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0
    ORDER BY sa.Importance DESC, sa.AssetName;
END;
GO

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

    SELECT cr.CharacterRelationshipID,
        cr.CharacterAID,
        ca.CharacterName AS CharacterAName,
        cr.CharacterBID,
        cb.CharacterName AS CharacterBName,
        rt.TypeName AS RelationshipTypeName,
        currentState.StateName AS CurrentStateName,
        ISNULL(events.EventCount, 0) AS EventCount,
        firstEvent.SceneLabel AS FirstEventLabel,
        events.EventHistory
    FROM dbo.CharacterRelationships cr
    INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
    INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
    INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
    OUTER APPLY
    (
        SELECT TOP (1) rs.StateName
        FROM dbo.RelationshipEvents re
        INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID
        INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID
        ORDER BY b.SortOrder DESC, c.SortOrder DESC, s.SortOrder DESC, re.RelationshipEventID DESC
    ) currentState
    OUTER APPLY
    (
        SELECT COUNT(*) AS EventCount,
            STRING_AGG(CONVERT(nvarchar(max), CONCAT(rs.StateName, ISNULL(N': ' + re.Description, N''))), N' -> ') WITHIN GROUP (ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, re.RelationshipEventID) AS EventHistory
        FROM dbo.RelationshipEvents re
        INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID
        INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID
    ) events
    OUTER APPLY
    (
        SELECT TOP (1) CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel
        FROM dbo.RelationshipEvents re
        INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID
        ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, re.RelationshipEventID
    ) firstEvent
    WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0
    ORDER BY ca.CharacterName, cb.CharacterName;
END;
GO

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

    SELECT lp.LocationID,
        lp.LocationName,
        lp.LocationPath,
        lt.TypeName AS LocationTypeName,
        ISNULL(sceneCounts.SceneCount, 0) AS SceneCount,
        ISNULL(characterCounts.CharacterCount, 0) AS CharacterCount,
        ISNULL(assetCounts.AssetCount, 0) AS AssetCount,
        ISNULL(relationshipCounts.RelationshipCount, 0) AS RelationshipCount,
        related.RelatedLocations
    FROM dbo.LocationPaths lp
    LEFT JOIN dbo.LocationTypes lt ON lt.LocationTypeID = lp.LocationTypeID
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT s.SceneID) AS SceneCount
        FROM dbo.Scenes s
        INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
        INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
        WHERE b.ProjectID = @ProjectID AND s.IsArchived = 0 AND s.PrimaryLocationID = lp.LocationID
    ) sceneCounts
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT sc.CharacterID) AS CharacterCount
        FROM dbo.SceneCharacters sc
        WHERE sc.LocationID = lp.LocationID
    ) characterCounts
    OUTER APPLY
    (
        SELECT COUNT(DISTINCT sa.StoryAssetID) AS AssetCount
        FROM dbo.StoryAssets sa
        WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND sa.CurrentLocationID = lp.LocationID
    ) assetCounts
    OUTER APPLY
    (
        SELECT COUNT(*) AS RelationshipCount
        FROM dbo.LocationRelationships lr
        WHERE lr.IsArchived = 0 AND (lr.FromLocationID = lp.LocationID OR lr.ToLocationID = lp.LocationID)
    ) relationshipCounts
    OUTER APPLY
    (
        SELECT STRING_AGG(CONVERT(nvarchar(max), relatedLocation.LocationPath), N', ') AS RelatedLocations
        FROM
        (
            SELECT DISTINCT other.LocationPath
            FROM dbo.LocationRelationships lr
            INNER JOIN dbo.LocationPaths other ON other.LocationID = CASE WHEN lr.FromLocationID = lp.LocationID THEN lr.ToLocationID ELSE lr.FromLocationID END
            WHERE lr.IsArchived = 0 AND (lr.FromLocationID = lp.LocationID OR lr.ToLocationID = lp.LocationID)
        ) relatedLocation
    ) related
    WHERE lp.ProjectID = @ProjectID
    ORDER BY lp.LocationPath;
END;
GO

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

    SELECT b.BookID,
        b.BookTitle,
        c.ChapterID,
        c.ChapterNumber,
        c.ChapterTitle,
        s.SceneID,
        s.SceneNumber,
        s.SceneTitle,
        s.Summary,
        pov.CharacterName AS PovCharacterName,
        location.LocationPath,
        rs.StatusName AS RevisionStatusName
    FROM dbo.Books b
    INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0
    INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
    LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID)
    LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID
    WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0
    ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID;
END;
GO

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

    SELECT N'Plot Thread' AS Category, N'Info' AS SeverityName, pt.ThreadTitle AS Title,
        CONCAT(N'Open thread in ', pl.PlotLineName, N' with status ', ts.StatusName) AS Detail,
        CAST(NULL AS int) AS SceneID, pt.PlotThreadID AS EntityID, N'PlotThread' AS EntityType
    FROM dbo.PlotThreads pt
    INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
    INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
    WHERE pt.IsArchived = 0 AND ts.IsOpenStatus = 1
    UNION ALL
    SELECT N'Story Asset', N'Info', sa.AssetName,
        CONCAT(N'Important unresolved asset. Importance ', sa.Importance, N'.'),
        CAST(NULL AS int), sa.StoryAssetID, N'StoryAsset'
    FROM dbo.StoryAssets sa
    WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND sa.IsResolved = 0 AND sa.Importance >= 6
    UNION ALL
    SELECT N'Scene', N'Warning', s.SceneTitle,
        CONCAT(N'Scene ', s.SceneNumber, N' is marked Needs Work.'),
        s.SceneID, s.SceneID, N'Scene'
    FROM dbo.Scenes s
    INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
    WHERE s.IsArchived = 0 AND rs.StatusName = N'Needs Work'
    UNION ALL
    SELECT N'Scene Purpose', N'Info', s.SceneTitle,
        N'No primary scene purpose has been assigned yet.',
        s.SceneID, s.SceneID, N'Scene'
    FROM dbo.Scenes s
    INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    WHERE s.IsArchived = 0 AND NOT EXISTS (SELECT 1 FROM dbo.ScenePurposes sp WHERE sp.SceneID = s.SceneID)
    UNION ALL
    SELECT N'Scene Outcome', N'Info', s.SceneTitle,
        N'Outcome notes are empty.',
        s.SceneID, s.SceneID, N'Scene'
    FROM dbo.Scenes s
    INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    WHERE s.IsArchived = 0 AND NULLIF(LTRIM(RTRIM(s.SceneOutcomeNotes)), N'') IS NULL
    UNION ALL
    SELECT N'Warning', ws.SeverityName, cw.Message, cw.Details,
        cw.SceneID, cw.EntityID, cw.EntityType
    FROM dbo.ContinuityWarnings cw
    INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
    WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0;
END;
GO

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

    SELECT N'Scene revision' AS GroupName,
        COUNT(*) AS ItemCount,
        rs.StatusName AS Detail,
        CAST(NULL AS int) AS SceneID,
        CAST(NULL AS int) AS ChapterID
    FROM dbo.Scenes s
    INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
    WHERE s.IsArchived = 0
    GROUP BY rs.StatusName, rs.SortOrder
    UNION ALL
    SELECT N'Unfinished chapters',
        COUNT(DISTINCT c.ChapterID),
        N'Chapters with at least one scene not revised, polished, locked, or cut',
        CAST(NULL AS int),
        CAST(NULL AS int)
    FROM dbo.Chapters c
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
    INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
    WHERE c.IsArchived = 0 AND rs.StatusName NOT IN (N'Revised', N'Polished', N'Locked', N'Cut Candidate');
END;
GO

CREATE OR ALTER PROCEDURE dbo.StoryBible_Search
    @ProjectID int,
    @Query nvarchar(200) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    IF NULLIF(LTRIM(RTRIM(@Query)), N'') IS NULL
        RETURN;

    DECLARE @Term nvarchar(204) = N'%' + LTRIM(RTRIM(@Query)) + N'%';

    SELECT N'Character' AS ResultType, ch.CharacterID AS EntityID, ch.CharacterName AS Title,
        ch.DefaultDescription AS Detail, CAST(NULL AS int) AS SceneID
    FROM dbo.Characters ch
    WHERE ch.ProjectID = @ProjectID AND ch.IsArchived = 0
        AND (ch.CharacterName LIKE @Term OR ch.ShortName LIKE @Term OR ch.DefaultDescription LIKE @Term)
    UNION ALL
    SELECT N'Scene', s.SceneID, CONCAT(N'Scene ', s.SceneNumber, N': ', s.SceneTitle),
        s.Summary, s.SceneID
    FROM dbo.Scenes s
    INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
    INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
    WHERE s.IsArchived = 0 AND (s.SceneTitle LIKE @Term OR s.Summary LIKE @Term OR s.ScenePurposeNotes LIKE @Term OR s.SceneOutcomeNotes LIKE @Term)
    UNION ALL
    SELECT N'Plot Thread', pt.PlotThreadID, pt.ThreadTitle, pt.Summary, CAST(NULL AS int)
    FROM dbo.PlotThreads pt
    INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
    WHERE pt.IsArchived = 0 AND (pt.ThreadTitle LIKE @Term OR pt.Summary LIKE @Term)
    UNION ALL
    SELECT N'Story Asset', sa.StoryAssetID, sa.AssetName, sa.Description, CAST(NULL AS int)
    FROM dbo.StoryAssets sa
    WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND (sa.AssetName LIKE @Term OR sa.Description LIKE @Term)
    UNION ALL
    SELECT N'Location', lp.LocationID, lp.LocationPath, lp.Description, CAST(NULL AS int)
    FROM dbo.LocationPaths lp
    WHERE lp.ProjectID = @ProjectID AND (lp.LocationName LIKE @Term OR lp.LocationPath LIKE @Term OR lp.Description LIKE @Term)
    UNION ALL
    SELECT N'Relationship', cr.CharacterRelationshipID, CONCAT(ca.CharacterName, N' - ', cb.CharacterName),
        CONCAT(rt.TypeName, N'. ', ISNULL(cr.Notes, N'')), CAST(NULL AS int)
    FROM dbo.CharacterRelationships cr
    INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
    INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
    INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
    WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0
        AND (ca.CharacterName LIKE @Term OR cb.CharacterName LIKE @Term OR rt.TypeName LIKE @Term OR cr.Notes LIKE @Term)
    UNION ALL
    SELECT N'Warning', cw.ContinuityWarningID, cw.Message, cw.Details, cw.SceneID
    FROM dbo.ContinuityWarnings cw
    WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
        AND (cw.Message LIKE @Term OR cw.Details LIKE @Term OR cw.EntityType LIKE @Term)
    ORDER BY ResultType, Title;
END;
GO
