-- ============================================================
--  Migración 03 · Stock: movimiento_stock como única fuente de verdad
--  Fecha: 2026-08-28
-- ------------------------------------------------------------
--  A partir de aquí `existencia_bodega.stock_actual` la mantiene
--  EXCLUSIVAMENTE un trigger sobre `movimiento_stock`. El código
--  PHP deja de tocar `existencia_bodega` directamente para stock
--  (ver cambios en InventarioController y Producto).
--
--  Orden interno IMPORTANTE:
--    1) ampliar enum
--    2) crear la función de signo
--    3) cargar saldos de apertura  <-- ANTES de crear los triggers
--    4) crear los triggers
-- ============================================================
USE tinsa_db;

-- ── 1) Nuevo tipo de movimiento para el saldo de apertura ────
ALTER TABLE movimiento_stock
  MODIFY tipo ENUM('entrada','salida','traslado','ajuste','carga_inicial') NOT NULL;

-- ── 2) Función de signo (reutilizada por triggers y reconciliación) ──
DROP FUNCTION IF EXISTS fn_stock_delta;
DELIMITER $$
CREATE FUNCTION fn_stock_delta(
    p_tipo     VARCHAR(20),
    p_tipo_ref VARCHAR(50),
    p_cant     DECIMAL(12,4)
) RETURNS DECIMAL(12,4)
DETERMINISTIC
NO SQL
BEGIN
    RETURN CASE
        WHEN p_tipo IN ('entrada','carga_inicial')              THEN  p_cant
        WHEN p_tipo = 'salida'                                  THEN -p_cant
        WHEN p_tipo = 'ajuste'   AND p_tipo_ref = 'aumento'     THEN  p_cant
        WHEN p_tipo = 'ajuste'   AND p_tipo_ref = 'disminucion' THEN -p_cant
        WHEN p_tipo = 'traslado' AND p_tipo_ref = 'destino'     THEN  p_cant
        WHEN p_tipo = 'traslado' AND p_tipo_ref = 'origen'      THEN -p_cant
        ELSE 0
    END;
END$$
DELIMITER ;

-- ── 3) Saldos de apertura ──────────────────────────────────
--     1 fila `carga_inicial` por existencia. La cantidad es el SALDO RESIDUAL:
--     stock_actual menos el neto de los movimientos que ya existían, para que
--     el Kardex completo cuadre exactamente con existencia_bodega.
--     Los triggers todavía NO existen -> no hay doble conteo.
--     Fecha sentinela antigua para que ordene primero en el Kardex.
INSERT INTO movimiento_stock
    (id_producto, id_bodega, tipo, cantidad, tipo_referencia, fecha, id_usuario)
SELECT eb.id_producto, eb.id_bodega, 'carga_inicial',
       eb.stock_actual - COALESCE((
           SELECT SUM(fn_stock_delta(m.tipo, m.tipo_referencia, m.cantidad))
           FROM movimiento_stock m
           WHERE m.id_bodega = eb.id_bodega AND m.id_producto = eb.id_producto
       ), 0) AS saldo_apertura,
       'migracion', '2000-01-01 00:00:00', NULL
FROM existencia_bodega eb
WHERE eb.stock_actual - COALESCE((
           SELECT SUM(fn_stock_delta(m.tipo, m.tipo_referencia, m.cantidad))
           FROM movimiento_stock m
           WHERE m.id_bodega = eb.id_bodega AND m.id_producto = eb.id_producto
       ), 0) > 0;

-- ── 4) Triggers ─────────────────────────────────────────────
DELIMITER $$

-- 4a. Aplica cada movimiento al saldo
CREATE TRIGGER trg_mov_stock_ai
AFTER INSERT ON movimiento_stock
FOR EACH ROW
BEGIN
    DECLARE v_delta    DECIMAL(12,4);
    DECLARE v_exist_id INT UNSIGNED DEFAULT NULL;
    DECLARE v_actual   DECIMAL(12,4) DEFAULT 0;
    DECLARE v_ubic     INT UNSIGNED DEFAULT NULL;

    SET v_delta = fn_stock_delta(NEW.tipo, NEW.tipo_referencia, NEW.cantidad);

    SELECT id, stock_actual INTO v_exist_id, v_actual
    FROM existencia_bodega
    WHERE id_bodega = NEW.id_bodega AND id_producto = NEW.id_producto
    ORDER BY id LIMIT 1;

    IF v_exist_id IS NULL THEN
        IF v_delta < 0 THEN
            SIGNAL SQLSTATE '45000'
              SET MESSAGE_TEXT = 'Stock insuficiente: no hay existencia previa para este producto/bodega';
        END IF;
        SELECT id INTO v_ubic FROM ubicacion_bodega
        WHERE id_bodega = NEW.id_bodega AND es_predeterminada = 1
        ORDER BY id LIMIT 1;
        INSERT INTO existencia_bodega
            (id_bodega, id_producto, id_ubicacion_bodega, stock_actual, stock_minimo)
        VALUES (NEW.id_bodega, NEW.id_producto, v_ubic, v_delta, 0);
    ELSE
        IF v_actual + v_delta < 0 THEN
            SIGNAL SQLSTATE '45000'
              SET MESSAGE_TEXT = 'Stock insuficiente: el movimiento dejaría existencia_bodega en negativo';
        END IF;
        UPDATE existencia_bodega
        SET stock_actual = stock_actual + v_delta
        WHERE id = v_exist_id;
    END IF;
END$$

-- 4b. Ledger inmutable: solo se permite enlazar id_referencia
CREATE TRIGGER trg_mov_stock_bu
BEFORE UPDATE ON movimiento_stock
FOR EACH ROW
BEGIN
    IF NEW.id_producto <> OLD.id_producto
    OR NEW.id_bodega   <> OLD.id_bodega
    OR NEW.tipo        <> OLD.tipo
    OR NEW.cantidad    <> OLD.cantidad
    OR NOT (NEW.tipo_referencia <=> OLD.tipo_referencia) THEN
        SIGNAL SQLSTATE '45000'
          SET MESSAGE_TEXT = 'movimiento_stock es inmutable: use un asiento inverso para corregir';
    END IF;
END$$

-- 4c. Borrar un movimiento revierte su efecto (limpieza de datos de prueba)
CREATE TRIGGER trg_mov_stock_ad
AFTER DELETE ON movimiento_stock
FOR EACH ROW
BEGIN
    UPDATE existencia_bodega
    SET stock_actual = stock_actual - fn_stock_delta(OLD.tipo, OLD.tipo_referencia, OLD.cantidad)
    WHERE id_bodega = OLD.id_bodega AND id_producto = OLD.id_producto
    ORDER BY id LIMIT 1;
END$$

DELIMITER ;
