-- =====================================================================
-- Migración: Trazabilidad de backups/restores de Velero (versión consolidada)
-- Fecha: 2026-08
-- Repo: nube_services_dao
--
-- Este es el script único a correr en un ambiente que todavía no tiene
-- nada de este feature (p. ej. producción). Reemplaza a la secuencia de
-- 3 archivos que se usó para llegar a este mismo estado en QA:
--   1. 2026-08_backups_tracking.sql            (creó las tablas, sembró
--                                                status_catalog/status_type)
--   2. 2026-08_backup_status_catalog_fix.sql    (desvío: tabla propia —
--                                                se decidió que no)
--   3. 2026-08_backup_status_catalog_revert.sql (volvió a status_catalog,
--                                                ensanchó status.status,
--                                                dropeó la tabla propia)
-- Esos 3 archivos se dejan como historial de QA, pero **no los corras en
-- prod** — este script ya incluye el resultado final de los tres, sin el
-- desvío.
--
-- Contexto:
--   client_bkps (Go) aprovisiona backups en Velero a través de
--   services_backups_daas, pero hoy no hay traza de lo que Velero
--   genera, no existe tabla de restores, y no se puede pausar/reanudar/
--   eliminar un schedule porque la BD no guarda la identidad de los
--   objetos de Velero (Schedule, BackupStorageLocation, bucket, secret,
--   k8scontext).
--
--   Esta migración:
--     1. Agrega identidad Velero + ciclo de vida a backup_schedule.
--     2. Espeja el CR Backup de Velero en backup_tracking.
--     3. Crea backup_restore (cola + registro de restores).
--     4. Crea backup_operations (cola + auditoría de pause/resume/delete,
--        equivalente a requests_queue pero para backups).
--     5. Ensancha status_catalog.status (32→64) y siembra fases de
--        backup/restore/schedule en status_type / status_catalog — el
--        mismo catálogo compartido que ya usan supply_status y
--        service_status. client_bkps mapea phase → status_id contra
--        esta tabla, reutilizando el caché de store/catalog.go (TTL 5h).
--
--   IMPORTANTE — antes de ejecutar esta migración:
--   revisa el contenido actual de `status_type` y `status_catalog`
--   (SELECT * FROM status_type; SELECT * FROM status_catalog;) y ajusta
--   la sección 5 si alguna de estas fases ya existe con otro nombre.
--   Los INSERT de la sección 5 son idempotentes (WHERE NOT EXISTS) pero
--   comparan por nombre exacto, así que un nombre distinto para el mismo
--   concepto produciría un duplicado.
--
--   Todo el bloque es re-ejecutable de forma segura (columnas con
--   verificación previa, tablas con CREATE TABLE IF NOT EXISTS, semillas
--   con WHERE NOT EXISTS).
-- =====================================================================

-- ---------------------------------------------------------------------
-- 1. backup_schedule — identidad Velero + ciclo de vida
-- ---------------------------------------------------------------------
ALTER TABLE `backup_schedule`
  ADD COLUMN `velero_schedule_name`  varchar(253) DEFAULT NULL AFTER `context_cron`,
  ADD COLUMN `storage_location_name` varchar(253) DEFAULT NULL,
  ADD COLUMN `bucket_name`           varchar(255) DEFAULT NULL,
  ADD COLUMN `secret_name`           varchar(253) DEFAULT NULL,
  ADD COLUMN `k8s_context`           varchar(100) DEFAULT NULL,
  ADD COLUMN `cluster_id`            int(11)      DEFAULT NULL,
  ADD COLUMN `velero_namespace`      varchar(63)  NOT NULL DEFAULT 'velero',
  ADD COLUMN `included_namespace`    varchar(63)  DEFAULT NULL,
  ADD COLUMN `status_id`             int(11)      DEFAULT NULL,
  ADD COLUMN `paused`                bit(1)       NOT NULL DEFAULT b'0',
  ADD COLUMN `paused_datetime`       datetime     DEFAULT NULL,
  ADD COLUMN `last_backup_datetime`  datetime     DEFAULT NULL,
  ADD COLUMN `error_log`             text         DEFAULT NULL,
  ADD COLUMN `synced_at`             datetime     DEFAULT NULL,
  ADD COLUMN `created_at`            datetime     NOT NULL DEFAULT current_timestamp(),
  ADD COLUMN `updated_at`            datetime     DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  ADD UNIQUE KEY `uk_backup_schedule_velero` (`velero_schedule_name`, `k8s_context`),
  ADD KEY `fk_backup_schedule_status_idx`  (`status_id`),
  ADD KEY `fk_backup_schedule_cluster_idx` (`cluster_id`),
  ADD CONSTRAINT `fk_backup_schedule_status`
      FOREIGN KEY (`status_id`)  REFERENCES `status_catalog` (`StatusCatalogID`) ON UPDATE CASCADE,
  ADD CONSTRAINT `fk_backup_schedule_cluster`
      FOREIGN KEY (`cluster_id`) REFERENCES `cluster` (`idcluster`) ON UPDATE CASCADE;

-- ---------------------------------------------------------------------
-- 2. backup_tracking — espejo del CR Backup de Velero
--
-- Decisión sobre `backup_size` (float, columna original): se DROPEA.
-- Razón: la tabla nunca se ha poblado en producción, por lo que no hay
-- filas ni consumidores que dependan de ella. Su semántica (unidad no
-- especificada) queda reemplazada por `backup_size_bytes` (bigint, bytes
-- explícitos, que es el dato que expone el CR Backup de Velero).
-- ---------------------------------------------------------------------
ALTER TABLE `backup_tracking`
  DROP COLUMN `backup_size`,
  ADD COLUMN `velero_backup_name`    varchar(253) NOT NULL AFTER `backup_schedule_id`,
  ADD COLUMN `backup_uid`            varchar(36)  DEFAULT NULL,
  ADD COLUMN `k8s_context`           varchar(100) DEFAULT NULL,
  ADD COLUMN `velero_namespace`      varchar(63)  NOT NULL DEFAULT 'velero',
  ADD COLUMN `storage_location_name` varchar(253) DEFAULT NULL,
  ADD COLUMN `phase`                 varchar(50)  DEFAULT NULL,
  ADD COLUMN `start_datetime`        datetime     DEFAULT NULL,
  ADD COLUMN `completion_datetime`   datetime     DEFAULT NULL,
  ADD COLUMN `expiration_datetime`   datetime     DEFAULT NULL,
  ADD COLUMN `ttl_spec`              varchar(20)  DEFAULT NULL,
  ADD COLUMN `total_items`           int(11)      DEFAULT NULL,
  ADD COLUMN `items_backed_up`       int(11)      DEFAULT NULL,
  ADD COLUMN `errors_count`          int(11)      NOT NULL DEFAULT 0,
  ADD COLUMN `warnings_count`        int(11)      NOT NULL DEFAULT 0,
  ADD COLUMN `error_log`             text         DEFAULT NULL,
  ADD COLUMN `backup_size_bytes`     bigint(20)   DEFAULT NULL,
  ADD COLUMN `synced_at`             datetime     DEFAULT NULL,
  ADD COLUMN `created_at`            datetime     NOT NULL DEFAULT current_timestamp(),
  ADD COLUMN `updated_at`            datetime     DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  ADD UNIQUE KEY `uk_backup_tracking_velero` (`velero_backup_name`, `k8s_context`);

-- Notas:
--   * backup_datetime (ya existía) = metadata.creationTimestamp del CR.
--   * backup_specs (ya existía, JSON) = spec crudo del CR.
--   * status_id (ya existía) referencia status_catalog — el FK original
--     Ffk_service_status_backup_tracking se mantiene sin tocar.
--   * El UNIQUE KEY (velero_backup_name, k8s_context) es lo que permite
--     que client_bkps sincronice vía Upsert con conflict_fields
--     compuestos de forma idempotente.

-- ---------------------------------------------------------------------
-- 3. backup_restore — cola + registro de restores
--
-- Dos columnas de nombre: services_backups_daas construye el nombre
-- real del restore como `restoreName + "-" + random(5)`, así que el
-- nombre definitivo solo se conoce en la respuesta gRPC.
-- restore_name_requested = lo que se pidió.
-- velero_restore_name    = lo que Velero terminó creando (se usa para
--                           volver a localizar el restore y consultar
--                           su estado).
--
-- supply sigue la misma semántica que en backup_schedule:
--   NULL = pendiente, 0 = reclamado/en proceso, 1 = aplicado.
-- attempts acompaña a supply para cortar reintentos de solicitudes que
-- fallan de forma permanente (mismo patrón que backup_operations).
--
-- Esta tabla es a la vez cola y registro del ciclo de vida completo del
-- restore (a diferencia de backup_operations, que solo modela acciones
-- de disparo único como pausar/reanudar/eliminar). Por eso
-- create_restore NO aparece en el enum de operation de
-- backup_operations: un restore corre varios minutos, pasa por fases,
-- tiene progreso y resultado — modelarlo también como "operación"
-- implicaría dos filas y dos colas para lo mismo.
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `backup_restore` (
  `BackupRestoreID`        int(11)      NOT NULL AUTO_INCREMENT,
  `backup_tracking_id`     int(11)      DEFAULT NULL,
  `backup_schedule_id`     int(11)      DEFAULT NULL,
  `contracted_services_id` int(11)      DEFAULT NULL,
  `requested_by_user_id`   int(11)      DEFAULT NULL,
  `velero_backup_name`     varchar(253) NOT NULL,
  `restore_name_requested` varchar(253) DEFAULT NULL,
  `velero_restore_name`    varchar(253) DEFAULT NULL,
  `restore_uid`            varchar(36)  DEFAULT NULL,
  `k8s_context`            varchar(100) DEFAULT NULL,
  `velero_namespace`       varchar(63)  NOT NULL DEFAULT 'velero',
  `target_namespace`       varchar(63)  DEFAULT NULL,
  `namespace_mapping`      longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
                             CHECK (json_valid(`namespace_mapping`)),
  `restore_specs`          longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
                             CHECK (json_valid(`restore_specs`)),
  `supply`                 bit(1)       DEFAULT NULL,
  `attempts`               int(11)      NOT NULL DEFAULT 0,
  `status_id`              int(11)      DEFAULT NULL,
  `phase`                  varchar(50)  DEFAULT NULL,
  `request_datetime`       datetime     NOT NULL DEFAULT current_timestamp(),
  `start_datetime`         datetime     DEFAULT NULL,
  `completion_datetime`    datetime     DEFAULT NULL,
  `total_items`            int(11)      DEFAULT NULL,
  `items_restored`         int(11)      DEFAULT NULL,
  `errors_count`           int(11)      NOT NULL DEFAULT 0,
  `warnings_count`         int(11)      NOT NULL DEFAULT 0,
  `error_log`              text         DEFAULT NULL,
  `synced_at`              datetime     DEFAULT NULL,
  `created_at`             datetime     NOT NULL DEFAULT current_timestamp(),
  `updated_at`             datetime     DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`BackupRestoreID`),
  UNIQUE KEY `uk_backup_restore_velero` (`velero_restore_name`,`k8s_context`),
  KEY `fk_backup_restore_tracking_idx`  (`backup_tracking_id`),
  KEY `fk_backup_restore_schedule_idx`  (`backup_schedule_id`),
  KEY `fk_backup_restore_contracted_idx`(`contracted_services_id`),
  KEY `fk_backup_restore_status_idx`    (`status_id`),
  CONSTRAINT `fk_backup_restore_tracking`
    FOREIGN KEY (`backup_tracking_id`)     REFERENCES `backup_tracking` (`BackupTrackingID`) ON UPDATE CASCADE,
  CONSTRAINT `fk_backup_restore_schedule`
    FOREIGN KEY (`backup_schedule_id`)     REFERENCES `backup_schedule` (`BackupScheduleID`) ON UPDATE CASCADE,
  CONSTRAINT `fk_backup_restore_contracted`
    FOREIGN KEY (`contracted_services_id`) REFERENCES `contracted_services` (`ContractedServicesID`) ON UPDATE CASCADE,
  CONSTRAINT `fk_backup_restore_status`
    FOREIGN KEY (`status_id`)              REFERENCES `status_catalog` (`StatusCatalogID`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

-- ---------------------------------------------------------------------
-- 4. backup_operations — cola + auditoría de pausar/reanudar/eliminar
--    (equivalente a requests_queue, pero para backups)
--
-- Matriz de operation × target_type válidas (verificado contra la API
-- de Velero v1.14.1, versión mínima desplegada). `operation` y
-- `target_type` se dejan como varchar (no ENUM de MySQL) para no
-- requerir DDL cada vez que se agregue una operación nueva:
--
--   target_type       | operation válidas                          | nota
--   ------------------+---------------------------------------------+------------------------------------------
--   schedule           | pause, resume, delete, create_backup, sync | pause/resume = patch a spec.paused;
--                       |                                            | create_backup dispara backup on-demand
--                       |                                            | desde la plantilla del schedule
--   backup             | delete                                     | vía DeleteBackupRequest: borra el CR
--                       |                                            | y los datos del bucket
--   restore            | delete                                     | solo borra el registro, NO deshace la
--                       |                                            | restauración (limpieza de historial)
--   storagelocation    | delete                                     | solo flujo de decommission
--   bucket              | delete                                     | solo flujo de decommission
--
-- Backups y restores NO se pueden pausar ni cancelar: los tipos Backup
-- y Restore de Velero no tienen campos Paused ni Cancel en ninguna
-- versión desplegada. Solo Schedule los tiene. No se modelan esas
-- operaciones aquí.
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `backup_operations` (
  `BackupOperationsID`   bigint(20)   NOT NULL AUTO_INCREMENT,
  `backup_schedule_id`   int(11)      DEFAULT NULL,
  `backup_restore_id`    int(11)      DEFAULT NULL,
  `operation`            varchar(20)  NOT NULL,   -- pause | resume | delete | create_backup | sync
  `target_type`          varchar(20)  NOT NULL,   -- schedule | backup | restore | storagelocation | bucket
  `target_name`          varchar(253) DEFAULT NULL,
  `k8s_context`          varchar(100) DEFAULT NULL,
  `payload`              longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
                           CHECK (json_valid(`payload`)),
  `supply`               bit(1)       DEFAULT NULL,  -- NULL pendiente / 0 en proceso / 1 aplicada
  `status_id`            int(11)      DEFAULT NULL,
  `attempts`             int(11)      NOT NULL DEFAULT 0,
  `error_log`            text         DEFAULT NULL,
  `requested_by_user_id` int(11)      DEFAULT NULL,
  `request_datetime`     datetime     NOT NULL DEFAULT current_timestamp(),
  `applied_datetime`     datetime     DEFAULT NULL,
  PRIMARY KEY (`BackupOperationsID`),
  KEY `fk_backup_operations_schedule_idx` (`backup_schedule_id`),
  KEY `fk_backup_operations_restore_idx`  (`backup_restore_id`),
  KEY `fk_backup_operations_status_idx`   (`status_id`),
  KEY `ix_backup_operations_pending`      (`supply`, `request_datetime`),
  CONSTRAINT `fk_backup_operations_schedule`
    FOREIGN KEY (`backup_schedule_id`) REFERENCES `backup_schedule` (`BackupScheduleID`) ON UPDATE CASCADE,
  CONSTRAINT `fk_backup_operations_restore`
    FOREIGN KEY (`backup_restore_id`)  REFERENCES `backup_restore` (`BackupRestoreID`)   ON UPDATE CASCADE,
  CONSTRAINT `fk_backup_operations_status`
    FOREIGN KEY (`status_id`)          REFERENCES `status_catalog` (`StatusCatalogID`)   ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

-- ---------------------------------------------------------------------
-- 5. Catálogo de estados — status_type / status_catalog
--
-- status_catalog.status es varchar(32) en el esquema original.
-- 'WaitingForPluginOperationsPartiallyFailed' (fase real de Velero) tiene
-- 42 caracteres y no cabe — hay que ensancharla ANTES de sembrar, si no
-- el INSERT truena con "Data too long for column 'status'". Ensanchar
-- un varchar es seguro: no trunca ni pierde datos existentes de
-- supply_status/service_status ni de ningún otro dominio.
--
-- Revisa PRIMERO qué hay ya en status_type/status_catalog y ajusta este
-- bloque para no duplicar. Las fases a cubrir:
--
--   Backup:   New, FailedValidation, InProgress,
--             WaitingForPluginOperations,
--             WaitingForPluginOperationsPartiallyFailed,
--             Finalizing, FinalizingPartiallyFailed,
--             Completed, PartiallyFailed, Failed, Deleting
--   Restore:  las mismas que Backup, menos Deleting
--   Schedule: Enabled, FailedValidation, Paused (nuestro), Deleted (nuestro)
--
-- Las tres tablas guardan además la fase cruda en su columna `phase`
-- (varchar) a propósito: si Velero agrega una fase nueva, el registro
-- no se pierde aunque todavía no exista en el catálogo.
-- ---------------------------------------------------------------------

ALTER TABLE `status_catalog` MODIFY COLUMN `status` VARCHAR(64) NOT NULL;

-- 5.1 status_type — un tipo por dominio (backup / restore / schedule)
INSERT INTO `status_type` (`name`, `active`)
SELECT 'backup', b'1'
WHERE NOT EXISTS (SELECT 1 FROM `status_type` WHERE `name` = 'backup');

INSERT INTO `status_type` (`name`, `active`)
SELECT 'restore', b'1'
WHERE NOT EXISTS (SELECT 1 FROM `status_type` WHERE `name` = 'restore');

INSERT INTO `status_type` (`name`, `active`)
SELECT 'backup_schedule', b'1'
WHERE NOT EXISTS (SELECT 1 FROM `status_type` WHERE `name` = 'backup_schedule');

-- 5.2 status_catalog — fases por dominio, enlazadas a su status_type

-- Backup
INSERT INTO `status_catalog` (`status`, `status_dsc`, `id_type`, `active`)
SELECT s.status, s.status, st.StatusTypeID, b'1'
FROM (
  SELECT 'New' AS status
  UNION ALL SELECT 'FailedValidation'
  UNION ALL SELECT 'InProgress'
  UNION ALL SELECT 'WaitingForPluginOperations'
  UNION ALL SELECT 'WaitingForPluginOperationsPartiallyFailed'
  UNION ALL SELECT 'Finalizing'
  UNION ALL SELECT 'FinalizingPartiallyFailed'
  UNION ALL SELECT 'Completed'
  UNION ALL SELECT 'PartiallyFailed'
  UNION ALL SELECT 'Failed'
  UNION ALL SELECT 'Deleting'
) s
JOIN `status_type` st ON st.name = 'backup'
WHERE NOT EXISTS (
  SELECT 1 FROM `status_catalog` sc WHERE sc.status = s.status AND sc.id_type = st.StatusTypeID
);

-- Restore (mismas fases que Backup, menos Deleting)
INSERT INTO `status_catalog` (`status`, `status_dsc`, `id_type`, `active`)
SELECT s.status, s.status, st.StatusTypeID, b'1'
FROM (
  SELECT 'New' AS status
  UNION ALL SELECT 'FailedValidation'
  UNION ALL SELECT 'InProgress'
  UNION ALL SELECT 'WaitingForPluginOperations'
  UNION ALL SELECT 'WaitingForPluginOperationsPartiallyFailed'
  UNION ALL SELECT 'Finalizing'
  UNION ALL SELECT 'FinalizingPartiallyFailed'
  UNION ALL SELECT 'Completed'
  UNION ALL SELECT 'PartiallyFailed'
  UNION ALL SELECT 'Failed'
) s
JOIN `status_type` st ON st.name = 'restore'
WHERE NOT EXISTS (
  SELECT 1 FROM `status_catalog` sc WHERE sc.status = s.status AND sc.id_type = st.StatusTypeID
);

-- Schedule (Enabled, FailedValidation de Velero + Paused, Deleted propios)
INSERT INTO `status_catalog` (`status`, `status_dsc`, `id_type`, `active`)
SELECT s.status, s.status, st.StatusTypeID, b'1'
FROM (
  SELECT 'Enabled' AS status
  UNION ALL SELECT 'FailedValidation'
  UNION ALL SELECT 'Paused'
  UNION ALL SELECT 'Deleted'
) s
JOIN `status_type` st ON st.name = 'backup_schedule'
WHERE NOT EXISTS (
  SELECT 1 FROM `status_catalog` sc WHERE sc.status = s.status AND sc.id_type = st.StatusTypeID
);

-- =====================================================================
-- ROLLBACK (ejecutar en orden inverso; comentado — descomentar a mano)
-- =====================================================================

-- -- 5. Semillas (borra solo lo sembrado por esta migración; ajustar si
-- --    hubo colisión de nombres con datos preexistentes). No revierte el
-- --    ensanchado de status_catalog.status — es seguro dejarlo en 64.
-- DELETE sc FROM `status_catalog` sc
--   JOIN `status_type` st ON st.StatusTypeID = sc.id_type
--   WHERE st.name IN ('backup', 'restore', 'backup_schedule');
-- DELETE FROM `status_type` WHERE `name` IN ('backup', 'restore', 'backup_schedule');

-- -- 4. backup_operations
-- DROP TABLE IF EXISTS `backup_operations`;

-- -- 3. backup_restore
-- DROP TABLE IF EXISTS `backup_restore`;

-- -- 2. backup_tracking — revertir a esquema original
-- ALTER TABLE `backup_tracking`
--   DROP KEY `uk_backup_tracking_velero`,
--   DROP COLUMN `velero_backup_name`,
--   DROP COLUMN `backup_uid`,
--   DROP COLUMN `k8s_context`,
--   DROP COLUMN `velero_namespace`,
--   DROP COLUMN `storage_location_name`,
--   DROP COLUMN `phase`,
--   DROP COLUMN `start_datetime`,
--   DROP COLUMN `completion_datetime`,
--   DROP COLUMN `expiration_datetime`,
--   DROP COLUMN `ttl_spec`,
--   DROP COLUMN `total_items`,
--   DROP COLUMN `items_backed_up`,
--   DROP COLUMN `errors_count`,
--   DROP COLUMN `warnings_count`,
--   DROP COLUMN `error_log`,
--   DROP COLUMN `backup_size_bytes`,
--   DROP COLUMN `synced_at`,
--   DROP COLUMN `created_at`,
--   DROP COLUMN `updated_at`,
--   ADD COLUMN `backup_size` float DEFAULT NULL;

-- -- 1. backup_schedule — revertir a esquema original
-- ALTER TABLE `backup_schedule`
--   DROP FOREIGN KEY `fk_backup_schedule_status`,
--   DROP FOREIGN KEY `fk_backup_schedule_cluster`,
--   DROP KEY `uk_backup_schedule_velero`,
--   DROP KEY `fk_backup_schedule_status_idx`,
--   DROP KEY `fk_backup_schedule_cluster_idx`,
--   DROP COLUMN `velero_schedule_name`,
--   DROP COLUMN `storage_location_name`,
--   DROP COLUMN `bucket_name`,
--   DROP COLUMN `secret_name`,
--   DROP COLUMN `k8s_context`,
--   DROP COLUMN `cluster_id`,
--   DROP COLUMN `velero_namespace`,
--   DROP COLUMN `included_namespace`,
--   DROP COLUMN `status_id`,
--   DROP COLUMN `paused`,
--   DROP COLUMN `paused_datetime`,
--   DROP COLUMN `last_backup_datetime`,
--   DROP COLUMN `error_log`,
--   DROP COLUMN `synced_at`,
--   DROP COLUMN `created_at`,
--   DROP COLUMN `updated_at`;
