-- V016__create_rubro.sql
-- CAT-001: Árbol N-ario de Rubros — tabla fundacional del catálogo comercial.
--
-- Cambios:
--   1. dbo.Rubro (adjacency list, self-FK, soft-delete, SYSTEM_VERSIONING ON, retention 10 años).
--   2. Índice filtrado unique UQ_Rubro_ParentId_Nombre_Activo (unicidad CI por padre en activos).
--   3. Índice cubriente IX_Rubro_ParentId_Activo (child lookups ordenados).
--   4. Permiso 'catalogo:rubros:gestionar' + asignación a rol 'admin'.
--
-- Patrón: V011 (dbo.Medio con SYSTEM_VERSIONING + PAGE compression + MERGE permisos).
-- Idempotente: seguro para re-ejecutar.
-- Reversa: V016_ROLLBACK.sql.
-- Run on: SIGCM2 (dev) y SIGCM2_Test (integration tests).
--
-- Notas:
--   - TarifarioBaseId es INT NULL SIN FK — la FK se agrega en PRC-001.
--   - UQ_Rubro_ParentId_Nombre_Activo cubre solo ParentId IS NOT NULL;
--     para roots (ParentId IS NULL) la unicidad CI la garantiza Application
--     via ExistsByNombreUnderParentAsync(null, ...) — SQL Server trata NULLs
--     como distintos en índices únicos. Ver Design §9 Risk 1.
--   - FechaCreacion / FechaModificacion: DATETIME2(3) alineado con Medio/Seccion.
--   - ValidFrom / ValidTo: DATETIME2(3) GENERATED ALWAYS HIDDEN (idéntico a V011).
--
-- Source of truth: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.5 📋 Auditoría.md
-- SDD Design: engram sdd/cat-001-arbol-nario-rubros/design

SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
SET NOCOUNT ON;
GO

-- ═══════════════════════════════════════════════════════════════════════
-- 1. dbo.Rubro
-- ═══════════════════════════════════════════════════════════════════════

IF OBJECT_ID(N'dbo.Rubro', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.Rubro (
        Id                INT IDENTITY(1,1)  NOT NULL CONSTRAINT PK_Rubro PRIMARY KEY,
        ParentId          INT                NULL,
        Nombre            NVARCHAR(200)      NOT NULL COLLATE SQL_Latin1_General_CP1_CI_AI,
        Orden             INT                NOT NULL CONSTRAINT DF_Rubro_Orden            DEFAULT(0),
        Activo            BIT                NOT NULL CONSTRAINT DF_Rubro_Activo           DEFAULT(1),
        TarifarioBaseId   INT                NULL,               -- FK reservada para PRC-001 (sin constraint por ahora)
        FechaCreacion     DATETIME2(3)       NOT NULL CONSTRAINT DF_Rubro_FechaCreacion    DEFAULT(SYSUTCDATETIME()),
        FechaModificacion DATETIME2(3)       NULL,
        CONSTRAINT FK_Rubro_Parent FOREIGN KEY (ParentId) REFERENCES dbo.Rubro(Id) ON DELETE NO ACTION
    );
    PRINT 'Table dbo.Rubro created.';
END
ELSE
    PRINT 'Table dbo.Rubro already exists — skip.';
GO

-- ═══════════════════════════════════════════════════════════════════════
-- 2. SYSTEM_VERSIONING — Rubro
-- ═══════════════════════════════════════════════════════════════════════

IF COL_LENGTH('dbo.Rubro', 'ValidFrom') IS NULL
BEGIN
    ALTER TABLE dbo.Rubro
    ADD
        ValidFrom DATETIME2(3) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL
            CONSTRAINT DF_Rubro_ValidFrom DEFAULT(SYSUTCDATETIME()),
        ValidTo   DATETIME2(3) GENERATED ALWAYS AS ROW END   HIDDEN NOT NULL
            CONSTRAINT DF_Rubro_ValidTo   DEFAULT(CONVERT(DATETIME2(3), '9999-12-31 23:59:59.999')),
        PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);
    PRINT 'Rubro: PERIOD FOR SYSTEM_TIME added.';
END
GO

IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Rubro') AND temporal_type = 2)
BEGIN
    ALTER TABLE dbo.Rubro
    SET (SYSTEM_VERSIONING = ON (
        HISTORY_TABLE = dbo.Rubro_History,
        HISTORY_RETENTION_PERIOD = 10 YEARS
    ));
    PRINT 'Rubro: SYSTEM_VERSIONING = ON (history: dbo.Rubro_History, retention: 10 years).';
END
ELSE
    PRINT 'Rubro: SYSTEM_VERSIONING already ON — skip.';
GO

IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'Rubro_History' AND schema_id = SCHEMA_ID('dbo'))
   AND NOT EXISTS (
       SELECT 1 FROM sys.partitions p
       JOIN sys.tables t ON t.object_id = p.object_id
       WHERE t.name = 'Rubro_History' AND p.data_compression = 2
   )
BEGIN
    ALTER TABLE dbo.Rubro_History REBUILD WITH (DATA_COMPRESSION = PAGE);
    PRINT 'Rubro_History: rebuilt with PAGE compression.';
END
GO

-- ═══════════════════════════════════════════════════════════════════════
-- 3. Índices
-- ═══════════════════════════════════════════════════════════════════════

-- Unicidad CI por nombre bajo el mismo padre (solo filas activas + ParentId NOT NULL).
-- Para roots (ParentId IS NULL) la unicidad la garantiza Application layer.
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'UQ_Rubro_ParentId_Nombre_Activo' AND object_id = OBJECT_ID('dbo.Rubro'))
BEGIN
    CREATE UNIQUE INDEX UQ_Rubro_ParentId_Nombre_Activo
        ON dbo.Rubro(ParentId, Nombre)
        WHERE Activo = 1 AND ParentId IS NOT NULL;
    PRINT 'Index UQ_Rubro_ParentId_Nombre_Activo created.';
END
GO

-- Cubriente para child lookups ordenados por Orden.
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Rubro_ParentId_Activo' AND object_id = OBJECT_ID('dbo.Rubro'))
BEGIN
    CREATE INDEX IX_Rubro_ParentId_Activo
        ON dbo.Rubro(ParentId, Activo)
        INCLUDE (Nombre, Orden);
    PRINT 'Index IX_Rubro_ParentId_Activo created.';
END
GO

-- ═══════════════════════════════════════════════════════════════════════
-- 4. Permiso: catalogo:rubros:gestionar + asignación a rol 'admin'
-- ═══════════════════════════════════════════════════════════════════════

MERGE dbo.Permiso AS t
USING (VALUES
    ('catalogo:rubros:gestionar', N'Gestionar rubros del catálogo', N'Crear, editar, mover y desactivar rubros del árbol de catálogo comercial', 'catalogo')
) AS s (Codigo, Nombre, Descripcion, Modulo)
ON t.Codigo = s.Codigo
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Codigo, Nombre, Descripcion, Modulo)
    VALUES (s.Codigo, s.Nombre, s.Descripcion, s.Modulo);
GO

MERGE dbo.RolPermiso AS t
USING (
    SELECT r.Id AS RolId, p.Id AS PermisoId
    FROM (VALUES
        ('admin', 'catalogo:rubros:gestionar')
    ) AS x (RolCodigo, PermisoCodigo)
    JOIN dbo.Rol     r ON r.Codigo = x.RolCodigo
    JOIN dbo.Permiso p ON p.Codigo = x.PermisoCodigo
) AS s ON t.RolId = s.RolId AND t.PermisoId = s.PermisoId
WHEN NOT MATCHED BY TARGET THEN
    INSERT (RolId, PermisoId) VALUES (s.RolId, s.PermisoId);
GO

PRINT '';
PRINT 'V016 applied successfully — dbo.Rubro (temporal, retention 10y) + permiso catalogo:rubros:gestionar.';
PRINT 'Next: V017 (future — TBD by next UDT).';
GO
