SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

IF OBJECT_ID(N'dbo.ProjectUserAccess', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.ProjectUserAccess
    (
        ProjectUserAccessID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ProjectUserAccess PRIMARY KEY,
        ProjectID int NOT NULL,
        UserID int NOT NULL,
        AccessRole nvarchar(50) NOT NULL,
        InvitedByUserID int NULL,
        InvitedDateUTC datetime2 NULL,
        AcceptedDateUTC datetime2 NULL,
        IsActive bit NOT NULL CONSTRAINT DF_ProjectUserAccess_IsActive DEFAULT 1,
        CONSTRAINT FK_ProjectUserAccess_Project FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
        CONSTRAINT FK_ProjectUserAccess_User FOREIGN KEY (UserID) REFERENCES dbo.AppUser(UserID),
        CONSTRAINT FK_ProjectUserAccess_InvitedByUser FOREIGN KEY (InvitedByUserID) REFERENCES dbo.AppUser(UserID),
        CONSTRAINT CK_ProjectUserAccess_AccessRole CHECK (AccessRole IN (N'Owner', N'Collaborator'))
    );
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_ProjectUserAccess_Project_User_Active' AND object_id = OBJECT_ID(N'dbo.ProjectUserAccess'))
    CREATE UNIQUE INDEX UX_ProjectUserAccess_Project_User_Active ON dbo.ProjectUserAccess(ProjectID, UserID) WHERE IsActive = 1;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ProjectUserAccess_User_Project' AND object_id = OBJECT_ID(N'dbo.ProjectUserAccess'))
    CREATE INDEX IX_ProjectUserAccess_User_Project ON dbo.ProjectUserAccess(UserID, ProjectID, IsActive, AccessRole);
GO

DECLARE @LegacyOwnerUserID int;
SELECT TOP (1) @LegacyOwnerUserID = UserID
FROM dbo.AppUser
ORDER BY UserID;

IF @LegacyOwnerUserID IS NOT NULL
BEGIN
    INSERT INTO dbo.ProjectUserAccess (ProjectID, UserID, AccessRole, AcceptedDateUTC, IsActive)
    SELECT p.ProjectID, @LegacyOwnerUserID, N'Owner', SYSUTCDATETIME(), 1
    FROM dbo.Projects p
    WHERE NOT EXISTS
    (
        SELECT 1
        FROM dbo.ProjectUserAccess pua
        WHERE pua.ProjectID = p.ProjectID
            AND pua.UserID = @LegacyOwnerUserID
            AND pua.IsActive = 1
    );
END;
GO

CREATE OR ALTER PROCEDURE dbo.ProjectAccess_EnsureOwner
    @ProjectID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS
    (
        SELECT 1
        FROM dbo.ProjectUserAccess
        WHERE ProjectID = @ProjectID
            AND UserID = @UserID
            AND IsActive = 1
    )
    BEGIN
        INSERT INTO dbo.ProjectUserAccess (ProjectID, UserID, AccessRole, AcceptedDateUTC, IsActive)
        VALUES (@ProjectID, @UserID, N'Owner', SYSUTCDATETIME(), 1);
    END;
END;
GO

CREATE OR ALTER PROCEDURE dbo.ProjectAccess_UserCanAccess
    @ProjectID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT CAST(CASE WHEN EXISTS
    (
        SELECT 1
        FROM dbo.ProjectUserAccess pua
        INNER JOIN dbo.Projects p ON p.ProjectID = pua.ProjectID
        WHERE pua.ProjectID = @ProjectID
            AND pua.UserID = @UserID
            AND pua.IsActive = 1
            AND p.IsArchived = 0
    )
    THEN 1 ELSE 0 END AS bit);
END;
GO

CREATE OR ALTER PROCEDURE dbo.ProjectAccess_UserCanOwn
    @ProjectID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT CAST(CASE WHEN EXISTS
    (
        SELECT 1
        FROM dbo.ProjectUserAccess pua
        INNER JOIN dbo.Projects p ON p.ProjectID = pua.ProjectID
        WHERE pua.ProjectID = @ProjectID
            AND pua.UserID = @UserID
            AND pua.AccessRole = N'Owner'
            AND pua.IsActive = 1
            AND p.IsArchived = 0
    )
    THEN 1 ELSE 0 END AS bit);
END;
GO

CREATE OR ALTER PROCEDURE dbo.ProjectAccess_GetProjectIDForEntity
    @EntityType nvarchar(100),
    @EntityID int
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ProjectID int = NULL;

    IF @EntityType = N'Project'
        SELECT @ProjectID = ProjectID FROM dbo.Projects WHERE ProjectID = @EntityID;
    ELSE IF @EntityType = N'Book'
        SELECT @ProjectID = ProjectID FROM dbo.Books WHERE BookID = @EntityID;
    ELSE IF @EntityType = N'Chapter'
        SELECT @ProjectID = b.ProjectID FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE c.ChapterID = @EntityID;
    ELSE IF @EntityType = N'Scene'
        SELECT @ProjectID = b.ProjectID 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 = @EntityID;
    ELSE IF @EntityType = N'Character'
        SELECT @ProjectID = ProjectID FROM dbo.Characters WHERE CharacterID = @EntityID;
    ELSE IF @EntityType = N'SceneCharacter'
        SELECT @ProjectID = ch.ProjectID FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID WHERE sc.SceneCharacterID = @EntityID;
    ELSE IF @EntityType = N'CharacterAttributeEvent'
        SELECT @ProjectID = ch.ProjectID FROM dbo.CharacterAttributeEvents cae INNER JOIN dbo.Characters ch ON ch.CharacterID = cae.CharacterID WHERE cae.CharacterAttributeEventID = @EntityID;
    ELSE IF @EntityType = N'CharacterKnowledge'
        SELECT @ProjectID = ch.ProjectID FROM dbo.CharacterKnowledge ck INNER JOIN dbo.Characters ch ON ch.CharacterID = ck.CharacterID WHERE ck.CharacterKnowledgeID = @EntityID;
    ELSE IF @EntityType = N'CharacterRelationship'
        SELECT @ProjectID = ProjectID FROM dbo.CharacterRelationships WHERE CharacterRelationshipID = @EntityID;
    ELSE IF @EntityType = N'RelationshipEvent'
        SELECT @ProjectID = cr.ProjectID FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID WHERE re.RelationshipEventID = @EntityID;
    ELSE IF @EntityType = N'StoryAsset'
        SELECT @ProjectID = ProjectID FROM dbo.StoryAssets WHERE StoryAssetID = @EntityID;
    ELSE IF @EntityType = N'AssetEvent'
        SELECT @ProjectID = sa.ProjectID FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID WHERE ae.AssetEventID = @EntityID;
    ELSE IF @EntityType = N'AssetDependency'
        SELECT @ProjectID = sa.ProjectID FROM dbo.AssetDependencies ad INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ad.SourceAssetID WHERE ad.AssetDependencyID = @EntityID;
    ELSE IF @EntityType = N'AssetCustodyEvent'
        SELECT @ProjectID = sa.ProjectID FROM dbo.AssetCustodyEvents ace INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID WHERE ace.AssetCustodyEventID = @EntityID;
    ELSE IF @EntityType = N'Location'
        SELECT @ProjectID = ProjectID FROM dbo.Locations WHERE LocationID = @EntityID;
    ELSE IF @EntityType = N'LocationRelationship'
        SELECT @ProjectID = l.ProjectID FROM dbo.LocationRelationships lr INNER JOIN dbo.Locations l ON l.LocationID = lr.FromLocationID WHERE lr.LocationRelationshipID = @EntityID;
    ELSE IF @EntityType = N'SceneAssetLocation'
        SELECT @ProjectID = l.ProjectID FROM dbo.SceneAssetLocations sal INNER JOIN dbo.Locations l ON l.LocationID = sal.LocationID WHERE sal.SceneAssetLocationID = @EntityID;
    ELSE IF @EntityType = N'PlotLine'
        SELECT @ProjectID = ProjectID FROM dbo.PlotLines WHERE PlotLineID = @EntityID;
    ELSE IF @EntityType = N'PlotThread'
        SELECT @ProjectID = pl.ProjectID FROM dbo.PlotThreads pt INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE pt.PlotThreadID = @EntityID;
    ELSE IF @EntityType = N'ThreadEvent'
        SELECT @ProjectID = pl.ProjectID FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE te.ThreadEventID = @EntityID;
    ELSE IF @EntityType = N'SceneDependency'
        SELECT @ProjectID = ProjectID FROM dbo.SceneDependencies WHERE SceneDependencyID = @EntityID;
    ELSE IF @EntityType = N'SceneMetricType'
        SELECT @ProjectID = ProjectID FROM dbo.SceneMetricTypes WHERE MetricTypeID = @EntityID;
    ELSE IF @EntityType = N'TimelineViewPreset'
        SELECT @ProjectID = ProjectID FROM dbo.TimelineViewPresets WHERE TimelineViewPresetID = @EntityID;
    ELSE IF @EntityType = N'ContinuityWarning'
        SELECT @ProjectID = ProjectID FROM dbo.ContinuityWarnings WHERE ContinuityWarningID = @EntityID;
    ELSE IF @EntityType = N'Scenario'
        SELECT @ProjectID = ProjectID FROM dbo.Scenarios WHERE ScenarioID = @EntityID;
    ELSE IF @EntityType = N'ScenarioWarning'
        SELECT @ProjectID = sc.ProjectID FROM dbo.ScenarioWarnings sw INNER JOIN dbo.Scenarios sc ON sc.ScenarioID = sw.ScenarioID WHERE sw.ScenarioWarningID = @EntityID;
    ELSE IF @EntityType = N'ProjectRestorePoint'
        SELECT @ProjectID = ProjectID FROM dbo.ProjectRestorePoints WHERE ProjectRestorePointID = @EntityID;
    ELSE IF @EntityType = N'ChapterGoal'
        SELECT @ProjectID = b.ProjectID FROM dbo.ChapterGoals cg INNER JOIN dbo.Chapters c ON c.ChapterID = cg.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE cg.ChapterGoalID = @EntityID;
    ELSE IF @EntityType = N'SceneWorkflow'
        SELECT @ProjectID = b.ProjectID FROM dbo.SceneWorkflow sw INNER JOIN dbo.Scenes s ON s.SceneID = sw.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sw.SceneWorkflowID = @EntityID;
    ELSE IF @EntityType = N'SceneNote'
        SELECT @ProjectID = b.ProjectID FROM dbo.SceneNotes sn INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sn.SceneNoteID = @EntityID;
    ELSE IF @EntityType = N'SceneChecklistItem'
        SELECT @ProjectID = b.ProjectID FROM dbo.SceneChecklistItems sci INNER JOIN dbo.Scenes s ON s.SceneID = sci.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sci.SceneChecklistItemID = @EntityID;
    ELSE IF @EntityType = N'SceneAttachment'
        SELECT @ProjectID = b.ProjectID FROM dbo.SceneAttachments sa INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sa.SceneAttachmentID = @EntityID;

    SELECT @ProjectID AS ProjectID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.ProjectAccess_UserCanAccessEntity
    @EntityType nvarchar(100),
    @EntityID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ProjectID int;
    DECLARE @Project TABLE (ProjectID int NULL);

    INSERT INTO @Project
    EXEC dbo.ProjectAccess_GetProjectIDForEntity @EntityType = @EntityType, @EntityID = @EntityID;

    SELECT @ProjectID = ProjectID FROM @Project;

    IF @ProjectID IS NULL
    BEGIN
        SELECT CAST(0 AS bit);
        RETURN;
    END;

    EXEC dbo.ProjectAccess_UserCanAccess @ProjectID = @ProjectID, @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Project_ListForUser
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT p.ProjectID, p.ProjectName, p.Description, p.CreatedDate, p.UpdatedDate, p.IsArchived
    FROM dbo.Projects p
    INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
    WHERE p.IsArchived = 0
        AND pua.UserID = @UserID
        AND pua.IsActive = 1
    ORDER BY p.ProjectName;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Project_GetForUser
    @ProjectID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT p.ProjectID, p.ProjectName, p.Description, p.CreatedDate, p.UpdatedDate, p.IsArchived
    FROM dbo.Projects p
    INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
    WHERE p.ProjectID = @ProjectID
        AND p.IsArchived = 0
        AND pua.UserID = @UserID
        AND pua.IsActive = 1;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Project_Save
    @ProjectID int = NULL,
    @ProjectName nvarchar(200),
    @Description nvarchar(max) = NULL,
    @GenreMetricPresetKey nvarchar(50) = NULL,
    @UserID int = NULL
AS
BEGIN
    SET NOCOUNT ON;

    IF @ProjectID IS NULL OR @ProjectID = 0
    BEGIN
        INSERT dbo.Projects (ProjectName, Description)
        VALUES (@ProjectName, @Description);

        SET @ProjectID = CAST(SCOPE_IDENTITY() AS int);

        IF @UserID IS NOT NULL
            EXEC dbo.ProjectAccess_EnsureOwner @ProjectID = @ProjectID, @UserID = @UserID;

        EXEC dbo.ProjectMetricPreset_Apply @ProjectID = @ProjectID, @GenreMetricPresetKey = @GenreMetricPresetKey;

        SELECT @ProjectID AS ProjectID;
        RETURN;
    END;

    UPDATE p
    SET ProjectName = @ProjectName,
        Description = @Description,
        UpdatedDate = SYSUTCDATETIME()
    FROM dbo.Projects p
    WHERE p.ProjectID = @ProjectID
        AND
        (
            @UserID IS NULL
            OR EXISTS
            (
                SELECT 1
                FROM dbo.ProjectUserAccess pua
                WHERE pua.ProjectID = p.ProjectID
                    AND pua.UserID = @UserID
                    AND pua.IsActive = 1
            )
        );

    SELECT CASE WHEN @@ROWCOUNT = 1 THEN @ProjectID ELSE 0 END AS ProjectID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Project_ArchiveForUser
    @ProjectID int,
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE p
    SET IsArchived = 1,
        UpdatedDate = SYSUTCDATETIME()
    FROM dbo.Projects p
    WHERE p.ProjectID = @ProjectID
        AND EXISTS
        (
            SELECT 1
            FROM dbo.ProjectUserAccess pua
            WHERE pua.ProjectID = p.ProjectID
                AND pua.UserID = @UserID
                AND pua.AccessRole = N'Owner'
                AND pua.IsActive = 1
        );
END;
GO

CREATE OR ALTER PROCEDURE dbo.Archive_ListForUser
    @UserID int,
    @ProjectID int = NULL,
    @EntityType nvarchar(80) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Archived TABLE
    (
        EntityType nvarchar(80),
        EntityID int,
        DisplayName nvarchar(500),
        ProjectID int,
        ProjectName nvarchar(200),
        BookID int NULL,
        BookTitle nvarchar(200) NULL,
        ArchivedDate datetime2 NULL,
        ArchivedReason nvarchar(500) NULL,
        UpdatedDate datetime2
    );

    INSERT INTO @Archived
    EXEC dbo.Archive_List @ProjectID = @ProjectID, @EntityType = @EntityType;

    SELECT a.EntityType, a.EntityID, a.DisplayName, a.ProjectID, a.ProjectName, a.BookID, a.BookTitle,
        a.ArchivedDate, a.ArchivedReason, a.UpdatedDate
    FROM @Archived a
    INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = a.ProjectID
    WHERE pua.UserID = @UserID
        AND pua.IsActive = 1
    ORDER BY ISNULL(a.ArchivedDate, a.UpdatedDate) DESC, a.EntityType, a.DisplayName;
END;
GO

CREATE OR ALTER PROCEDURE dbo.Archive_ProjectFilterListForUser
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT p.ProjectID, p.ProjectName, p.Description, p.CreatedDate, p.UpdatedDate, p.IsArchived
    FROM dbo.Projects p
    INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
    WHERE pua.UserID = @UserID
        AND pua.IsActive = 1
    ORDER BY p.IsArchived, p.ProjectName;
END;
GO
