<?php
// api/movimiento_save.php — Guarda/edita movimientos y actualiza STOCK con validación
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }

session_start();
require_once __DIR__ . '/../conex.php';
mysqli_set_charset($conn,'utf8mb4');

function fail(string $msg, int $code=400){
  http_response_code($code);
  echo json_encode(['success'=>false,'message'=>$msg]); exit;
}

/** Bloquea y obtiene (o crea en 0) el registro de stock */
function lock_stock_row(mysqli $c, int $id_bod, int $id_acc, ?int $id_ubi): array {
  $ubi = $id_ubi ?? null;
  // Intentar seleccionar con FOR UPDATE
  $sql = "SELECT id_stock, stock FROM tb_inv_stock
          WHERE id_bodega=? AND id_accesorio=? AND IFNULL(id_ubicacion,0)=IFNULL(?,0)
          FOR UPDATE";
  $st = mysqli_prepare($c,$sql);
  mysqli_stmt_bind_param($st,'iii',$id_bod,$id_acc,$ubi);
  mysqli_stmt_execute($st);
  $res = mysqli_stmt_get_result($st);
  $row = mysqli_fetch_assoc($res);
  if ($row) return $row;

  // No existe -> crear en 0 y volver a bloquear
  $ins = mysqli_prepare($c,"INSERT INTO tb_inv_stock (id_bodega,id_accesorio,id_ubicacion,stock) VALUES (?,?,?,0)
                            ON DUPLICATE KEY UPDATE stock=stock");
  mysqli_stmt_bind_param($ins,'iii',$id_bod,$id_acc,$ubi);
  if (!mysqli_stmt_execute($ins)) throw new Exception('No se pudo inicializar stock: '.mysqli_error($c));

  // Releer con FOR UPDATE
  mysqli_stmt_execute($st);
  $res = mysqli_stmt_get_result($st);
  $row = mysqli_fetch_assoc($res);
  if (!$row) throw new Exception('No se pudo bloquear stock');
  return $row;
}

/** Aplica delta (+/-) al stock, validando no-negativo */
function apply_stock_delta(mysqli $c, int $id_bod, int $id_acc, ?int $id_ubi, float $delta, string $ctx=''){
  $row = lock_stock_row($c,$id_bod,$id_acc,$id_ubi);
  $actual = (float)$row['stock'];
  $nuevo  = $actual + $delta;
  if ($nuevo < -1e-9) { // tolerancia flotante
    $ubiTxt = is_null($id_ubi) ? 'SIN ubicación' : "Ubicación $id_ubi";
    throw new Exception("Stock insuficiente ($ctx). Bodega $id_bod, Accesorio $id_acc, $ubiTxt. Disponible: $actual, Requerido: ".(-$delta));
  }
  $upd = mysqli_prepare($c,"UPDATE tb_inv_stock SET stock=? WHERE id_stock=?");
  mysqli_stmt_bind_param($upd,'di',$nuevo,$row['id_stock']);
  if (!mysqli_stmt_execute($upd)) throw new Exception('No se pudo actualizar stock: '.mysqli_error($c));
}

/** Aplica los renglones del movimiento al stock según tipo */
function aplicar_movimiento(mysqli $c, string $tipo, int $id_origen, int $id_destino, array $det, string $ctx=''){
  foreach($det as $i=>$r){
    $acc = (int)($r['id_accesorio'] ?? 0);
    $can = (float)($r['cantidad'] ?? 0);
    $ubi = isset($r['id_ubicacion']) && $r['id_ubicacion'] !== '' ? (int)$r['id_ubicacion'] : null;
    if ($acc<=0 || $can<=0) throw new Exception("Renglón ".($i+1).": accesorio/cantidad inválidos");
    if ($tipo==='IN'){
      // Entrada: suma en destino
      apply_stock_delta($c,$id_destino,$acc,$ubi, +$can, "ENTRADA");
    } elseif ($tipo==='OUT'){
      // Salida: resta en origen
      apply_stock_delta($c,$id_origen,$acc,$ubi, -$can, "SALIDA");
    } else { // TRF
      // Transfiere: resta en origen, suma en destino
      apply_stock_delta($c,$id_origen, $acc,$ubi, -$can, "TRANSFERENCIA (origen)");
      apply_stock_delta($c,$id_destino,$acc,$ubi, +$can, "TRANSFERENCIA (destino)");
    }
  }
}

/** Revierte un movimiento previo del stock (operación inversa) */
function revertir_movimiento(mysqli $c, array $cab, array $det){
  $tipo = $cab['tipo'];
  $id_o = (int)$cab['id_bodega_origen'];
  $id_d = (int)$cab['id_bodega_destino'];

  foreach($det as $r){
    $acc = (int)$r['id_accesorio'];
    $can = (float)$r['cantidad'];
    $ubi = $r['id_ubicacion'] !== null ? (int)$r['id_ubicacion'] : null;
    if ($tipo==='IN'){
      // Reversa de entrada: RESTAR en destino
      apply_stock_delta($c,$id_d,$acc,$ubi, -$can, "REVERSA ENTRADA");
    } elseif ($tipo==='OUT'){
      // Reversa de salida: SUMAR en origen
      apply_stock_delta($c,$id_o,$acc,$ubi, +$can, "REVERSA SALIDA");
    } else { // TRF
      // Reversa de transferencia: SUMAR en origen, RESTAR en destino
      apply_stock_delta($c,$id_o,$acc,$ubi, +$can, "REVERSA TRANSFER (origen)");
      apply_stock_delta($c,$id_d,$acc,$ubi, -$can, "REVERSA TRANSFER (destino)");
    }
  }
}

$in = json_decode(file_get_contents('php://input'), true);

$id_mov      = (int)($in['id_movimiento'] ?? 0);
$fecha       = trim((string)($in['fecha'] ?? ''));           // opcional (si vacío: NOW())
$tipo        = trim((string)($in['tipo'] ?? ''));            // IN|OUT|TRF
$id_origen   = (int)($in['id_bodega_origen'] ?? 0);
$id_destino  = (int)($in['id_bodega_destino'] ?? 0);
$motivo      = trim((string)($in['motivo'] ?? ''));
$ref_tipo    = trim((string)($in['ref_tipo'] ?? ''));
$ref_id      = trim((string)($in['ref_id'] ?? ''));
$id_usuario  = (int)($_SESSION['id_usuario'] ?? 0);          // o tomar del payload si te conviene

$det = $in['det'] ?? [];

if (!in_array($tipo, ['IN','OUT','TRF'], true)) fail('Tipo inválido');
if (!is_array($det) || count($det)===0) fail('Debe ingresar al menos un renglón');
if ($tipo==='IN'  && $id_destino<=0) fail('Destino requerido para ENTRADA');
if ($tipo==='OUT' && $id_origen<=0)  fail('Origen requerido para SALIDA');
if ($tipo==='TRF' && ($id_origen<=0 || $id_destino<=0)) fail('Origen y Destino requeridos para TRANSFERENCIA');
if ($tipo==='TRF' && $id_origen===$id_destino) fail('Origen y Destino no pueden ser la misma bodega');

try{
  mysqli_begin_transaction($conn);

  if ($id_mov>0){
    // ===== EDITAR: revertir stock anterior, actualizar cabecera, regrabar detalle y aplicar nuevo
    // 1) Leer cabecera y detalles actuales (y bloquear cabecera)
    $st = mysqli_prepare($conn,"SELECT id_movimiento, fecha, tipo, id_bodega_origen, id_bodega_destino, motivo, ref_tipo, ref_id, id_usuario
                                FROM tb_inv_movimiento WHERE id_movimiento=? FOR UPDATE");
    mysqli_stmt_bind_param($st,'i',$id_mov);
    mysqli_stmt_execute($st);
    $res = mysqli_stmt_get_result($st);
    $cabOld = mysqli_fetch_assoc($res);
    if (!$cabOld) throw new Exception('Movimiento no encontrado');

    $detOld = [];
    $qr = mysqli_query($conn,"SELECT id_movimiento_det, id_accesorio, cantidad, id_ubicacion
                              FROM tb_inv_movimiento_det
                              WHERE id_movimiento={$id_mov} ORDER BY id_movimiento_det");
    while($x=mysqli_fetch_assoc($qr)) $detOld[]=$x;

    // 2) Revertir stock previo (valida saldos también)
    revertir_movimiento($conn, $cabOld, $detOld);

    // 3) Actualizar cabecera
    $sqlUp = "UPDATE tb_inv_movimiento
              SET fecha = IF(?='', fecha, ?),
                  tipo=?, id_bodega_origen=?, id_bodega_destino=?,
                  motivo=?, ref_tipo=?, ref_id=?, id_usuario=?
              WHERE id_movimiento=?";
    $up = mysqli_prepare($conn,$sqlUp);
    mysqli_stmt_bind_param($up,'ssssisssii',
      $fecha,$fecha,$tipo,$id_origen,$id_destino,$motivo,$ref_tipo,$ref_id,$id_usuario,$id_mov
    );
    if (!mysqli_stmt_execute($up)) throw new Exception('No se pudo actualizar cabecera: '.mysqli_error($conn));

    // 4) Reemplazar detalle
    $del = mysqli_prepare($conn,"DELETE FROM tb_inv_movimiento_det WHERE id_movimiento=?");
    mysqli_stmt_bind_param($del,'i',$id_mov);
    if (!mysqli_stmt_execute($del)) throw new Exception('No se pudo limpiar detalle: '.mysqli_error($conn));

    $ins = mysqli_prepare($conn,"INSERT INTO tb_inv_movimiento_det (id_movimiento, id_accesorio, cantidad, id_ubicacion) VALUES (?,?,?,?)");
    foreach($det as $r){
      $acc = (int)($r['id_accesorio'] ?? 0);
      $can = (float)($r['cantidad'] ?? 0);
      $ubi = isset($r['id_ubicacion']) && $r['id_ubicacion']!=='' ? (int)$r['id_ubicacion'] : null;
      if ($acc<=0 || $can<=0) throw new Exception('Detalle inválido');
      mysqli_stmt_bind_param($ins,'iidi',$id_mov,$acc,$can,$ubi);
      if (!mysqli_stmt_execute($ins)) throw new Exception('No se pudo insertar detalle: '.mysqli_error($conn));
    }

    // 5) Aplicar nuevo movimiento al stock
    aplicar_movimiento($conn,$tipo,$id_origen,$id_destino,$det,'APLICACIÓN NUEVA');

  } else {
    // ===== NUEVO: crear cabecera, detalle y aplicar stock
    $sql = "INSERT INTO tb_inv_movimiento (fecha, tipo, id_bodega_origen, id_bodega_destino, motivo, ref_tipo, ref_id, id_usuario)
            VALUES (IF(?='', NOW(), ?), ?, ?, ?, ?, ?, ?, ?)";
    $st = mysqli_prepare($conn,$sql);
    mysqli_stmt_bind_param($st,'ssssisssi',
      $fecha,$fecha,$tipo,$id_origen,$id_destino,$motivo,$ref_tipo,$ref_id,$id_usuario
    );
    if (!mysqli_stmt_execute($st)) throw new Exception('No se pudo crear cabecera: '.mysqli_error($conn));
    $id_mov = (int)mysqli_insert_id($conn);

    $ins = mysqli_prepare($conn,"INSERT INTO tb_inv_movimiento_det (id_movimiento, id_accesorio, cantidad, id_ubicacion) VALUES (?,?,?,?)");
    foreach($det as $r){
      $acc = (int)($r['id_accesorio'] ?? 0);
      $can = (float)($r['cantidad'] ?? 0);
      $ubi = isset($r['id_ubicacion']) && $r['id_ubicacion']!=='' ? (int)$r['id_ubicacion'] : null;
      if ($acc<=0 || $can<=0) throw new Exception('Detalle inválido');
      mysqli_stmt_bind_param($ins,'iidi',$id_mov,$acc,$can,$ubi);
      if (!mysqli_stmt_execute($ins)) throw new Exception('No se pudo insertar detalle: '.mysqli_error($conn));
    }

    aplicar_movimiento($conn,$tipo,$id_origen,$id_destino,$det,'NUEVO');
  }

  mysqli_commit($conn);
  echo json_encode(['success'=>true,'message'=>'Guardado','id'=>$id_mov]);

} catch(Throwable $e){
  mysqli_rollback($conn);
  http_response_code(400);
  echo json_encode(['success'=>false,'message'=>$e->getMessage()]);
}
