-- V018__create_product.sql
-- PRD-002: Product — entidad vendible concreta del catálogo comercial.
--
-- Cambios:
--   1. dbo.Product (FK Medio/ProductType/Rubro, SYSTEM_VERSIONING ON, retention 10 años).
--   2. Índices: filtered UQ por (MedioId, ProductTypeId, Nombre) activos; cover por ProductTypeId
--      (para IProductQueryRepository); cover por MedioId; cover filtrado por RubroId.
--   3. Permiso 'catalogo:productos:gestionar' + asignación a rol 'admin'.
--
-- Patrón: V017 (dbo.ProductType con SYSTEM_VERSIONING + PAGE compression + MERGE permisos).
-- Idempotente: seguro para re-ejecutar.
-- Reversa: V018_ROLLBACK.sql.
-- Run on: SIGCM2 (dev) y SIGCM2_Test (integration tests).
--
-- Notas:
--   - SIN seed de datos — PRD-008 (V019) seedea los 12 productos legacy.
--   - Validación de flags (RequiresCategory, HasDuration) vive en Application layer:
--     un ProductType puede cambiar flags; la Product queda en estado snapshot.
--   - UQ filtered WHERE IsActive=1: permite reusar nombres tras soft-delete.
--
-- SDD Design: engram sdd/prd-002-product-crud/design

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

-- ═══════════════════════════════════════════════════════════════════════
-- 1. dbo.Product
-- ═══════════════════════════════════════════════════════════════════════

IF OBJECT_ID(N'dbo.Product', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.Product (
        Id                INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Product PRIMARY KEY,
        Nombre            NVARCHAR(300)     COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL,
        MedioId           INT               NOT NULL,
        ProductTypeId     INT               NOT NULL,
        RubroId           INT               NULL,
        BasePrice         DECIMAL(18,4)     NOT NULL,
        PriceDurationDays INT               NULL,
        IsActive          BIT               NOT NULL CONSTRAINT DF_Product_IsActive         DEFAULT(1),
        FechaCreacion     DATETIME2(3)      NOT NULL CONSTRAINT DF_Product_FechaCreacion    DEFAULT(SYSUTCDATETIME()),
        FechaModificacion DATETIME2(3)      NULL,
        CONSTRAINT FK_Product_Medio       FOREIGN KEY (MedioId)       REFERENCES dbo.Medio(Id)       ON DELETE NO ACTION,
        CONSTRAINT FK_Product_ProductType FOREIGN KEY (ProductTypeId) REFERENCES dbo.ProductType(Id) ON DELETE NO ACTION,
        CONSTRAINT FK_Product_Rubro       FOREIGN KEY (RubroId)       REFERENCES dbo.Rubro(Id)       ON DELETE NO ACTION,
        CONSTRAINT CK_Product_BasePrice_NonNegative CHECK (BasePrice >= 0),
        CONSTRAINT CK_Product_PriceDurationDays_Positive CHECK (PriceDurationDays IS NULL OR PriceDurationDays >= 1)
    );
    PRINT 'Table dbo.Product created.';
END
ELSE
    PRINT 'Table dbo.Product already exists — skip.';
GO

-- ═══════════════════════════════════════════════════════════════════════
-- 2. SYSTEM_VERSIONING — Product
-- ═══════════════════════════════════════════════════════════════════════

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

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

IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'Product_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 = 'Product_History' AND p.data_compression = 2
   )
BEGIN
    ALTER TABLE dbo.Product_History REBUILD WITH (DATA_COMPRESSION = PAGE);
    PRINT 'Product_History: rebuilt with PAGE compression.';
END
GO

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

-- Filtered UQ: unicidad activa por (Medio, Tipo, Nombre). Permite reusar nombres tras soft-delete.
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'UQ_Product_MedioId_ProductTypeId_Nombre_Active' AND object_id = OBJECT_ID('dbo.Product'))
BEGIN
    CREATE UNIQUE INDEX UQ_Product_MedioId_ProductTypeId_Nombre_Active
        ON dbo.Product (MedioId, ProductTypeId, Nombre)
        WHERE IsActive = 1;
    PRINT 'Index UQ_Product_MedioId_ProductTypeId_Nombre_Active created.';
END
GO

-- Cover para IProductQueryRepository.ExistsActiveByProductTypeAsync
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_ProductTypeId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
BEGIN
    CREATE INDEX IX_Product_ProductTypeId_IsActive
        ON dbo.Product (ProductTypeId, IsActive);
    PRINT 'Index IX_Product_ProductTypeId_IsActive created.';
END
GO

-- Cover para list filtered by MedioId
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_MedioId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
BEGIN
    CREATE INDEX IX_Product_MedioId_IsActive
        ON dbo.Product (MedioId, IsActive);
    PRINT 'Index IX_Product_MedioId_IsActive created.';
END
GO

-- Cover para list filtered by RubroId
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_Product_RubroId_IsActive' AND object_id = OBJECT_ID('dbo.Product'))
BEGIN
    CREATE INDEX IX_Product_RubroId_IsActive
        ON dbo.Product (RubroId, IsActive)
        WHERE RubroId IS NOT NULL;
    PRINT 'Index IX_Product_RubroId_IsActive created.';
END
GO

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

MERGE dbo.Permiso AS t
USING (VALUES
    ('catalogo:productos:gestionar',
     N'Gestionar productos del catálogo',
     N'Crear, editar y desactivar productos del 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:productos: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 'V018 applied — dbo.Product (temporal, retention 10y) + permiso catalogo:productos:gestionar.';
PRINT 'Next: V019 (PRD-008 — seed 12 productos legacy).';
GO
