-- ============================================================
-- Migracion de base de datos - ApiDenuncias
-- Envelope Encryption adaptado al esquema real de la aplicacion
-- Entorno previsto: PRE / MySQL Flexible Server
-- BD prevista: gestiona
-- ============================================================
--
-- OBJETIVO
--   Incorporar el modelo de claves diarias propuesto por infraestructura
--
-- MODELO REAL DE LA APP
--   complaints
--     Guarda la denuncia, metadatos tecnicos, estado, expediente Gestiona
--     y payload sensible cifrado en columnas existentes como:
--       - raw_report_text
--       - form_fields_json
--       - campos textuales derivados del report
--
--   complaint_attachments
--     Guarda adjuntos y report, con hash en claro para deduplicacion:
--       - content
--       - description
--       - notes
--       - content_sha256
--
--   inbox_reports / user_inbox_reports / app_users
--     Trazabilidad de bandeja, usuario propietario y control de descargas.
--     No almacenan el contenido sensible de la denuncia.
--
-- ESTRATEGIA
--   1. Crear encryption_keys, gestionada por Function App / infraestructura.
--   2. Anadir key_date a complaints y complaint_attachments.
--   3. Mantener key_date nullable para no romper datos legacy o entornos
--      con registros previos. La API debera rellenarlo en nuevas escrituras.
--   4. No se crean columnas descripcion_cifrada/iv/auth_tag separadas porque
--      la API cifra el payload/contenido en el propio campo con AES-256-GCM,
--      empaquetando nonce + auth_tag + ciphertext en el valor persistido.
--
-- IMPORTANTE
--   Ejecutar dentro de la base de datos correcta:
--     USE gestiona;
-- ============================================================

USE gestiona;

-- ------------------------------------------------------------
-- TABLA: encryption_keys
-- Gestionada por la Function App de claves.
-- La API la lee para obtener la eDEK diaria y hacer unwrapKey en Key Vault.
-- ------------------------------------------------------------

CREATE TABLE IF NOT EXISTS encryption_keys (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    key_date    DATE         NOT NULL COMMENT 'Fecha de validez de la KEK/DEK diaria. Una fila por dia.',
    key_name    VARCHAR(100) NOT NULL COMMENT 'Nombre de la clave en Azure Key Vault. Ej: key-2026-05-19.',
    key_version VARCHAR(500) NOT NULL COMMENT 'URI completa con version de la KEK en Key Vault. Se usa para unwrapKey.',
    edek        TEXT         NOT NULL COMMENT 'DEK cifrada en Base64. La API hace unwrapKey y mantiene la DEK solo en memoria.',
    status      ENUM('active','purged') NOT NULL DEFAULT 'active' COMMENT 'active: operativa | purged: KEK deshabilitada y datos ilegibles.',
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp de creacion de la clave.',
    purged_at   DATETIME     NULL COMMENT 'Timestamp de purga criptografica. NULL mientras status = active.',
    CONSTRAINT uq_encryption_keys_date UNIQUE (key_date),
    INDEX ix_encryption_keys_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ------------------------------------------------------------
-- Helpers idempotentes para MySQL.
-- Evitamos ADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS porque
-- no son seguros en todas las versiones/configuraciones de MySQL usadas.
-- ------------------------------------------------------------

DROP PROCEDURE IF EXISTS add_column_if_missing;
DROP PROCEDURE IF EXISTS add_index_if_missing;
DROP PROCEDURE IF EXISTS add_fk_if_missing;

DELIMITER //

CREATE PROCEDURE add_column_if_missing(
    IN p_table_name VARCHAR(64),
    IN p_column_name VARCHAR(64),
    IN p_column_definition TEXT
)
BEGIN
    IF NOT EXISTS (
        SELECT 1
        FROM information_schema.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = p_table_name
          AND COLUMN_NAME = p_column_name
    ) THEN
        SET @sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN ', p_column_definition);
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END//

CREATE PROCEDURE add_index_if_missing(
    IN p_table_name VARCHAR(64),
    IN p_index_name VARCHAR(64),
    IN p_index_definition TEXT
)
BEGIN
    IF NOT EXISTS (
        SELECT 1
        FROM information_schema.STATISTICS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = p_table_name
          AND INDEX_NAME = p_index_name
    ) THEN
        SET @sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD ', p_index_definition);
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END//

CREATE PROCEDURE add_fk_if_missing(
    IN p_table_name VARCHAR(64),
    IN p_constraint_name VARCHAR(64),
    IN p_constraint_definition TEXT
)
BEGIN
    IF NOT EXISTS (
        SELECT 1
        FROM information_schema.TABLE_CONSTRAINTS
        WHERE CONSTRAINT_SCHEMA = DATABASE()
          AND TABLE_NAME = p_table_name
          AND CONSTRAINT_NAME = p_constraint_name
          AND CONSTRAINT_TYPE = 'FOREIGN KEY'
    ) THEN
        SET @sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD CONSTRAINT `', p_constraint_name, '` ', p_constraint_definition);
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END//

DELIMITER ;

-- ------------------------------------------------------------
-- MODIFICACION: complaints
-- key_date indica con que DEK diaria se cifro la denuncia/payload sensible.
-- Queda nullable para permitir registros anteriores a la migracion.
-- ------------------------------------------------------------

CALL add_column_if_missing(
    'complaints',
    'key_date',
    '`key_date` DATE NULL COMMENT ''FK a encryption_keys.key_date. Indica la clave diaria usada para cifrar los datos sensibles de la denuncia.'' AFTER `is_rejected`'
);

CALL add_column_if_missing(
    'complaints',
    'encryption_scheme',
    '`encryption_scheme` VARCHAR(64) NOT NULL DEFAULT ''none'' COMMENT ''Formato de cifrado aplicado. Ej: aes-256-gcm-envelope-v1.'' AFTER `key_date`'
);

CALL add_column_if_missing(
    'complaints',
    'encrypted_at_utc',
    '`encrypted_at_utc` DATETIME(6) NULL COMMENT ''Fecha UTC en la que la API cifro o recifro el payload sensible.'' AFTER `encryption_scheme`'
);

CALL add_index_if_missing(
    'complaints',
    'ix_complaints_key_date',
    'INDEX `ix_complaints_key_date` (`key_date`)'
);

CALL add_fk_if_missing(
    'complaints',
    'fk_complaints_key_date',
    'FOREIGN KEY (`key_date`) REFERENCES `encryption_keys` (`key_date`)'
);

-- ------------------------------------------------------------
-- MODIFICACION: complaint_attachments
-- key_date indica con que DEK diaria se cifro cada adjunto/report.
-- El hash content_sha256 permanece en claro para deduplicar actualizaciones.
-- ------------------------------------------------------------

CALL add_column_if_missing(
    'complaint_attachments',
    'key_date',
    '`key_date` DATE NULL COMMENT ''FK a encryption_keys.key_date. Indica la clave diaria usada para cifrar el contenido del adjunto.'' AFTER `content_sha256`'
);

CALL add_column_if_missing(
    'complaint_attachments',
    'encryption_scheme',
    '`encryption_scheme` VARCHAR(64) NOT NULL DEFAULT ''none'' COMMENT ''Formato de cifrado aplicado. Ej: aes-256-gcm-envelope-v1.'' AFTER `key_date`'
);

CALL add_column_if_missing(
    'complaint_attachments',
    'encrypted_at_utc',
    '`encrypted_at_utc` DATETIME(6) NULL COMMENT ''Fecha UTC en la que la API cifro o recifro el adjunto.'' AFTER `encryption_scheme`'
);

CALL add_index_if_missing(
    'complaint_attachments',
    'ix_attachments_key_date',
    'INDEX `ix_attachments_key_date` (`key_date`)'
);

CALL add_fk_if_missing(
    'complaint_attachments',
    'fk_attachments_key_date',
    'FOREIGN KEY (`key_date`) REFERENCES `encryption_keys` (`key_date`)'
);

-- ------------------------------------------------------------
-- Limpieza de helpers.
-- ------------------------------------------------------------

DROP PROCEDURE IF EXISTS add_column_if_missing;
DROP PROCEDURE IF EXISTS add_index_if_missing;
DROP PROCEDURE IF EXISTS add_fk_if_missing;

-- ============================================================
-- CONSULTAS DE VERIFICACION
-- ============================================================

SELECT
    TABLE_NAME,
    ENGINE,
    TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME IN ('encryption_keys', 'complaints', 'complaint_attachments')
ORDER BY TABLE_NAME;

SELECT
    COLUMN_NAME,
    DATA_TYPE,
    IS_NULLABLE,
    COLUMN_DEFAULT,
    COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = 'encryption_keys'
ORDER BY ORDINAL_POSITION;

SELECT
    TABLE_NAME,
    COLUMN_NAME,
    DATA_TYPE,
    IS_NULLABLE,
    COLUMN_DEFAULT,
    COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME IN ('complaints', 'complaint_attachments')
  AND COLUMN_NAME IN ('key_date', 'encryption_scheme', 'encrypted_at_utc')
ORDER BY TABLE_NAME, ORDINAL_POSITION;

SELECT
    CONSTRAINT_NAME,
    TABLE_NAME,
    COLUMN_NAME,
    REFERENCED_TABLE_NAME,
    REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE()
  AND REFERENCED_TABLE_NAME = 'encryption_keys'
ORDER BY TABLE_NAME, CONSTRAINT_NAME;

SELECT
    TABLE_NAME,
    INDEX_NAME,
    COLUMN_NAME,
    NON_UNIQUE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME IN ('encryption_keys', 'complaints', 'complaint_attachments')
  AND INDEX_NAME IN ('ix_encryption_keys_status', 'ix_complaints_key_date', 'ix_attachments_key_date')
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX;

-- ============================================================
-- FIN
-- ============================================================
