IF OBJECT_ID(N'dbo.FloorPlanBlocks', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.FloorPlanBlocks
    (
        FloorPlanBlockID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlanBlocks PRIMARY KEY,
        FloorPlanFloorID int NOT NULL,
        LocationID int NOT NULL,
        X int NOT NULL,
        Y int NOT NULL,
        WidthCells int NOT NULL,
        HeightCells int NOT NULL,
        BlockType nvarchar(40) NOT NULL CONSTRAINT DF_FloorPlanBlocks_BlockType DEFAULT N'Room',
        LabelOverride nvarchar(200) NULL,
        Notes nvarchar(max) NULL,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlanBlocks_CreatedDate DEFAULT SYSUTCDATETIME(),
        ModifiedDate datetime2 NULL
    );
END;
GO

IF OBJECT_ID(N'dbo.FloorPlanFloors', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.FloorPlanFloors
    (
        FloorPlanFloorID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlanFloors PRIMARY KEY,
        FloorPlanID int NOT NULL,
        Name nvarchar(100) NOT NULL,
        SortOrder int NOT NULL CONSTRAINT DF_FloorPlanFloors_SortOrder DEFAULT 0,
        GridWidth int NOT NULL CONSTRAINT DF_FloorPlanFloors_GridWidth DEFAULT 24,
        GridHeight int NOT NULL CONSTRAINT DF_FloorPlanFloors_GridHeight DEFAULT 16,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlanFloors_CreatedDate DEFAULT SYSUTCDATETIME(),
        ModifiedDate datetime2 NULL
    );
END;
GO

IF OBJECT_ID(N'dbo.FloorPlans', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.FloorPlans
    (
        FloorPlanID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlans PRIMARY KEY,
        ProjectID int NOT NULL,
        Name nvarchar(200) NOT NULL,
        Description nvarchar(max) NULL,
        CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlans_CreatedDate DEFAULT SYSUTCDATETIME(),
        ModifiedDate datetime2 NULL
    );
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlans_Projects')
BEGIN
    ALTER TABLE dbo.FloorPlans
        ADD CONSTRAINT FK_FloorPlans_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanFloors_FloorPlans')
BEGIN
    ALTER TABLE dbo.FloorPlanFloors
        ADD CONSTRAINT FK_FloorPlanFloors_FloorPlans FOREIGN KEY (FloorPlanID) REFERENCES dbo.FloorPlans(FloorPlanID) ON DELETE CASCADE;
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanBlocks_FloorPlanFloors')
BEGIN
    ALTER TABLE dbo.FloorPlanBlocks
        ADD CONSTRAINT FK_FloorPlanBlocks_FloorPlanFloors FOREIGN KEY (FloorPlanFloorID) REFERENCES dbo.FloorPlanFloors(FloorPlanFloorID) ON DELETE CASCADE;
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanBlocks_Locations')
BEGIN
    ALTER TABLE dbo.FloorPlanBlocks
        ADD CONSTRAINT FK_FloorPlanBlocks_Locations FOREIGN KEY (LocationID) REFERENCES dbo.Locations(LocationID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanFloors_GridSize')
BEGIN
    ALTER TABLE dbo.FloorPlanFloors
        ADD CONSTRAINT CK_FloorPlanFloors_GridSize CHECK (GridWidth BETWEEN 8 AND 80 AND GridHeight BETWEEN 8 AND 80);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanBlocks_PositionSize')
BEGIN
    ALTER TABLE dbo.FloorPlanBlocks
        ADD CONSTRAINT CK_FloorPlanBlocks_PositionSize CHECK (X >= 0 AND Y >= 0 AND WidthCells >= 1 AND HeightCells >= 1);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanBlocks_BlockType')
BEGIN
    ALTER TABLE dbo.FloorPlanBlocks
        ADD CONSTRAINT CK_FloorPlanBlocks_BlockType CHECK (BlockType IN (N'Room', N'Corridor', N'OpenSpace', N'Exterior', N'Other'));
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlans_ProjectID' AND object_id = OBJECT_ID(N'dbo.FloorPlans'))
BEGIN
    CREATE INDEX IX_FloorPlans_ProjectID ON dbo.FloorPlans(ProjectID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanFloors_FloorPlanID' AND object_id = OBJECT_ID(N'dbo.FloorPlanFloors'))
BEGIN
    CREATE INDEX IX_FloorPlanFloors_FloorPlanID ON dbo.FloorPlanFloors(FloorPlanID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanBlocks_FloorPlanFloorID' AND object_id = OBJECT_ID(N'dbo.FloorPlanBlocks'))
BEGIN
    CREATE INDEX IX_FloorPlanBlocks_FloorPlanFloorID ON dbo.FloorPlanBlocks(FloorPlanFloorID);
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanBlocks_LocationID' AND object_id = OBJECT_ID(N'dbo.FloorPlanBlocks'))
BEGIN
    CREATE INDEX IX_FloorPlanBlocks_LocationID ON dbo.FloorPlanBlocks(LocationID);
END;
GO

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

    SELECT
        fp.FloorPlanID,
        fp.ProjectID,
        fp.Name,
        fp.Description,
        fp.CreatedDate,
        fp.ModifiedDate,
        COUNT(DISTINCT fpf.FloorPlanFloorID) AS FloorCount,
        COUNT(fpb.FloorPlanBlockID) AS BlockCount
    FROM dbo.FloorPlans fp
    LEFT JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanID = fp.FloorPlanID
    LEFT JOIN dbo.FloorPlanBlocks fpb ON fpb.FloorPlanFloorID = fpf.FloorPlanFloorID
    WHERE fp.ProjectID = @ProjectID
    GROUP BY fp.FloorPlanID, fp.ProjectID, fp.Name, fp.Description, fp.CreatedDate, fp.ModifiedDate
    ORDER BY fp.Name;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlan_Get
    @FloorPlanID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT FloorPlanID, ProjectID, Name, Description, CreatedDate, ModifiedDate
    FROM dbo.FloorPlans
    WHERE FloorPlanID = @FloorPlanID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlan_GetEditor
    @FloorPlanID int
AS
BEGIN
    SET NOCOUNT ON;

    EXEC dbo.FloorPlan_Get @FloorPlanID;

    SELECT FloorPlanFloorID, FloorPlanID, Name, SortOrder, GridWidth, GridHeight, CreatedDate, ModifiedDate
    FROM dbo.FloorPlanFloors
    WHERE FloorPlanID = @FloorPlanID
    ORDER BY SortOrder, Name;

    SELECT
        fpb.FloorPlanBlockID,
        fpb.FloorPlanFloorID,
        fpb.LocationID,
        lp.LocationName,
        COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath,
        fpb.X,
        fpb.Y,
        fpb.WidthCells,
        fpb.HeightCells,
        fpb.BlockType,
        fpb.LabelOverride,
        fpb.Notes,
        fpb.CreatedDate,
        fpb.ModifiedDate
    FROM dbo.FloorPlanBlocks fpb
    INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID
    INNER JOIN dbo.LocationPaths lp ON lp.LocationID = fpb.LocationID
    WHERE fpf.FloorPlanID = @FloorPlanID
    ORDER BY fpf.SortOrder, fpb.Y, fpb.X, lp.LocationName;
END;
GO

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

    SET @Name = NULLIF(LTRIM(RTRIM(@Name)), N'');
    IF @Name IS NULL
        THROW 51000, 'Floor plan name is required.', 1;

    IF @FloorPlanID = 0
    BEGIN
        INSERT dbo.FloorPlans (ProjectID, Name, Description)
        VALUES (@ProjectID, @Name, @Description);

        SELECT CONVERT(int, SCOPE_IDENTITY());
        RETURN;
    END;

    UPDATE dbo.FloorPlans
    SET Name = @Name,
        Description = @Description,
        ModifiedDate = SYSUTCDATETIME()
    WHERE FloorPlanID = @FloorPlanID
      AND ProjectID = @ProjectID;

    SELECT @FloorPlanID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlan_Delete
    @FloorPlanID int
AS
BEGIN
    SET NOCOUNT ON;

    DELETE dbo.FloorPlans
    WHERE FloorPlanID = @FloorPlanID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Get
    @FloorPlanFloorID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT FloorPlanFloorID, FloorPlanID, Name, SortOrder, GridWidth, GridHeight, CreatedDate, ModifiedDate
    FROM dbo.FloorPlanFloors
    WHERE FloorPlanFloorID = @FloorPlanFloorID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Save
    @FloorPlanFloorID int,
    @FloorPlanID int,
    @Name nvarchar(100),
    @SortOrder int,
    @GridWidth int,
    @GridHeight int
AS
BEGIN
    SET NOCOUNT ON;

    SET @Name = NULLIF(LTRIM(RTRIM(@Name)), N'');
    IF @Name IS NULL
        THROW 51001, 'Floor name is required.', 1;

    IF @GridWidth NOT BETWEEN 8 AND 80 OR @GridHeight NOT BETWEEN 8 AND 80
        THROW 51002, 'Grid width and height must be between 8 and 80.', 1;

    IF @FloorPlanFloorID = 0
    BEGIN
        INSERT dbo.FloorPlanFloors (FloorPlanID, Name, SortOrder, GridWidth, GridHeight)
        VALUES (@FloorPlanID, @Name, @SortOrder, @GridWidth, @GridHeight);

        SELECT CONVERT(int, SCOPE_IDENTITY());
        RETURN;
    END;

    UPDATE dbo.FloorPlanFloors
    SET Name = @Name,
        SortOrder = @SortOrder,
        GridWidth = @GridWidth,
        GridHeight = @GridHeight,
        ModifiedDate = SYSUTCDATETIME()
    WHERE FloorPlanFloorID = @FloorPlanFloorID
      AND FloorPlanID = @FloorPlanID;

    SELECT @FloorPlanFloorID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Delete
    @FloorPlanFloorID int
AS
BEGIN
    SET NOCOUNT ON;

    DELETE dbo.FloorPlanFloors
    WHERE FloorPlanFloorID = @FloorPlanFloorID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlanBlock_Save
    @FloorPlanBlockID int,
    @FloorPlanFloorID int,
    @LocationID int,
    @X int,
    @Y int,
    @WidthCells int,
    @HeightCells int,
    @BlockType nvarchar(40),
    @LabelOverride nvarchar(200) = NULL,
    @Notes nvarchar(max) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @GridWidth int, @GridHeight int, @ProjectID int;

    SELECT
        @GridWidth = fpf.GridWidth,
        @GridHeight = fpf.GridHeight,
        @ProjectID = fp.ProjectID
    FROM dbo.FloorPlanFloors fpf
    INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID
    WHERE fpf.FloorPlanFloorID = @FloorPlanFloorID;

    IF @ProjectID IS NULL
        THROW 51003, 'Floor not found.', 1;

    IF NOT EXISTS (SELECT 1 FROM dbo.Locations WHERE LocationID = @LocationID AND ProjectID = @ProjectID AND IsArchived = 0)
        THROW 51004, 'Location must belong to this project.', 1;

    IF @BlockType NOT IN (N'Room', N'Corridor', N'OpenSpace', N'Exterior', N'Other')
        THROW 51005, 'Choose a valid block type.', 1;

    IF @X < 0 OR @Y < 0 OR @WidthCells < 1 OR @HeightCells < 1 OR @X + @WidthCells > @GridWidth OR @Y + @HeightCells > @GridHeight
        THROW 51006, 'Block is outside the floor grid.', 1;

    IF @FloorPlanBlockID = 0
    BEGIN
        INSERT dbo.FloorPlanBlocks (FloorPlanFloorID, LocationID, X, Y, WidthCells, HeightCells, BlockType, LabelOverride, Notes)
        VALUES (@FloorPlanFloorID, @LocationID, @X, @Y, @WidthCells, @HeightCells, @BlockType, NULLIF(LTRIM(RTRIM(@LabelOverride)), N''), @Notes);

        SELECT CONVERT(int, SCOPE_IDENTITY());
        RETURN;
    END;

    UPDATE dbo.FloorPlanBlocks
    SET FloorPlanFloorID = @FloorPlanFloorID,
        LocationID = @LocationID,
        X = @X,
        Y = @Y,
        WidthCells = @WidthCells,
        HeightCells = @HeightCells,
        BlockType = @BlockType,
        LabelOverride = NULLIF(LTRIM(RTRIM(@LabelOverride)), N''),
        Notes = @Notes,
        ModifiedDate = SYSUTCDATETIME()
    WHERE FloorPlanBlockID = @FloorPlanBlockID;

    SELECT @FloorPlanBlockID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.FloorPlanBlock_Delete
    @FloorPlanBlockID int
AS
BEGIN
    SET NOCOUNT ON;

    DELETE dbo.FloorPlanBlocks
    WHERE FloorPlanBlockID = @FloorPlanBlockID;
END;
GO

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

    DECLARE @ProjectID int;

    IF @EntityType = N'Project'
        SET @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'StoryAsset'
        SELECT @ProjectID = ProjectID FROM dbo.StoryAssets WHERE StoryAssetID = @EntityID;
    ELSE IF @EntityType = N'Location'
        SELECT @ProjectID = ProjectID FROM dbo.Locations WHERE LocationID = @EntityID;
    ELSE IF @EntityType = N'FloorPlan'
        SELECT @ProjectID = ProjectID FROM dbo.FloorPlans WHERE FloorPlanID = @EntityID;
    ELSE IF @EntityType = N'FloorPlanFloor'
        SELECT @ProjectID = fp.ProjectID FROM dbo.FloorPlanFloors fpf INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fpf.FloorPlanFloorID = @EntityID;
    ELSE IF @EntityType = N'FloorPlanBlock'
        SELECT @ProjectID = fp.ProjectID FROM dbo.FloorPlanBlocks fpb INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fpb.FloorPlanBlockID = @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'CharacterRelationship'
        SELECT @ProjectID = ProjectID FROM dbo.CharacterRelationships WHERE CharacterRelationshipID = @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'TimelineViewPreset'
        SELECT @ProjectID = ProjectID FROM dbo.TimelineViewPresets WHERE TimelineViewPresetID = @EntityID;
    ELSE IF @EntityType = N'ProjectRestorePoint'
        SELECT @ProjectID = ProjectID FROM dbo.ProjectRestorePoints WHERE ProjectRestorePointID = @EntityID;
    ELSE IF @EntityType = N'Scenario'
        SELECT @ProjectID = ProjectID FROM dbo.Scenarios WHERE ScenarioID = @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'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'SceneAssetLocation'
        SELECT @ProjectID = sa.ProjectID FROM dbo.SceneAssetLocations sal INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = sal.StoryAssetID WHERE sal.SceneAssetLocationID = @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'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'SceneDependency'
        SELECT @ProjectID = ProjectID FROM dbo.SceneDependencies WHERE SceneDependencyID = @EntityID;
    ELSE IF @EntityType = N'CharacterKnowledge'
        SELECT @ProjectID = c.ProjectID FROM dbo.CharacterKnowledge ck INNER JOIN dbo.Characters c ON c.CharacterID = ck.CharacterID WHERE ck.CharacterKnowledgeID = @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'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 CONVERT(bit, CASE WHEN EXISTS
    (
        SELECT 1
        FROM dbo.ProjectUserAccess
        WHERE ProjectID = @ProjectID
          AND UserID = @UserID
          AND IsActive = 1
    ) THEN 1 ELSE 0 END);
END;
GO
