-- =============================================================================
--  inv_solicitud — BLOQUEO por veredicto del privado (memoria del último "no")
-- =============================================================================
--  Base de datos : asturias_api  (servidor PÚBLICO, CL032BACK)
--  Archivo       : database/sql/inv_solicitud_add_bloqueo.sql
--  Fecha         : 2026-08-20
--  Equivale a    : database/migrations/2026_08_20_100000_add_bloqueo_to_inv_solicitud.php
--  Propuesta     : docs/replicacion/12-PROPUESTA-VINCULO-SOLICITUD-INVITADOS.md §3.5
--  Contraparte   : CL034RESERVACIONESV2/docs/PROPUESTA_VINCULO_SOLICITUD_REPLICA_PORTAL.md
--
--  MOTIVO
--  ------
--  Cuando Reservaciones V2 rechaza una réplica por causa de la SOLICITUD DE INVITADOS —o todavía
--  no ha llegado allá, o ya tiene una reservación viva encima— el portal cancela la
--  Temporal del socio, libera el cupo y RECUERDA el motivo aquí.
--
--  Ese recuerdo es lo único que hace que el siguiente intento se corte ANTES de que el
--  socio vuelva a elegir habitación. Sin él, el primer clic se repite en cada reintento
--  y siempre acaba igual: seis segundos después, con la reservación cancelada otra vez.
--
--    solicitudBloqueoMotivo  slug del último veredicto. NULL = sin bloqueo, que es el
--                            estado normal de toda solicitud. Los cinco valores posibles
--                            los emite Reservaciones V2 (ReplicarReservacionEnLinea::MOTIVO_*):
--                              sincronizando        la solicitud aún no llegó al privado;
--                                                   se disparó la sincronizacion con Tlalpan
--                              no_existe_en_origen  ya se sincronizo y sigue sin existir
--                              sin_transferir       ni siquiera salio del portal a Tlalpan
--                              ocupada_firme        ya tiene reservacion asentada, con folio
--                              ocupada_en_curso     la estan capturando en el club ahora
--    solicitudBloqueoFecha   cuándo se registró. Diagnóstico, y base del contador que ve
--                            el socio. La escribe el PORTAL con su propio reloj.
--    solicitudBloqueoVence   cuándo deja de aplicar.
--
--  LA COLUMNA QUE IMPORTA ES LA TERCERA, Y POR ESO SON TRES Y NO DOS
--  -----------------------------------------------------------------
--  Con el MOTIVO PRESENTE, `solicitudBloqueoVence = NULL` significa **el bloqueo NO
--  caduca**. Es el caso `ocupada_firme`: la solicitud ya tiene una reservación asentada
--  con folio y esperar no va a cambiarlo, así que dejar que el socio reintente cada dos
--  minutos para recibir el mismo 'no' es hostigarlo.
--
--  Sin esta columna haría falta una bandera aparte para ese caso, y habría que acordarse
--  de consultar LAS DOS en cada punto de entrada — que es exactamente como se cuelan los
--  bloqueos que no bloquean.
--
--  El bloqueo permanente se levanta por el camino correcto, no a mano: si esa reservación
--  se cancela en el privado, la réplica inversa la trae y `Solicitud::liberarBloqueo()`
--  limpia las tres columnas de una vez.
--
--  POR QUÉ LOS NOMBRES SON GENÉRICOS
--  ---------------------------------
--  `Bloqueo` y no `Sincronizacion`: las mismas tres columnas sirven a los DOS veredictos
--  (SIN_SOLICITUD y SOLICITUD_OCUPADA). Bautizarlas por uno de ellos habría obligado a
--  añadir otras tres para el otro, y a que cada lectura preguntara por las seis.
--
--  POR QUÉ NO HAY ÍNDICE
--  ---------------------
--  Siempre se leen POR `idSolicitud`, que es la PK. No existe ninguna consulta del tipo
--  "dame las solicitudes bloqueadas": el bloqueo se mira cuando el socio intenta usar UNA
--  solicitud concreta. Un índice aquí solo costaría escrituras.
--
--  NINGUNA FILA EXISTENTE CAMBIA DE SIGNIFICADO
--  --------------------------------------------
--  Las tres nacen NULL, y `solicitudBloqueoMotivo = NULL` es precisamente "sin bloqueo".
--  Tras aplicar, todas las solicitudes del padrón siguen pudiendo reservarse igual que
--  antes. No hace falta backfill.
--
--  APLICAR MANUALMENTE
--  -------------------
--    mysql -u <usuario> -p asturias_api < inv_solicitud_add_bloqueo.sql
--
--  RE-EJECUTABLE: cada columna se crea solo si falta. Correrlo dos veces seguidas no
--  produce error ni cambia datos. Son ALTER de solo-adición sobre una tabla InnoDB
--  pequeña; no reescriben datos y no requieren ventana de mantenimiento.
--
--  ORDEN DE DESPLIEGUE: este script puede aplicarse ANTES de subir el código. Las
--  columnas vacías no alteran ningún comportamiento; el código nuevo es el que empieza a
--  escribirlas. Al revés NO: el código sin las columnas revienta al primer veredicto.
-- =============================================================================

-- -----------------------------------------------------------------------------
--  1. inv_solicitud.solicitudBloqueoMotivo
-- -----------------------------------------------------------------------------
SET @existe := (
    SELECT COUNT(*) FROM information_schema.COLUMNS
     WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_solicitud'
       AND COLUMN_NAME = 'solicitudBloqueoMotivo'
);
SET @sql := IF(@existe = 0,
    "ALTER TABLE `inv_solicitud`
       ADD COLUMN `solicitudBloqueoMotivo` varchar(40) NULL DEFAULT NULL
           COMMENT 'Slug del ultimo veredicto de Reservaciones V2 que impide reservar esta solicitud. NULL = sin bloqueo.'
           AFTER `solicitudHashReplica`",
    "SELECT 'inv_solicitud.solicitudBloqueoMotivo ya existe' AS aviso"
);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;

-- -----------------------------------------------------------------------------
--  2. inv_solicitud.solicitudBloqueoFecha
-- -----------------------------------------------------------------------------
SET @existe := (
    SELECT COUNT(*) FROM information_schema.COLUMNS
     WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_solicitud'
       AND COLUMN_NAME = 'solicitudBloqueoFecha'
);
SET @sql := IF(@existe = 0,
    "ALTER TABLE `inv_solicitud`
       ADD COLUMN `solicitudBloqueoFecha` datetime NULL DEFAULT NULL
           COMMENT 'Cuando se registro el bloqueo. La escribe el Portal con su propio reloj.'
           AFTER `solicitudBloqueoMotivo`",
    "SELECT 'inv_solicitud.solicitudBloqueoFecha ya existe' AS aviso"
);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;

-- -----------------------------------------------------------------------------
--  3. inv_solicitud.solicitudBloqueoVence
-- -----------------------------------------------------------------------------
--  NULL CON MOTIVO PRESENTE = NO CADUCA. No es "sin fecha aún": es el bloqueo
--  permanente de `ocupada_firme`. Quien lea esta columna sin mirar el motivo va a
--  interpretarla al revés.
-- -----------------------------------------------------------------------------
SET @existe := (
    SELECT COUNT(*) FROM information_schema.COLUMNS
     WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_solicitud'
       AND COLUMN_NAME = 'solicitudBloqueoVence'
);
SET @sql := IF(@existe = 0,
    "ALTER TABLE `inv_solicitud`
       ADD COLUMN `solicitudBloqueoVence` datetime NULL DEFAULT NULL
           COMMENT 'Cuando deja de aplicar el bloqueo. NULL CON MOTIVO PRESENTE = no caduca (ocupada_firme).'
           AFTER `solicitudBloqueoFecha`",
    "SELECT 'inv_solicitud.solicitudBloqueoVence ya existe' AS aviso"
);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;

-- -----------------------------------------------------------------------------
--  4. Registrar la migración equivalente como YA APLICADA
-- -----------------------------------------------------------------------------
--  ESTE BLOQUE NO ES OPCIONAL SI SE APLICÓ EL SQL A MANO.
--
--  `php artisan migrate` corre en orden y se DETIENE al primer error. Si la migración
--  2026_08_20_100000 queda como pendiente después de haber aplicado estas columnas,
--  el siguiente `migrate` aborta con "Duplicate column name 'solicitudBloqueoMotivo'"
--  — y todo lo que venga detrás en la cola nunca llega a aplicarse.
--
--  Es exactamente el problema que documenta database/sql/registrar_migraciones_aplicadas.sql,
--  y que ya se pagó una vez en este proyecto.
--
--  NO TOCA EL ESQUEMA: solo escribe la fila de control. Idempotente por el NOT EXISTS.
--
--  Si en cambio se aplicó con `php artisan migrate`, este bloque no hace nada: la fila
--  ya está y el INSERT se salta solo.
-- -----------------------------------------------------------------------------
SET @batch := (SELECT IFNULL(MAX(`batch`), 0) + 1 FROM `migrations`);

INSERT INTO `migrations` (`migration`, `batch`)
SELECT '2026_08_20_100000_add_bloqueo_to_inv_solicitud', @batch FROM DUAL
 WHERE NOT EXISTS (
     SELECT 1 FROM `migrations`
      WHERE `migration` = '2026_08_20_100000_add_bloqueo_to_inv_solicitud'
 );

-- -----------------------------------------------------------------------------
--  VERIFICACIÓN (opcional, tras aplicar)
-- -----------------------------------------------------------------------------
-- Las tres columnas existen y son NULL-ables:
-- SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT
--   FROM information_schema.COLUMNS
--  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_solicitud'
--    AND COLUMN_NAME LIKE 'solicitudBloqueo%'
--  ORDER BY ORDINAL_POSITION;
--
-- Ninguna solicitud nace bloqueada (esperado: 0):
-- SELECT COUNT(*) AS bloqueadas FROM `inv_solicitud` WHERE `solicitudBloqueoMotivo` IS NOT NULL;
--
-- La migración quedó registrada (esperado: 1):
-- SELECT COUNT(*) AS registrada FROM `migrations`
--  WHERE `migration` = '2026_08_20_100000_add_bloqueo_to_inv_solicitud';

-- -----------------------------------------------------------------------------
--  DIAGNÓSTICO EN OPERACIÓN (para soporte)
-- -----------------------------------------------------------------------------
--  Qué solicitudes están bloqueadas ahora mismo y por qué. `permanente` = Si es el
--  caso de `ocupada_firme`, y no se destraba esperando: hay que revisar la reservación
--  que ocupa esa solicitud en el privado.
--
-- SELECT s.`idSolicitud`,
--        s.`solicitudNo`,
--        s.`idMembresia`,
--        s.`idSolicitudTlalpan`,
--        s.`solicitudBloqueoMotivo`                              AS motivo,
--        s.`solicitudBloqueoFecha`                               AS desde,
--        IF(s.`solicitudBloqueoVence` IS NULL, 'Si', 'No')       AS permanente,
--        s.`solicitudBloqueoVence`                               AS vence
--   FROM `inv_solicitud` s
--  WHERE s.`solicitudBloqueoMotivo` IS NOT NULL
--    AND (s.`solicitudBloqueoVence` IS NULL OR s.`solicitudBloqueoVence` > NOW())
--  ORDER BY s.`solicitudBloqueoFecha` DESC;
--
--  DESTRABAR UNA A MANO — último recurso, y casi nunca la respuesta correcta.
--  El bloqueo es solo un RECUERDO del veredicto: la autoridad la tiene el maestro y
--  viaja en cada réplica. Limpiarlo no arregla nada de fondo — si la causa sigue ahí,
--  el socio recibirá el mismo veredicto seis segundos después de su primera habitación.
--  Sirve únicamente cuando ya se resolvió el problema en el privado y el canal inverso
--  está apagado, que es el único caso en que la marca no se levanta sola.
--
-- UPDATE `inv_solicitud`
--    SET `solicitudBloqueoMotivo` = NULL,
--        `solicitudBloqueoFecha`  = NULL,
--        `solicitudBloqueoVence`  = NULL
--  WHERE `idSolicitud` = <id>;

-- -----------------------------------------------------------------------------
--  ROLLBACK
-- -----------------------------------------------------------------------------
--  Solo con el código nuevo YA RETIRADO: sin estas columnas, el primer veredicto
--  SIN_SOLICITUD / SOLICITUD_OCUPADA revienta al intentar escribirlas.
--
-- ALTER TABLE `inv_solicitud`
--   DROP COLUMN `solicitudBloqueoVence`,
--   DROP COLUMN `solicitudBloqueoFecha`,
--   DROP COLUMN `solicitudBloqueoMotivo`;
--
-- DELETE FROM `migrations`
--  WHERE `migration` = '2026_08_20_100000_add_bloqueo_to_inv_solicitud';
