<?php
/**
 * transferencias.php
 * ============================================================
 * BReport · TRANSFERENCIAS ENTRE PLANTAS / LOCALIDADES
 * ------------------------------------------------------------
 * Reglas operativas MES multiplanta:
 * - Las transferencias se realizan solo entre localidades de la misma empresa.
 * - El inventario se calcula desde inventory_moves por company_id + location_id.
 * - La regla especial CF1/CF2/CF3/CUB/SOB-CUB aplica únicamente al flujo de Hielo Barra.
 * - Botella, Hielo Bolsa, Garrafón y plantas futuras usan sus almacenes configurados.
 * - Disponible = existencia real en almacenes transferibles de la localidad activa.
 * ------------------------------------------------------------
 * Incluye:
 * - Inventario total / tránsito / por recibir / disponible
 * - Crear transferencia
 * - Ver detalle
 * - Cancelar
 * - Recibir
 * - Indicadores de existencia origen / destino en modal
 * ============================================================
 */

declare(strict_types=1);

require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/includes/helpers.php';
require_once __DIR__ . '/includes/csrf.php';

require_scope();
ensure_session();

$pdo = pdo();
$u   = current_user();
$sc  = scope();

$companyId  = (int)$sc['company_id'];
$locationId = (int)$sc['location_id'];

$canWrite = has_role('admin') || has_role('warehouse') || has_role('corp') || has_role('plant_admin');
$action   = (string)($_GET['action'] ?? '');

/* ============================================================
   HELPERS
============================================================ */
function jexit(array $data, int $code = 200): void {
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  exit;
}

function read_json(): array {
  $raw = file_get_contents('php://input');
  $d = json_decode($raw ?: '[]', true);
  return is_array($d) ? $d : [];
}

function need_csrf(): void {
  if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET') return;
  $hdr = (string)($_SERVER['HTTP_X_CSRF'] ?? '');
  if ($hdr === '') jexit(['ok'=>false,'error'=>'Falta X-CSRF'], 419);
  csrf_verify($hdr);
}

function col_exists(PDO $pdo, string $table, string $col): bool {
  $st = $pdo->prepare("
    SELECT COUNT(*)
    FROM information_schema.columns
    WHERE table_schema = DATABASE()
      AND table_name = ?
      AND column_name = ?
  ");
  $st->execute([$table, $col]);
  return (int)$st->fetchColumn() > 0;
}

function ensure_transfer_cols(PDO $pdo): void {
  $alters = [];
  if (!col_exists($pdo,'transfers','driver_name')) $alters[] = "ADD COLUMN driver_name VARCHAR(80) NULL";
  if (!col_exists($pdo,'transfers','truck_no'))    $alters[] = "ADD COLUMN truck_no VARCHAR(40) NULL";
  if (!col_exists($pdo,'transfers','depart_at'))   $alters[] = "ADD COLUMN depart_at DATETIME NULL";
  if (!col_exists($pdo,'transfers','arrive_at'))   $alters[] = "ADD COLUMN arrive_at DATETIME NULL";
  if ($alters) {
    $pdo->exec("ALTER TABLE transfers " . implode(", ", $alters));
  }
}

function fmt_dt_for_db(string $v): ?string {
  $v = trim($v);
  if ($v === '') return null;
  $v = str_replace('T', ' ', $v);
  if (strlen($v) === 16) $v .= ':00';
  return $v;
}


function valid_mail_date(string $date): string {
  $date = trim($date);
  if ($date === '') return '';
  $dt = DateTimeImmutable::createFromFormat('Y-m-d', $date);
  return ($dt && $dt->format('Y-m-d') === $date) ? $date : '';
}

/**
 * Vista tipo bandeja de transferencias.
 * Se mantiene separada de la lógica de inventario/movimientos:
 * únicamente consulta documentos del alcance activo y permite filtrarlos por día.
 */
function mailbox_filter_scope(string $folder, int $companyId, int $locationId, string $date = ''): array {
  $folder = strtolower(trim($folder));
  $where = [];
  $params = [];

  switch ($folder) {
    case 'received':
      $where[] = "t.from_company_id = ?";
      $where[] = "t.to_company_id = ?";
      $where[] = "t.to_location_id = ?";
      $where[] = "t.status = 'RECEIVED'";
      $params = [$companyId, $companyId, $locationId];
      $dateExpression = "DATE(COALESCE(t.arrive_at, t.received_at, t.created_at))";
      break;

    case 'sent':
      $where[] = "t.from_company_id = ?";
      $where[] = "t.to_company_id = ?";
      $where[] = "t.from_location_id = ?";
      $where[] = "t.status IN ('DRAFT','SENT','RECEIVED','CANCELLED')";
      $params = [$companyId, $companyId, $locationId];
      $dateExpression = "DATE(COALESCE(t.depart_at, t.created_at))";
      break;

    case 'pending':
    default:
      $folder = 'pending';
      $where[] = "t.from_company_id = ?";
      $where[] = "t.to_company_id = ?";
      $where[] = "t.to_location_id = ?";
      $where[] = "t.status = 'SENT'";
      $params = [$companyId, $companyId, $locationId];
      $dateExpression = "DATE(COALESCE(t.depart_at, t.created_at))";
      break;
  }

  $date = valid_mail_date($date);
  if ($date !== '') {
    $where[] = "{$dateExpression} = ?";
    $params[] = $date;
  }

  return [
    'folder' => $folder,
    'sql' => implode(' AND ', $where),
    'params' => $params,
  ];
}

function mailbox_count(PDO $pdo, string $folder, int $companyId, int $locationId, string $date = ''): int {
  $scope = mailbox_filter_scope($folder, $companyId, $locationId, $date);
  $st = $pdo->prepare("SELECT COUNT(*) FROM transfers t WHERE " . $scope['sql']);
  $st->execute($scope['params']);
  return (int)$st->fetchColumn();
}

function is_bar_sku(string $sku): bool {
  return mb_strtoupper(trim($sku)) === 'BARRA 150 KILOS';
}

function get_bar_product_id(PDO $pdo, int $companyId): int {
  $st = $pdo->prepare("
    SELECT id
    FROM products
    WHERE company_id = ?
      AND sku = 'BARRA 150 KILOS'
    LIMIT 1
  ");
  $st->execute([$companyId]);
  return (int)($st->fetchColumn() ?: 0);
}

function get_location_warehouse_map(PDO $pdo, int $companyId, int $locationId): array {
  $st = $pdo->prepare("
    SELECT id, code
    FROM warehouses
    WHERE company_id = ?
      AND location_id = ?
      AND active = 1
  ");
  $st->execute([$companyId, $locationId]);

  $map = [];
  foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
    $map[(int)$r['id']] = (string)$r['code'];
  }
  return $map;
}


function get_warehouse_id_by_code(PDO $pdo, int $companyId, int $locationId, string $code): ?int {
  $st = $pdo->prepare("\n    SELECT id\n    FROM warehouses\n    WHERE company_id = ?\n      AND location_id = ?\n      AND UPPER(code) = UPPER(?)\n      AND active = 1\n    ORDER BY id\n    LIMIT 1\n  ");
  $st->execute([$companyId, $locationId, $code]);
  $id = (int)($st->fetchColumn() ?: 0);
  return $id > 0 ? $id : null;
}

function get_product_sku(PDO $pdo, int $companyId, int $productId): string {
  $st = $pdo->prepare("\n    SELECT sku\n    FROM products\n    WHERE company_id = ?\n      AND id = ?\n    LIMIT 1\n  ");
  $st->execute([$companyId, $productId]);
  return (string)($st->fetchColumn() ?: '');
}

function get_location_company_id(PDO $pdo, int $locationId): int {
  $st = $pdo->prepare("\n    SELECT company_id\n    FROM locations\n    WHERE id = ?\n      AND active = 1\n    LIMIT 1\n  ");
  $st->execute([$locationId]);
  return (int)($st->fetchColumn() ?: 0);
}

function find_product_id_by_sku(PDO $pdo, int $companyId, string $sku): int {
  $sku = trim($sku);
  if ($sku === '') return 0;
  $st = $pdo->prepare("\n    SELECT id\n    FROM products\n    WHERE company_id = ?\n      AND UPPER(sku) = UPPER(?)\n      AND active = 1\n    ORDER BY id\n    LIMIT 1\n  ");
  $st->execute([$companyId, $sku]);
  return (int)($st->fetchColumn() ?: 0);
}

function get_product_warehouse_balance(PDO $pdo, int $companyId, int $locationId, int $productId, int $warehouseId): float {
  $st = $pdo->prepare("\n    SELECT COALESCE(SUM(\n      CASE\n        WHEN move_type IN ('IN','ADJ') THEN qty\n        WHEN move_type = 'OUT' THEN -qty\n        ELSE 0\n      END\n    ),0) AS qty\n    FROM inventory_moves\n    WHERE company_id = ?\n      AND location_id = ?\n      AND product_id = ?\n      AND warehouse_id = ?\n  ");
  $st->execute([$companyId, $locationId, $productId, $warehouseId]);
  return (float)$st->fetchColumn();
}

function transfer_table_exists(PDO $pdo, string $table): bool {
  $st = $pdo->prepare("
    SELECT COUNT(*)
    FROM information_schema.tables
    WHERE table_schema = DATABASE()
      AND table_name = ?
  ");
  $st->execute([$table]);
  return (int)$st->fetchColumn() > 0;
}

function transfer_is_internal_warehouse(string $code): bool {
  return in_array(strtoupper(trim($code)), ['MERMA','APROV-MERMA','CUB','SOB-CUB','SIN-WH'], true);
}

function transfer_normalize_scope_text(string $txt): string {
  $txt = strtr($txt, [
    'Á'=>'A','É'=>'E','Í'=>'I','Ó'=>'O','Ú'=>'U','Ü'=>'U','Ñ'=>'N',
    'á'=>'A','é'=>'E','í'=>'I','ó'=>'O','ú'=>'U','ü'=>'U','ñ'=>'N',
  ]);
  return mb_strtoupper(trim($txt), 'UTF-8');
}

/**
 * Detecta la regla especial únicamente para el alcance/planta de Hielo Barra.
 *
 * No basta con que existan almacenes con nombres similares en otra planta.
 * El alcance seleccionado debe identificar Hielo Barra y la localidad debe
 * tener habilitada su estructura operativa de CF/CUB.
 */
function transfer_scope_is_hielo_barra(PDO $pdo, int $companyId, int $locationId): bool {
  $st = $pdo->prepare("
    SELECT c.name AS company_name, l.name AS location_name, COALESCE(l.code,'') AS location_code
    FROM locations l
    JOIN companies c ON c.id = l.company_id
    WHERE l.company_id=?
      AND l.id=?
      AND l.active=1
    LIMIT 1
  ");
  $st->execute([$companyId, $locationId]);
  $scope = $st->fetch(PDO::FETCH_ASSOC);
  if (!$scope) return false;

  $scopeText = transfer_normalize_scope_text(
    (string)$scope['company_name'] . ' ' .
    (string)$scope['location_name'] . ' ' .
    (string)$scope['location_code']
  );

  if (!str_contains($scopeText, 'HIELO') || !str_contains($scopeText, 'BARRA')) {
    return false;
  }

  $st = $pdo->prepare("
    SELECT COUNT(DISTINCT UPPER(code))
    FROM warehouses
    WHERE company_id=?
      AND location_id=?
      AND active=1
      AND UPPER(code) IN ('CF1','CF2','CF3','CUB')
  ");
  $st->execute([$companyId, $locationId]);

  return (int)$st->fetchColumn() >= 4;
}

/**
 * Almacenes transferibles alineados con Almacén:
 * - La existencia siempre sale de inventory_moves.
 * - Solo el alcance/planta Hielo Barra conserva su regla: Barra = CF1/CF2; producto terminado = CF3.
 * - Botella, Hielo Bolsa, Garrafón y demás plantas usan product_warehouse_rules si existe configuración.
 * - Sin regla, usan almacenes activos de SU localidad excluyendo almacenes internos/de disposición.
 */
function transfer_allowed_warehouses(PDO $pdo, int $companyId, int $locationId, int $productId, string $purpose = 'source'): array {
  $purpose = strtolower(trim($purpose)) === 'destination' ? 'destination' : 'source';
  $sku = get_product_sku($pdo, $companyId, $productId);
  $rows = [];

  if (transfer_scope_is_hielo_barra($pdo, $companyId, $locationId)) {
    $codes = is_bar_sku($sku) ? ['CF1', 'CF2'] : ['CF3'];
    foreach ($codes as $code) {
      $wid = get_warehouse_id_by_code($pdo, $companyId, $locationId, $code);
      if ($wid) {
        $rows[] = ['warehouse_id'=>(int)$wid, 'code'=>$code, 'name'=>$code];
      }
    }
    return $rows;
  }

  if (transfer_table_exists($pdo, 'product_warehouse_rules')) {
    try {
      $st = $pdo->prepare("
        SELECT default_finished_warehouse_id, default_sales_warehouse_id
        FROM product_warehouse_rules
        WHERE company_id=?
          AND location_id=?
          AND product_id=?
          AND active=1
        LIMIT 1
      ");
      $st->execute([$companyId, $locationId, $productId]);
      $rule = $st->fetch(PDO::FETCH_ASSOC);
      if ($rule) {
        $ids = $purpose === 'source'
          ? [(int)($rule['default_sales_warehouse_id'] ?? 0), (int)($rule['default_finished_warehouse_id'] ?? 0)]
          : [(int)($rule['default_finished_warehouse_id'] ?? 0), (int)($rule['default_sales_warehouse_id'] ?? 0)];

        $ids = array_values(array_unique(array_filter($ids)));
        if ($ids) {
          $in = implode(',', array_fill(0, count($ids), '?'));
          $params = array_merge([$companyId, $locationId], $ids);
          $st = $pdo->prepare("
            SELECT id AS warehouse_id, code, name
            FROM warehouses
            WHERE company_id=?
              AND location_id=?
              AND active=1
              AND id IN ($in)
          ");
          $st->execute($params);
          $byId = [];
          foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $w) {
            if (!transfer_is_internal_warehouse((string)$w['code'])) {
              $byId[(int)$w['warehouse_id']] = $w;
            }
          }
          foreach ($ids as $id) {
            if (isset($byId[$id])) $rows[] = $byId[$id];
          }
          if ($rows) return $rows;
        }
      }
    } catch (Throwable $e) {
      // Si el esquema aún no tiene reglas completas, se continúa con almacenes activos.
    }
  }

  $st = $pdo->prepare("
    SELECT id AS warehouse_id, code, name
    FROM warehouses
    WHERE company_id=?
      AND location_id=?
      AND active=1
    ORDER BY code, name
  ");
  $st->execute([$companyId, $locationId]);
  foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $w) {
    if (transfer_is_internal_warehouse((string)$w['code'])) continue;
    $rows[] = $w;
  }
  return $rows;
}

function default_transfer_warehouse_id(PDO $pdo, int $companyId, int $locationId, int $productId, string $sku = ''): ?int {
  $allowed = transfer_allowed_warehouses($pdo, $companyId, $locationId, $productId, 'destination');
  return $allowed ? (int)$allowed[0]['warehouse_id'] : null;
}

function resolve_transfer_out_segments(PDO $pdo, int $companyId, int $locationId, int $productId, float $qty): array {
  $qty = round($qty, 3);
  if ($qty <= 0) return [];

  $sku = get_product_sku($pdo, $companyId, $productId);
  $allowed = transfer_allowed_warehouses($pdo, $companyId, $locationId, $productId, 'source');
  if (!$allowed) {
    throw new RuntimeException('No existe almacén transferible configurado para ' . ($sku !== '' ? $sku : ('producto #' . $productId)) . ' en esta localidad.');
  }

  $remaining = $qty;
  $segments = [];
  $labels = [];

  foreach ($allowed as $wh) {
    $wid = (int)$wh['warehouse_id'];
    $code = (string)$wh['code'];
    $labels[] = $code;

    $balance = get_product_warehouse_balance($pdo, $companyId, $locationId, $productId, $wid);
    if ($balance <= 0.000001) continue;

    $take = min($remaining, $balance);
    if ($take > 0.000001) {
      $segments[] = ['warehouse_id'=>$wid, 'warehouse_code'=>$code, 'qty'=>round($take, 3)];
      $remaining = round($remaining - $take, 3);
    }
    if ($remaining <= 0.000001) break;
  }

  if ($remaining > 0.000001) {
    $available = $qty - $remaining;
    throw new RuntimeException(
      'No hay inventario transferible suficiente para ' . ($sku !== '' ? $sku : ('producto #' . $productId)) .
      '. Solicitado: ' . number_format($qty, 3, '.', '') .
      ', disponible: ' . number_format($available, 3, '.', '') .
      '. Almacenes permitidos: ' . implode('/', $labels) . '.'
    );
  }

  return $segments;
}

function insert_inventory_move(PDO $pdo, int $companyId, int $locationId, ?int $warehouseId, string $workDate, int $productId, float $qty, string $moveType, string $refType, int $refId, string $notes, int $userId): void {
  $st = $pdo->prepare("
    INSERT INTO inventory_moves (
      company_id, location_id, warehouse_id, work_date,
      product_id, qty, move_type, channel_id,
      ref_type, ref_id, notes, created_by, created_at
    ) VALUES (
      ?, ?, ?, ?,
      ?, ?, ?, NULL,
      ?, ?, ?, ?, NOW()
    )
  ");
  $st->execute([
    $companyId, $locationId, $warehouseId, $workDate,
    $productId, $qty, $moveType, $refType, $refId, $notes, $userId
  ]);
}

/**
 * Inventario transferible por producto.
 * Usa la misma fuente oficial que Almacén: inventory_moves filtrado por empresa,
 * localidad y almacenes autorizados para el producto.
 */
function build_transfer_inventory_snapshot(PDO $pdo, int $companyId, int $locationId): array {
  $st = $pdo->prepare("
    SELECT id, sku, name, uom
    FROM products
    WHERE company_id=?
      AND active=1
    ORDER BY sku
  ");
  $st->execute([$companyId]);

  $rows = [];
  foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $p) {
    $pid = (int)$p['id'];
    $allowed = transfer_allowed_warehouses($pdo, $companyId, $locationId, $pid, 'source');
    $qtyTotal = 0.0;
    $breakdown = [];

    foreach ($allowed as $wh) {
      $qty = get_product_warehouse_balance($pdo, $companyId, $locationId, $pid, (int)$wh['warehouse_id']);
      $qtyTotal += $qty;
      $breakdown[] = [
        'warehouse_id'=>(int)$wh['warehouse_id'],
        'code'=>(string)$wh['code'],
        'name'=>(string)($wh['name'] ?? ''),
        'qty'=>round($qty, 3),
      ];
    }

    $rows[$pid] = [
      'id'=>$pid,
      'sku'=>(string)$p['sku'],
      'name'=>(string)$p['name'],
      'uom'=>(string)$p['uom'],
      'qty_total'=>round($qtyTotal, 3),
      'qty_available'=>round($qtyTotal, 3),
      'qty_in_transit_out'=>0.0,
      'qty_pending_in'=>0.0,
      'warehouse_breakdown'=>$breakdown,
    ];
  }

  // Informativo: el OUT ya fue descontado en inventory_moves al enviar; no se vuelve a restar.
  $st = $pdo->prepare("
    SELECT ti.product_id, SUM(ti.qty) AS qty
    FROM transfers t
    JOIN transfer_items ti ON ti.transfer_id=t.id
    WHERE t.from_company_id=?
      AND t.to_company_id=?
      AND t.from_location_id=?
      AND t.status='SENT'
    GROUP BY ti.product_id
  ");
  $st->execute([$companyId, $companyId, $locationId]);
  foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
    $pid = (int)$r['product_id'];
    if (isset($rows[$pid])) $rows[$pid]['qty_in_transit_out'] = round((float)$r['qty'], 3);
  }

  // Informativo: aún no suma a inventario destino hasta confirmar recepción.
  $st = $pdo->prepare("
    SELECT ti.product_id, SUM(ti.qty) AS qty
    FROM transfers t
    JOIN transfer_items ti ON ti.transfer_id=t.id
    WHERE t.from_company_id=?
      AND t.to_company_id=?
      AND t.to_location_id=?
      AND t.status='SENT'
    GROUP BY ti.product_id
  ");
  $st->execute([$companyId, $companyId, $locationId]);
  foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
    $pid = (int)$r['product_id'];
    if (isset($rows[$pid])) $rows[$pid]['qty_pending_in'] = round((float)$r['qty'], 3);
  }

  return array_values($rows);
}

function get_product_available_for_transfer(PDO $pdo, int $companyId, int $locationId, int $productId): float {
  foreach (build_transfer_inventory_snapshot($pdo, $companyId, $locationId) as $r) {
    if ((int)$r['id'] === $productId) return (float)$r['qty_available'];
  }
  return 0.0;
}

/* ===================== API ===================== */
if ($action !== '') {
  need_csrf();
  ensure_transfer_cols($pdo);

  if ($action === 'destinations') {
    /*
      Regla MES: las transferencias se realizan exclusivamente entre
      localidades activas de la MISMA empresa seleccionada.
      No se permite transferir inventario entre empresas diferentes.
    */
    $st = $pdo->prepare("
      SELECT l.id, l.name, l.code, l.company_id, c.name AS company_name
      FROM locations l
      JOIN companies c ON c.id = l.company_id
      WHERE l.company_id = ?
        AND l.active = 1
        AND l.id <> ?
      ORDER BY l.name
    ");
    $st->execute([$companyId, $locationId]);
    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'products') {
    $st = $pdo->prepare("
      SELECT id, sku, name, uom
      FROM products
      WHERE company_id = ?
        AND active = 1
      ORDER BY sku
    ");
    $st->execute([$companyId]);
    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'product_conversions') {
    $st = $pdo->prepare("
      SELECT pc.from_product_id, pc.to_product_id, pc.factor,
             p1.sku AS from_sku, p1.name AS from_name,
             p2.sku AS to_sku,   p2.name AS to_name
      FROM product_conversions pc
      JOIN products p1 ON p1.id = pc.from_product_id
      JOIN products p2 ON p2.id = pc.to_product_id
      WHERE pc.company_id = ?
        AND pc.active = 1
      ORDER BY p1.sku, p2.sku
    ");
    $st->execute([$companyId]);
    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'inventory_summary') {
    $rows = build_transfer_inventory_snapshot($pdo, $companyId, $locationId);
    jexit(['ok'=>true, 'rows'=>$rows]);
  }

  if ($action === 'product_stock_info') {
    $productId = (int)($_GET['product_id'] ?? 0);
    $toLocationId = (int)($_GET['to_location_id'] ?? 0);

    if ($productId <= 0) {
      jexit(['ok'=>false,'error'=>'product_id requerido'], 400);
    }

    $originAvailable = get_product_available_for_transfer($pdo, $companyId, $locationId, $productId);

    $destinationAvailable = 0.0;
    $destinationProductId = 0;
    $destinationCompanyId = 0;
    $destinationProductMissing = false;

    if ($toLocationId > 0) {
      $st = $pdo->prepare("
        SELECT id
        FROM locations
        WHERE id = ?
          AND company_id = ?
          AND active = 1
          AND id <> ?
        LIMIT 1
      ");
      $st->execute([$toLocationId, $companyId, $locationId]);
      if (!$st->fetchColumn()) {
        jexit(['ok'=>false,'error'=>'El destino debe ser una localidad activa de la misma empresa.'], 409);
      }

      $destinationCompanyId = $companyId;
      $destinationProductId = $productId;
      $destinationAvailable = get_product_available_for_transfer($pdo, $companyId, $toLocationId, $productId);
      $destinationProductMissing = false;
    }

    jexit([
      'ok' => true,
      'origin_available' => round($originAvailable, 3),
      'destination_available' => round($destinationAvailable, 3),
      'destination_company_id' => $destinationCompanyId,
      'destination_product_id' => $destinationProductId,
      'destination_product_missing' => $destinationProductMissing,
    ]);
  }

  if ($action === 'create') {
    if (!$canWrite) jexit(['ok'=>false,'error'=>'Sin permiso'], 403);

    $d = read_json();
    $toLocationId = (int)($d['to_location_id'] ?? 0);
    $notes        = trim((string)($d['notes'] ?? ''));

    if ($toLocationId <= 0) jexit(['ok'=>false,'error'=>'Destino requerido'], 400);
    if ($toLocationId === $locationId) jexit(['ok'=>false,'error'=>'El destino no puede ser el mismo'], 400);

    $st = $pdo->prepare("
      SELECT company_id, active
      FROM locations
      WHERE id = ?
        AND company_id = ?
        AND active = 1
      LIMIT 1
    ");
    $st->execute([$toLocationId, $companyId]);
    $loc = $st->fetch(PDO::FETCH_ASSOC);

    if (!$loc) {
      jexit(['ok'=>false,'error'=>'Destino inválido. Solo puedes transferir a localidades activas de la misma empresa.'], 409);
    }

    $today = date('Y-m-d');
    $toCompanyId = $companyId;

    $st = $pdo->prepare("
      INSERT INTO transfers(
        company_id, from_company_id, to_company_id,
        from_location_id, to_location_id,
        transfer_date, work_date, status,
        created_by, notes, created_at, transfer_type
      ) VALUES (
        ?, ?, ?, ?, ?, ?, ?, 'DRAFT', ?, ?, NOW(), 'MOVE'
      )
    ");
    $st->execute([
      $companyId,
      $companyId,
      $toCompanyId,
      $locationId,
      $toLocationId,
      $today,
      $today,
      (int)$u['id'],
      ($notes === '' ? null : $notes)
    ]);

    jexit(['ok'=>true, 'id'=>(int)$pdo->lastInsertId()]);
  }

  if ($action === 'item_save') {
    if (!$canWrite) jexit(['ok'=>false,'error'=>'Sin permiso'], 403);

    $d = read_json();
    $transferId = (int)($d['transfer_id'] ?? 0);
    $productId  = (int)($d['product_id'] ?? 0);
    $qty        = (float)($d['qty'] ?? 0);
    $convToId   = (int)($d['conversion_to_product_id'] ?? 0);
    $convFactor = ($d['conversion_factor'] ?? null);

    if ($transferId <= 0 || $productId <= 0 || $qty <= 0) {
      jexit(['ok'=>false,'error'=>'Datos inválidos'], 400);
    }
    if ($convToId > 0) {
      jexit(['ok'=>false,'error'=>'Las conversiones están bloqueadas en Transferencias. Usa Producción Cubicado para transformar y después transfiere el producto final.'], 409);
    }

    $st = $pdo->prepare("
      SELECT id, status, from_company_id, from_location_id
      FROM transfers
      WHERE id = ?
      LIMIT 1
    ");
    $st->execute([$transferId]);
    $t = $st->fetch(PDO::FETCH_ASSOC);

    if (!$t) jexit(['ok'=>false,'error'=>'Transferencia no encontrada'], 404);
    if ((string)$t['status'] !== 'DRAFT') jexit(['ok'=>false,'error'=>'Solo se puede editar en DRAFT'], 409);
    if ((int)$t['from_company_id'] !== $companyId || (int)$t['from_location_id'] !== $locationId) {
      jexit(['ok'=>false,'error'=>'Solo el origen puede editar'], 403);
    }

    $available = get_product_available_for_transfer($pdo, $companyId, $locationId, $productId);
    if ($qty > $available) {
      jexit(['ok'=>false,'error'=>'La cantidad excede el disponible del origen'], 409);
    }

    $st = $pdo->prepare("
      INSERT INTO transfer_items(
        transfer_id, product_id, qty, conversion_to_product_id, conversion_factor
      ) VALUES (?, ?, ?, ?, ?)
      ON DUPLICATE KEY UPDATE
        qty = VALUES(qty),
        conversion_to_product_id = VALUES(conversion_to_product_id),
        conversion_factor = VALUES(conversion_factor)
    ");
    $st->execute([
      $transferId,
      $productId,
      $qty,
      $convToId > 0 ? $convToId : null,
      ($convFactor !== null && $convFactor !== '' ? (float)$convFactor : null)
    ]);

    jexit(['ok'=>true]);
  }

  if ($action === 'items') {
    $transferId = (int)($_GET['transfer_id'] ?? 0);
    if ($transferId <= 0) jexit(['ok'=>false,'error'=>'transfer_id requerido'], 400);

    $st = $pdo->prepare("
      SELECT
        ti.id,
        ti.transfer_id,
        ti.product_id,
        ti.qty,
        ti.conversion_to_product_id,
        ti.conversion_factor,
        p.sku,
        p.name AS product_name,
        p.uom,
        pc.sku AS conv_sku,
        pc.name AS conv_name
      FROM transfer_items ti
      JOIN products p ON p.id = ti.product_id
      LEFT JOIN products pc ON pc.id = ti.conversion_to_product_id
      WHERE ti.transfer_id = ?
      ORDER BY p.sku
    ");
    $st->execute([$transferId]);

    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'detail') {
    $transferId = (int)($_GET['transfer_id'] ?? 0);
    if ($transferId <= 0) jexit(['ok'=>false,'error'=>'transfer_id requerido'], 400);

    $st = $pdo->prepare("
      SELECT
        t.*,
        lf.name AS from_location_name,
        lf.code AS from_location_code,
        lt.name AS to_location_name,
        lt.code AS to_location_code
      FROM transfers t
      JOIN locations lf ON lf.id = t.from_location_id
      JOIN locations lt ON lt.id = t.to_location_id
      WHERE t.id = ?
      LIMIT 1
    ");
    $st->execute([$transferId]);
    $transfer = $st->fetch(PDO::FETCH_ASSOC);

    if (!$transfer) jexit(['ok'=>false,'error'=>'Transferencia no encontrada'], 404);

    $st = $pdo->prepare("
      SELECT
        ti.id,
        ti.transfer_id,
        ti.product_id,
        ti.qty,
        ti.conversion_to_product_id,
        ti.conversion_factor,
        p.sku,
        p.name AS product_name,
        p.uom,
        pc.sku AS conv_sku,
        pc.name AS conv_name
      FROM transfer_items ti
      JOIN products p ON p.id = ti.product_id
      LEFT JOIN products pc ON pc.id = ti.conversion_to_product_id
      WHERE ti.transfer_id = ?
      ORDER BY p.sku
    ");
    $st->execute([$transferId]);
    $items = $st->fetchAll(PDO::FETCH_ASSOC);

    jexit(['ok'=>true, 'transfer'=>$transfer, 'items'=>$items]);
  }

  if ($action === 'mailbox') {
    $folder = strtolower(trim((string)($_GET['folder'] ?? 'pending')));
    if (!in_array($folder, ['pending','received','sent'], true)) {
      $folder = 'pending';
    }
    $date = valid_mail_date((string)($_GET['date'] ?? ''));
    $scopeFilter = mailbox_filter_scope($folder, $companyId, $locationId, $date);

    $sql = "
      SELECT
        t.*,
        lf.name AS from_location_name,
        lf.code AS from_location_code,
        lt.name AS to_location_name,
        lt.code AS to_location_code,
        cf.name AS from_company_name,
        ct.name AS to_company_name,
        COALESCE(itemAgg.items_count, 0) AS items_count,
        COALESCE(itemAgg.total_qty, 0) AS total_qty,
        COALESCE(itemAgg.item_summary, '') AS item_summary
      FROM transfers t
      JOIN locations lf ON lf.id = t.from_location_id
      JOIN locations lt ON lt.id = t.to_location_id
      LEFT JOIN companies cf ON cf.id = t.from_company_id
      LEFT JOIN companies ct ON ct.id = t.to_company_id
      LEFT JOIN (
        SELECT
          ti.transfer_id,
          COUNT(*) AS items_count,
          SUM(ti.qty) AS total_qty,
          GROUP_CONCAT(CONCAT(p.sku, ' × ', ROUND(ti.qty, 3)) ORDER BY p.sku SEPARATOR ' · ') AS item_summary
        FROM transfer_items ti
        JOIN products p ON p.id = ti.product_id
        GROUP BY ti.transfer_id
      ) itemAgg ON itemAgg.transfer_id = t.id
      WHERE " . $scopeFilter['sql'] . "
      ORDER BY
        CASE WHEN t.status = 'SENT' THEN 0 ELSE 1 END,
        COALESCE(t.arrive_at, t.depart_at, t.created_at) DESC,
        t.id DESC
      LIMIT 300
    ";
    $st = $pdo->prepare($sql);
    $st->execute($scopeFilter['params']);

    jexit([
      'ok' => true,
      'folder' => $scopeFilter['folder'],
      'date' => $date,
      'rows' => $st->fetchAll(PDO::FETCH_ASSOC),
      'counts' => [
        'pending' => mailbox_count($pdo, 'pending', $companyId, $locationId, $date),
        'received' => mailbox_count($pdo, 'received', $companyId, $locationId, $date),
        'sent' => mailbox_count($pdo, 'sent', $companyId, $locationId, $date),
      ],
    ]);
  }

  if ($action === 'list_outbox') {
    $st = $pdo->prepare("
      SELECT
        t.*,
        lf.name AS from_location_name,
        lt.name AS to_location_name
      FROM transfers t
      JOIN locations lf ON lf.id = t.from_location_id
      JOIN locations lt ON lt.id = t.to_location_id
      WHERE t.from_company_id = ?
        AND t.to_company_id = ?
        AND t.from_location_id = ?
      ORDER BY t.id DESC
      LIMIT 200
    ");
    $st->execute([$companyId, $companyId, $locationId]);
    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'list_inbox') {
    $st = $pdo->prepare("
      SELECT
        t.*,
        lf.name AS from_location_name,
        lt.name AS to_location_name
      FROM transfers t
      JOIN locations lf ON lf.id = t.from_location_id
      JOIN locations lt ON lt.id = t.to_location_id
      WHERE t.from_company_id = ?
        AND t.to_company_id = ?
        AND t.to_location_id = ?
      ORDER BY t.id DESC
      LIMIT 200
    ");
    $st->execute([$companyId, $companyId, $locationId]);
    jexit(['ok'=>true, 'rows'=>$st->fetchAll(PDO::FETCH_ASSOC)]);
  }

  if ($action === 'send') {
    if (!$canWrite) jexit(['ok'=>false,'error'=>'Sin permiso'], 403);

    $d = read_json();
    $transferId = (int)($d['transfer_id'] ?? 0);
    $driver     = trim((string)($d['driver_name'] ?? ''));
    $truck      = trim((string)($d['truck_no'] ?? ''));
    $departAt   = fmt_dt_for_db((string)($d['depart_at'] ?? ''));

    if ($transferId <= 0) jexit(['ok'=>false,'error'=>'transfer_id requerido'], 400);
    if ($driver === '')   jexit(['ok'=>false,'error'=>'Chofer requerido'], 400);
    if ($truck === '')    jexit(['ok'=>false,'error'=>'Unidad / transporte requerida'], 400);
    if (!$departAt)       jexit(['ok'=>false,'error'=>'Fecha/hora salida requerida'], 400);

    $pdo->beginTransaction();
    try {
      $st = $pdo->prepare("SELECT * FROM transfers WHERE id = ? FOR UPDATE");
      $st->execute([$transferId]);
      $t = $st->fetch(PDO::FETCH_ASSOC);

      if (!$t) throw new RuntimeException('Transferencia no encontrada');
      if ((string)$t['status'] !== 'DRAFT') throw new RuntimeException('Solo se puede enviar desde DRAFT');
      if ((int)$t['from_company_id'] !== $companyId || (int)$t['from_location_id'] !== $locationId) {
        throw new RuntimeException('Solo el origen puede enviar');
      }

      $st = $pdo->prepare("
        SELECT ti.product_id, ti.qty, ti.conversion_to_product_id, ti.conversion_factor, p.sku, p.name AS product_name
        FROM transfer_items ti
        JOIN products p ON p.id = ti.product_id
        WHERE ti.transfer_id = ?
        ORDER BY p.sku
      ");
      $st->execute([$transferId]);
      $items = $st->fetchAll(PDO::FETCH_ASSOC);

      if (!$items) throw new RuntimeException('Agrega al menos un artículo');

      $workDate = date('Y-m-d', strtotime($departAt));
      if ($workDate === '1970-01-01') $workDate = date('Y-m-d');

      foreach ($items as $it) {
        $productId = (int)$it['product_id'];
        $qty = (float)$it['qty'];
        if ((int)($it['conversion_to_product_id'] ?? 0) > 0) {
          throw new RuntimeException('Las conversiones dentro de transferencias están bloqueadas para no alterar inventario. Usa Cubicado para transformar producto y luego transfiere el producto final.');
        }
        $available = get_product_available_for_transfer($pdo, $companyId, $locationId, $productId);
        if ($qty > $available) {
          throw new RuntimeException('La cantidad excede el disponible del origen para ' . (string)$it['sku']);
        }

        $segments = resolve_transfer_out_segments($pdo, $companyId, $locationId, $productId, $qty);
        foreach ($segments as $seg) {
          insert_inventory_move(
            $pdo,
            (int)$t['from_company_id'],
            (int)$t['from_location_id'],
            $seg['warehouse_id'] !== null ? (int)$seg['warehouse_id'] : null,
            $workDate,
            $productId,
            (float)$seg['qty'],
            'OUT',
            'TRANSFER_OUT',
            $transferId,
            'Transferencia enviada #' . $transferId . ' | ' . (string)$it['sku'],
            (int)$u['id']
          );
        }
      }

      $st = $pdo->prepare("
        UPDATE transfers
        SET status='SENT',
            driver_name=?,
            truck_no=?,
            depart_at=?
        WHERE id=?
          AND status='DRAFT'
      ");
      $st->execute([$driver, $truck, $departAt, $transferId]);

      $pdo->commit();
      jexit(['ok'=>true]);
    } catch (Throwable $e) {
      if ($pdo->inTransaction()) $pdo->rollBack();
      jexit(['ok'=>false,'error'=>$e->getMessage()], 500);
    }
  }

  if ($action === 'cancel') {
    if (!$canWrite) jexit(['ok'=>false,'error'=>'Sin permiso'], 403);

    $d = read_json();
    $transferId = (int)($d['transfer_id'] ?? 0);

    if ($transferId <= 0) jexit(['ok'=>false,'error'=>'transfer_id requerido'], 400);

    $pdo->beginTransaction();
    try {
      $st = $pdo->prepare("SELECT * FROM transfers WHERE id = ? FOR UPDATE");
      $st->execute([$transferId]);
      $t = $st->fetch(PDO::FETCH_ASSOC);

      if (!$t) throw new RuntimeException('Transferencia no encontrada');
      if ((int)$t['from_company_id'] !== $companyId || (int)$t['from_location_id'] !== $locationId) {
        throw new RuntimeException('Solo el origen puede cancelar');
      }
      if (!in_array((string)$t['status'], ['DRAFT','SENT'], true)) {
        throw new RuntimeException('Solo se puede cancelar si está DRAFT o SENT');
      }

      if ((string)$t['status'] === 'SENT') {
        $st = $pdo->prepare("
          SELECT product_id, warehouse_id, SUM(qty) AS qty
          FROM inventory_moves
          WHERE company_id = ?
            AND location_id = ?
            AND ref_type IN ('TRANSFER','TRANSFER_OUT')
            AND ref_id = ?
            AND move_type = 'OUT'
          GROUP BY product_id, warehouse_id
        ");
        $st->execute([(int)$t['from_company_id'], (int)$t['from_location_id'], $transferId]);
        foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $mv) {
          insert_inventory_move(
            $pdo,
            (int)$t['from_company_id'],
            (int)$t['from_location_id'],
            $mv['warehouse_id'] !== null ? (int)$mv['warehouse_id'] : null,
            date('Y-m-d'),
            (int)$mv['product_id'],
            (float)$mv['qty'],
            'IN',
            'TRANSFER_CANCEL',
            $transferId,
            'Cancelación de transferencia #' . $transferId,
            (int)$u['id']
          );
        }
      }

      $st = $pdo->prepare("
        UPDATE transfers
        SET status='CANCELLED'
        WHERE id=?
          AND status IN ('DRAFT','SENT')
      ");
      $st->execute([$transferId]);

      $pdo->commit();
      jexit(['ok'=>true]);
    } catch (Throwable $e) {
      if ($pdo->inTransaction()) $pdo->rollBack();
      jexit(['ok'=>false,'error'=>$e->getMessage()], 500);
    }
  }

  if ($action === 'receive') {
    if (!$canWrite) jexit(['ok'=>false,'error'=>'Sin permiso'], 403);

    $d = read_json();
    $transferId = (int)($d['transfer_id'] ?? 0);
    $arriveAt   = fmt_dt_for_db((string)($d['arrive_at'] ?? ''));

    if ($transferId <= 0) jexit(['ok'=>false,'error'=>'transfer_id requerido'], 400);
    if (!$arriveAt)       jexit(['ok'=>false,'error'=>'Fecha/hora llegada requerida'], 400);

    $pdo->beginTransaction();
    try {
      $st = $pdo->prepare("SELECT * FROM transfers WHERE id = ? FOR UPDATE");
      $st->execute([$transferId]);
      $t = $st->fetch(PDO::FETCH_ASSOC);

      if (!$t) throw new RuntimeException('Transferencia no encontrada');
      if ((string)$t['status'] !== 'SENT') throw new RuntimeException('Solo se puede recibir si está en SENT');
      if ((int)$t['from_company_id'] !== $companyId || (int)$t['to_company_id'] !== $companyId) {
        throw new RuntimeException('No se pueden recibir transferencias entre empresas diferentes.');
      }
      if ((int)$t['to_location_id'] !== $locationId) {
        throw new RuntimeException('Solo la localidad destino puede recibir.');
      }

      $st = $pdo->prepare("
        SELECT ti.product_id, ti.qty, p.sku
        FROM transfer_items ti
        JOIN products p ON p.id = ti.product_id
        WHERE ti.transfer_id = ?
      ");
      $st->execute([$transferId]);
      $items = $st->fetchAll(PDO::FETCH_ASSOC);

      if (!$items) throw new RuntimeException('La transferencia no tiene artículos');

      $workDate = date('Y-m-d', strtotime($arriveAt));
      if ($workDate === '1970-01-01') $workDate = date('Y-m-d');

      foreach ($items as $it) {
        $sourceProductId = (int)$it['product_id'];
        $qty       = (float)$it['qty'];
        $sku       = (string)$it['sku'];

        /*
          La transferencia es entre localidades de la misma empresa:
          el catálogo de productos es el mismo y se conserva product_id.
        */
        $destinationProductId = $sourceProductId;
        if (get_product_sku($pdo, $companyId, $destinationProductId) === '') {
          throw new RuntimeException('El producto ' . $sku . ' no existe o está inactivo en la empresa actual.');
        }

        $warehouseId = default_transfer_warehouse_id($pdo, $companyId, (int)$t['to_location_id'], $destinationProductId, $sku);
        if ($warehouseId === null || $warehouseId <= 0) {
          throw new RuntimeException('No se encontró almacén destino permitido para ' . $sku . '. BARRA debe entrar a CF1/CF2 y producto terminado a CF3.');
        }

        insert_inventory_move(
          $pdo,
          $companyId,
          (int)$t['to_location_id'],
          $warehouseId,
          $workDate,
          $destinationProductId,
          $qty,
          'IN',
          'TRANSFER_IN',
          $transferId,
          'Transferencia recibida #' . $transferId . ' | ' . $sku . ' | product_id origen ' . $sourceProductId,
          (int)$u['id']
        );
      }

      $st = $pdo->prepare("
        UPDATE transfers
        SET status='RECEIVED',
            received_by=?,
            received_at=NOW(),
            arrive_at=?
        WHERE id=?
      ");
      $st->execute([(int)$u['id'], $arriveAt, $transferId]);

      $pdo->commit();
      jexit(['ok'=>true]);
    } catch (Throwable $e) {
      if ($pdo->inTransaction()) $pdo->rollBack();
      jexit(['ok'=>false,'error'=>$e->getMessage()], 500);
    }
  }

  jexit(['ok'=>false,'error'=>'Acción inválida'], 404);
}

/* ===================== UI ===================== */
$csrf = csrf_token();

$st = $pdo->prepare("SELECT name FROM companies WHERE id=?");
$st->execute([$companyId]);
$companyName = (string)($st->fetchColumn() ?: '');

$st = $pdo->prepare("SELECT name FROM locations WHERE id=?");
$st->execute([$locationId]);
$locationName = (string)($st->fetchColumn() ?: '');

$isHieloBarraScope = transfer_scope_is_hielo_barra($pdo, $companyId, $locationId);
$scopeWarehouseNote = $isHieloBarraScope
  ? 'Flujo Hielo Barra: CF1/CF2/CF3 según producto'
  : 'Flujo MES: almacenes configurados de esta planta';
?>
<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BReport · Transferencias</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">

  <style>
    :root{
      --bg1:#031126;
      --bg2:#071d3b;
      --panel:rgba(7,25,57,.80);
      --panel2:rgba(10,31,70,.90);
      --line:rgba(145,188,255,.14);
      --txt:#eef4ff;
      --txt-strong:#ffffff;
      --txt-soft:#c7d8f4;
      --muted:#9fb6d9;
      --muted-2:#86a2cd;
      --blue:#2f7bff;
      --blue2:#57a5ff;
      --gold:#f4be59;
      --gold-soft:#ffe19b;
      --green:#72f0a5;
      --danger:#ff8fa3;
      --shadow:0 20px 60px rgba(0,0,0,.35);
      --radius:24px;
    }

    *{box-sizing:border-box}
    html,body{min-height:100%}

    body{
      margin:0;
      font-family:'Inter',system-ui,Arial,sans-serif;
      color:var(--txt);
      background:
        radial-gradient(circle at 10% 18%, rgba(28,112,255,.24), transparent 24%),
        radial-gradient(circle at 88% 82%, rgba(0,190,255,.12), transparent 20%),
        linear-gradient(135deg, #021026 0%, #03152e 42%, #071d3b 100%);
      background-attachment:fixed;
      -webkit-font-smoothing:antialiased;
      -moz-osx-font-smoothing:grayscale;
    }

    .topbar{
      background:rgba(3,16,40,.84);
      backdrop-filter:blur(12px);
      border-bottom:1px solid rgba(255,255,255,.07);
      box-shadow:0 8px 24px rgba(0,0,0,.18);
      position:sticky;
      top:0;
      z-index:1040;
    }

    .page-wrap{padding:28px 18px 40px}
    .shell{max-width:1380px;margin:0 auto}
    .panel-pad{padding:22px}

    .brand-title{
      font-weight:800;
      font-size:1.95rem;
      color:var(--txt-strong);
      line-height:1.1;
      margin:0;
      text-shadow:0 1px 0 rgba(0,0,0,.16);
    }

    .brand-sub{
      color:var(--txt-soft);
      font-size:1rem;
      margin-top:4px;
      font-weight:500;
    }

    .glass{
      background:linear-gradient(180deg, rgba(7,27,63,.96), rgba(3,18,46,.94));
      border:1px solid rgba(145,188,255,.12);
      border-radius:var(--radius);
      box-shadow:var(--shadow);
      backdrop-filter:blur(10px);
    }

    .section-title,
    .fw-bold,
    .fw-semibold,
    .kpi-value,
    .table thead th{
      color:var(--txt-strong);
      text-shadow:0 1px 0 rgba(0,0,0,.18);
    }

    .muted,
    .small,
    .small-note,
    .kpi-label,
    .kpi-hint{
      color:var(--txt-soft) !important;
    }

    .kpi-card{
      position:relative;
      overflow:hidden;
      min-height:140px;
      border-radius:24px;
      border:1px solid rgba(160,198,255,.14);
      background:linear-gradient(180deg, rgba(8,25,60,.98), rgba(4,18,42,.95));
      transition:transform .18s ease,border-color .18s ease, box-shadow .18s ease;
      box-shadow:
        inset 0 1px 0 rgba(255,255,255,.04),
        0 18px 40px rgba(0,0,0,.24);
    }

    .kpi-card::after{
      content:"";
      position:absolute;
      right:-40px;
      top:-40px;
      width:120px;
      height:120px;
      border-radius:50%;
      background:radial-gradient(circle, rgba(87,165,255,.18), transparent 70%);
      pointer-events:none;
    }

    .kpi-card:hover{
      transform:translateY(-3px);
      border-color:rgba(123,179,255,.28);
    }

    .kpi-top{
      display:flex;
      align-items:center;
      gap:12px;
      margin-bottom:10px;
    }

    .kpi-icon{
      width:46px;
      height:46px;
      border-radius:16px;
      display:flex;
      align-items:center;
      justify-content:center;
      font-size:1.2rem;
      color:#fff;
      background:linear-gradient(135deg, rgba(47,123,255,.85), rgba(87,165,255,.85));
      box-shadow:0 12px 22px rgba(36,97,210,.22);
      flex:0 0 auto;
    }

    .kpi-label{
      color:#c2d5f8 !important;
      font-size:.95rem;
      font-weight:700;
      margin:0;
    }

    .kpi-value{
      color:#fff !important;
      font-size:2.2rem;
      font-weight:800;
      letter-spacing:.35px;
      margin:4px 0 2px;
    }

    .kpi-hint{
      color:#bfd1f2 !important;
      font-size:.9rem;
      line-height:1.35;
    }

    .badge-soft{
      display:inline-flex;
      align-items:center;
      gap:8px;
      padding:9px 16px;
      border-radius:999px;
      background:rgba(244,190,89,.12);
      color:#ffd978;
      border:1px solid rgba(244,190,89,.28);
      font-weight:800;
      white-space:nowrap;
      box-shadow:inset 0 1px 0 rgba(255,255,255,.04);
    }

    .small-note{
      border-radius:16px;
      border:1px solid rgba(244,190,89,.22);
      background:rgba(244,190,89,.08);
      color:var(--gold-soft) !important;
      padding:12px 14px;
      font-size:.94rem;
      font-weight:600;
      line-height:1.45;
    }

    .table-wrap{
      border:1px solid rgba(162,198,255,.10);
      border-radius:22px;
      overflow:hidden;
      background:rgba(5,20,47,.48);
    }

    .table{
      margin:0;
      color:var(--txt);
      --bs-table-bg:transparent;
      --bs-table-striped-bg:rgba(255,255,255,.018);
      --bs-table-hover-bg:rgba(68,124,220,.08);
      --bs-table-border-color:rgba(169,203,255,.06);
    }

    .table thead th{
      color:#f8fbff !important;
      font-weight:800;
      font-size:1rem;
      background:rgba(11,30,70,.88);
      white-space:nowrap;
      border-bottom:1px solid rgba(169,203,255,.10);
      padding-top:1rem;
      padding-bottom:1rem;
    }

    .table tbody td{
      color:#edf4ff !important;
      vertical-align:middle;
      border-color:rgba(169,203,255,.06);
      font-weight:500;
    }

    .table tbody tr:hover td{
      color:#ffffff !important;
    }

    .mono{
      font-family:ui-monospace,SFMono-Regular,Consolas,monospace;
      font-variant-numeric:tabular-nums;
      color:#f7fbff !important;
      letter-spacing:.2px;
    }

    .strong-num{
      font-weight:800;
      color:#fff !important;
    }

    .form-label{
      color:#d2e0f8;
      font-weight:700;
      font-size:.92rem;
    }

    .form-control,
    .form-select{
      min-height:50px;
      border-radius:16px;
      color:#fff !important;
      background:rgba(8,24,58,.92);
      border:1px solid rgba(160,197,255,.16);
      padding-left:14px;
      padding-right:14px;
    }

    .form-control::placeholder{
      color:#9fb4d8;
      opacity:1;
    }

    .form-control:focus,
    .form-select:focus{
      color:#fff;
      background:rgba(8,24,58,.98);
      border-color:rgba(78,160,255,.56);
      box-shadow:0 0 0 .18rem rgba(57,129,255,.14);
    }

    .toolbar-btn{
      border-radius:16px;
      border:1px solid rgba(160,199,255,.18);
      background:rgba(14,35,78,.88);
      color:#f7fbff !important;
      font-weight:700;
      min-height:42px;
      padding:.58rem .95rem;
      transition:all .18s ease;
      box-shadow:inset 0 1px 0 rgba(255,255,255,.03);
    }

    .toolbar-btn:hover{
      transform:translateY(-1px);
      color:#fff !important;
      background:rgba(23,48,98,.96);
      border-color:rgba(160,199,255,.24);
    }

    .btn-primary-soft{
      color:#fff !important;
      font-weight:800;
      border:none;
      border-radius:18px;
      min-height:44px;
      background:linear-gradient(135deg, #1e63ff, #3898ff);
      box-shadow:0 12px 32px rgba(37,113,255,.28);
    }

    .btn-primary-soft:hover{ color:#fff !important; transform:translateY(-1px) }

    .btn-green-soft{
      color:#fff !important;
      font-weight:800;
      border:none;
      border-radius:18px;
      min-height:44px;
      background:linear-gradient(135deg, #149f5e, #24cd7a);
      box-shadow:0 12px 32px rgba(20,159,94,.24);
    }

    .btn-green-soft:hover{ color:#fff !important; transform:translateY(-1px) }

    .btn-danger-soft{
      color:#fff !important;
      font-weight:800;
      border:none;
      border-radius:18px;
      min-height:44px;
      background:linear-gradient(135deg, #d24d65, #ff8298);
      box-shadow:0 12px 32px rgba(210,77,101,.25);
    }

    .btn-danger-soft:hover{ color:#fff !important; transform:translateY(-1px) }

    .btn-action{
      border-radius:14px;
      border:1px solid rgba(160,199,255,.16);
      background:rgba(15,37,81,.84);
      color:#fff !important;
      font-weight:700;
      min-height:38px;
      padding:.42rem .75rem;
      display:inline-flex;
      align-items:center;
      gap:8px;
      transition:all .18s ease;
    }

    .btn-action:hover{
      transform:translateY(-1px);
      background:rgba(28,53,104,.96);
      color:#fff !important;
    }

    .btn-action.detail{
      border-color:rgba(88,164,255,.22);
      box-shadow:0 8px 18px rgba(32,84,180,.12);
    }

    .btn-action.receive{
      background:rgba(18,110,68,.28);
      border-color:rgba(88,231,160,.22);
    }

    .btn-action.cancel{
      background:rgba(135,36,56,.28);
      border-color:rgba(255,143,163,.20);
    }

    .status-chip{
      display:inline-flex;
      align-items:center;
      justify-content:center;
      min-width:112px;
      padding:8px 14px;
      border-radius:999px;
      font-weight:800;
      font-size:.82rem;
      letter-spacing:.32px;
      border:1px solid transparent;
      text-transform:uppercase;
    }

    .status-draft{
      background:rgba(160,170,190,.12);
      color:#d9e2f5;
      border-color:rgba(160,170,190,.22);
    }

    .status-sent{
      background:rgba(244,190,89,.14);
      color:#ffd978;
      border-color:rgba(244,190,89,.24);
    }

    .status-received{
      background:rgba(114,240,165,.14);
      color:#7cf0ae;
      border-color:rgba(114,240,165,.24);
    }

    .status-cancelled{
      background:rgba(255,143,163,.14);
      color:#ff9cb0;
      border-color:rgba(255,143,163,.24);
    }

    .modal-content{
      background:linear-gradient(180deg, rgba(7,24,58,.985), rgba(4,18,44,.985));
      border:1px solid rgba(161,198,255,.14);
      color:#fff;
      border-radius:24px;
    }

    .modal-header,
    .modal-footer{
      border-color:rgba(161,198,255,.10);
    }

    .modal-title{
      font-weight:800;
      color:#fff;
    }

    .item-row{
      padding:16px;
      border-radius:18px;
      border:1px solid rgba(160,197,255,.10);
      background:rgba(255,255,255,.035);
    }

    .nav-tabs{
      border-bottom:1px solid rgba(255,255,255,.06);
      margin-bottom:0;
      gap:8px;
    }

    .nav-tabs .nav-link{
      border:none;
      color:#cad9f2;
      font-weight:800;
      border-radius:18px 18px 0 0;
      background:transparent;
      margin-right:0;
      padding:.82rem 1.25rem;
    }

    .nav-tabs .nav-link.active{
      color:#fff;
      background:rgba(14,35,79,.98);
      border:1px solid rgba(161,198,255,.12);
      border-bottom:none;
      box-shadow:0 8px 20px rgba(0,0,0,.18);
    }

    .detail-kv{
      border:1px solid rgba(160,197,255,.10);
      background:rgba(255,255,255,.035);
      border-radius:18px;
      padding:14px 16px;
      height:100%;
    }

    .detail-kv .label{
      color:#aecdff;
      font-size:.85rem;
      font-weight:800;
      margin-bottom:6px;
      text-transform:uppercase;
      letter-spacing:.3px;
    }

    .detail-kv .value{
      color:#fff;
      font-weight:700;
      word-break:break-word;
      line-height:1.45;
    }

    .title-row{
      display:flex;
      flex-wrap:wrap;
      gap:14px;
      align-items:center;
      justify-content:space-between;
      margin-bottom:18px;
    }

    .title-left h3{
      margin:0;
      font-size:2rem;
      font-weight:800;
      color:#fff;
    }

    .title-left p{
      margin:4px 0 0;
      color:#c3d4ef;
      font-size:1rem;
    }

    .stock-box{
      border:1px solid rgba(160,197,255,.12);
      border-radius:18px;
      background:rgba(255,255,255,.04);
      padding:14px 16px;
      height:100%;
    }
    .stock-box .cap{
      color:#aecdff;
      font-size:.84rem;
      font-weight:800;
      text-transform:uppercase;
      letter-spacing:.3px;
      margin-bottom:8px;
    }
    .stock-box .qty{
      color:#fff;
      font-size:1.8rem;
      font-weight:800;
      line-height:1;
    }
    .stock-box .hint{
      color:#c7d8f4;
      font-size:.88rem;
      margin-top:8px;
    }


    /* Bandeja inteligente de transferencias · vista tipo correo */
    .mail-toolbar{
      display:flex;
      align-items:end;
      justify-content:space-between;
      gap:16px;
      flex-wrap:wrap;
      margin-bottom:16px;
    }
    .mail-filter{
      display:flex;
      align-items:end;
      gap:10px;
      flex-wrap:wrap;
    }
    .mail-filter .form-control{min-width:180px;}
    .mail-shell{
      display:grid;
      grid-template-columns:220px minmax(280px, 390px) minmax(340px, 1fr);
      min-height:600px;
      border:1px solid var(--mes-line);
      border-radius:22px;
      overflow:hidden;
      background:#fff;
    }
    .mail-folders{
      padding:14px 10px;
      background:#f7faff;
      border-right:1px solid var(--mes-line);
    }
    .folder-btn{
      width:100%;
      display:flex;
      align-items:center;
      gap:10px;
      padding:13px 12px;
      margin-bottom:6px;
      border:0;
      border-radius:14px;
      background:transparent;
      color:var(--mes-text);
      text-align:left;
      font-weight:700;
    }
    .folder-btn:hover{background:#edf4ff;}
    .folder-btn.active{
      background:#e8f1ff;
      color:#0f4ecf;
    }
    .folder-btn .folder-count{
      margin-left:auto;
      padding:3px 9px;
      border-radius:999px;
      background:#e7edf5;
      color:#42566e;
      font-size:.78rem;
      font-weight:800;
    }
    .folder-btn.active .folder-count{background:#165dff;color:#fff;}
    .mail-list-panel{
      border-right:1px solid var(--mes-line);
      background:#fff;
      min-width:0;
    }
    .mail-panel-header{
      padding:18px 16px 12px;
      border-bottom:1px solid var(--mes-line);
    }
    .mail-panel-title{
      font-size:1.12rem;
      font-weight:800;
      color:var(--mes-text);
    }
    .mail-list{
      max-height:650px;
      overflow:auto;
    }
    .mail-item{
      width:100%;
      display:block;
      text-align:left;
      background:#fff;
      border:0;
      border-bottom:1px solid #edf2f8;
      padding:15px 15px 14px;
      transition:background .15s ease;
      color:var(--mes-text);
    }
    .mail-item:hover{background:#f4f8ff;}
    .mail-item.active{
      background:#edf5ff;
      box-shadow:inset 3px 0 0 #165dff;
    }
    .mail-item-row{
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap:8px;
      margin-bottom:6px;
    }
    .mail-route{
      font-weight:800;
      color:var(--mes-text);
      overflow:hidden;
      white-space:nowrap;
      text-overflow:ellipsis;
    }
    .mail-time{
      flex:0 0 auto;
      font-size:.78rem;
      color:var(--mes-muted);
    }
    .mail-subject{
      font-size:.9rem;
      color:#42566e;
      overflow:hidden;
      white-space:nowrap;
      text-overflow:ellipsis;
    }
    .mail-preview{
      font-size:.82rem;
      color:#71859c;
      overflow:hidden;
      white-space:nowrap;
      text-overflow:ellipsis;
      margin-top:4px;
    }
    .mail-detail-pane{
      padding:20px;
      min-width:0;
      background:#fff;
    }
    .mail-empty{
      height:100%;
      min-height:300px;
      display:flex;
      align-items:center;
      justify-content:center;
      text-align:center;
      color:var(--mes-muted);
      padding:30px;
    }
    .mail-suggestion{
      border:1px solid #cfe0f6;
      background:#f1f7ff;
      color:#24405e;
      border-radius:16px;
      padding:12px 14px;
      font-size:.9rem;
      margin-top:14px;
    }
    .mail-detail-header{
      border-bottom:1px solid var(--mes-line);
      padding-bottom:16px;
      margin-bottom:16px;
    }
    .mail-detail-title{
      font-size:1.25rem;
      font-weight:850;
      color:var(--mes-text);
    }
    .mail-detail-grid{
      display:grid;
      grid-template-columns:repeat(2, minmax(0,1fr));
      gap:10px;
      margin:16px 0;
    }
    .mail-detail-field{
      background:#f7faff;
      border:1px solid #e3ecf6;
      border-radius:13px;
      padding:10px 12px;
    }
    .mail-detail-field span{
      display:block;
      font-size:.72rem;
      color:var(--mes-muted);
      font-weight:800;
      text-transform:uppercase;
      margin-bottom:4px;
    }
    .mail-detail-field b{
      color:var(--mes-text);
      font-size:.9rem;
      word-break:break-word;
    }
    .mail-items-mini{
      width:100%;
      margin-top:12px;
      border-collapse:collapse;
      font-size:.89rem;
    }
    .mail-items-mini th,.mail-items-mini td{
      padding:9px 8px;
      border-bottom:1px solid #e7eef7;
    }
    .mail-items-mini th{
      color:var(--mes-muted);
      font-size:.76rem;
      text-transform:uppercase;
    }
    @media (max-width: 1199.98px){
      .mail-shell{grid-template-columns:190px minmax(270px,1fr);}
      .mail-detail-pane{grid-column:1 / -1;border-top:1px solid var(--mes-line);}
    }
    @media (max-width: 767.98px){
      .mail-shell{display:block;}
      .mail-folders{display:flex;gap:6px;overflow:auto;border-right:0;border-bottom:1px solid var(--mes-line);}
      .folder-btn{min-width:max-content;margin:0;}
      .mail-list-panel{border-right:0;}
      .mail-detail-pane{padding:15px;}
      .mail-detail-grid{grid-template-columns:1fr;}
    }

    @media (max-width: 991.98px){
      .brand-title{font-size:1.5rem}
      .brand-sub{font-size:.95rem}
      .kpi-value{font-size:1.75rem}
      .page-wrap{padding:18px 12px 28px}
      .panel-pad{padding:16px}
      .title-left h3{font-size:1.45rem}
      .toolbar-btn,.btn-green-soft{width:100%}
    }


/* ============================================================
   TEMA CLARO MES · Similar a Almacén
   ------------------------------------------------------------
   Mantiene el banner azul y usa tarjetas/paneles claros para
   mejorar lectura en operación.
============================================================ */
:root{
  --mes-bg:#f4f7fb;
  --mes-panel:#ffffff;
  --mes-line:#dbe6f2;
  --mes-text:#10243e;
  --mes-muted:#5e7188;
  --mes-blue:#145798;
  --mes-accent:#1f6fff;
  --mes-green:#128b4f;
  --mes-gold:#b7791f;
  --mes-danger:#c63b50;
}
html{color-scheme:light!important;}
body{
  color:var(--mes-text)!important;
  background:
    radial-gradient(circle at 12% 20%, rgba(31,111,255,.06), transparent 22%),
    linear-gradient(180deg,#f9fbff 0%,#f4f7fb 100%)!important;
}
.topbar{
  background:linear-gradient(135deg,#0b2848,#145798)!important;
  border-bottom:1px solid rgba(255,255,255,.16)!important;
  color:#fff!important;
}
.topbar *, .topbar .brand-title, .topbar .brand-sub{color:#fff!important;}
.topbar .btn, .topbar a.btn{color:#fff!important;border-color:rgba(255,255,255,.45)!important;}
.topbar .btn:hover{background:rgba(255,255,255,.14)!important;}
.glass,.kpi-card,.modal-content,.item-row,.table-wrap,.stock-box,.detail-kv{
  background:#ffffff!important;
  background-color:#ffffff!important;
  color:var(--mes-text)!important;
  border-color:var(--mes-line)!important;
  box-shadow:0 14px 34px rgba(16,36,62,.08)!important;
}
.panel-pad,.modal-body,.modal-header,.modal-footer{background:#ffffff!important;color:var(--mes-text)!important;}
.brand-title:not(.topbar .brand-title),.title-left h3,.section-title,.fw-bold,.fw-semibold,.kpi-value,.modal-title,.table thead th,.mono,label,.form-label,.detail-kv .value,.stock-box .qty{
  color:var(--mes-text)!important;
}
.brand-sub:not(.topbar .brand-sub),.muted,.small,.small-note,.kpi-label,.kpi-hint,.title-left p,.detail-kv .label,.stock-box .cap,.stock-box .hint{
  color:var(--mes-muted)!important;
}
.kpi-card{border-radius:22px;min-height:130px;}
.kpi-icon{background:#eaf2ff!important;color:#145798!important;box-shadow:none!important;}
.kpi-value{font-weight:900!important;}
.badge-soft,.small-note{
  background:#fff7e6!important;
  border:1px solid #f3d39b!important;
  color:#7c4a03!important;
}
.table{color:var(--mes-text)!important;--bs-table-bg:#fff!important;--bs-table-striped-bg:#f8fbff!important;--bs-table-hover-bg:#eef6ff!important;--bs-table-border-color:#e3ecf6!important;}
.table thead th{background:#eef4fb!important;color:var(--mes-text)!important;border-bottom:1px solid var(--mes-line)!important;}
.table tbody td{color:var(--mes-text)!important;background:#fff!important;border-color:#e7eef7!important;}
.table tbody tr:hover td{background:#f4f9ff!important;}
.form-control,.form-select,textarea,input[type="text"],input[type="number"],input[type="datetime-local"]{
  background:#fff!important;color:var(--mes-text)!important;border:1px solid #ccd9e8!important;border-radius:14px!important;box-shadow:none!important;
}
.form-control:focus,.form-select:focus,textarea:focus,input:focus{
  background:#fff!important;color:var(--mes-text)!important;border-color:rgba(31,111,255,.55)!important;box-shadow:0 0 0 .22rem rgba(31,111,255,.12)!important;
}
.form-control::placeholder{color:#8aa0b8!important;}
.form-select option{background:#fff!important;color:var(--mes-text)!important;}
.btn{border-radius:14px!important;font-weight:800!important;box-shadow:0 8px 18px rgba(16,36,62,.08)!important;}
.toolbar-btn{background:#fff!important;color:var(--mes-text)!important;border:1.5px solid #cdd9e7!important;}
.toolbar-btn:hover{background:#eef4fb!important;color:var(--mes-text)!important;}
.btn-primary-soft,.btn-green-soft,.btn-danger-soft{color:#fff!important;}
.btn-primary-soft{background:#165dff!important;}
.btn-green-soft{background:#128b4f!important;}
.btn-danger-soft{background:#c63b50!important;}
.btn-action{background:#fff!important;color:var(--mes-text)!important;border:1.5px solid #cdd9e7!important;}
.btn-action.detail{color:#0f4ecf!important;border-color:#9fc0ff!important;}
.btn-action.receive{color:#0f743f!important;border-color:#9bd8b8!important;background:#f2fff7!important;}
.btn-action.cancel{color:#b42335!important;border-color:#f0b5c0!important;background:#fff5f7!important;}
.status-draft{background:#eef2f7!important;color:#40546d!important;border-color:#d6e1ec!important;}
.status-sent{background:#fff7e6!important;color:#8a5600!important;border-color:#f3d39b!important;}
.status-received{background:#ecfdf3!important;color:#0f743f!important;border-color:#a6e7c0!important;}
.status-cancelled{background:#fff1f3!important;color:#b42335!important;border-color:#f0b5c0!important;}
.nav-tabs{border-bottom:1px solid var(--mes-line)!important;}
.nav-tabs .nav-link{color:var(--mes-muted)!important;background:#f8fbff!important;border:1px solid var(--mes-line)!important;border-radius:16px 16px 0 0!important;}
.nav-tabs .nav-link.active{color:var(--mes-text)!important;background:#fff!important;border-bottom-color:#fff!important;}
.modal-header{border-bottom:1px solid var(--mes-line)!important;}
.modal-footer{border-top:1px solid var(--mes-line)!important;}
.btn-close-white{filter:none!important;}
*::-webkit-scrollbar{width:10px;height:10px;}
*::-webkit-scrollbar-thumb{background:#c4d3e4;border-radius:999px;}
*::-webkit-scrollbar-track{background:#eef3f8;}

  
/* Encabezado operativo: botones con contraste profesional */
.topbar .toolbar-btn,
.topbar a.toolbar-btn{
  background:#1765d1!important;
  border:1px solid #1765d1!important;
  color:#ffffff!important;
  box-shadow:0 8px 18px rgba(0,38,105,.22)!important;
}
.topbar .toolbar-btn i,
.topbar a.toolbar-btn i{
  color:#ffffff!important;
}
.topbar .toolbar-btn:hover,
.topbar a.toolbar-btn:hover{
  background:#0f4ea8!important;
  border-color:#0f4ea8!important;
  color:#ffffff!important;
}
.topbar .btn-green-soft{
  background:#128b4f!important;
  border-color:#128b4f!important;
  color:#ffffff!important;
}
.topbar .btn-green-soft i{ color:#ffffff!important; }

  </style>
</head>
<body>

<nav class="topbar">
  <div class="container-fluid py-3 px-3 px-md-4">
    <div class="d-flex flex-wrap align-items-center gap-3">
      <div>
        <h1 class="brand-title">BReport · Transferencias</h1>
        <div class="brand-sub"><?=h($companyName)?> · <?=h($locationName)?> · Solo localidades de esta empresa · <?=h($scopeWarehouseNote)?></div>
      </div>

      <div class="ms-auto d-flex flex-wrap gap-2">
        <a href="dashboard.php" class="btn toolbar-btn">
          <i class="bi bi-speedometer2 me-1"></i> Dashboard
        </a>
        <button id="btnTransferir" class="btn btn-green-soft" <?=(!$canWrite?'disabled':'')?>>
          <i class="bi bi-truck me-1"></i> Transferir
        </button>
        <button id="btnReload" class="btn toolbar-btn">
          <i class="bi bi-arrow-repeat me-1"></i> Actualizar
        </button>
        <a href="logout.php" class="btn toolbar-btn">
          <i class="bi bi-box-arrow-right me-1"></i> Salir
        </a>
      </div>
    </div>
  </div>
</nav>

<div class="page-wrap">
  <div class="shell">

    <div class="row g-3 mb-3">
      <div class="col-12 col-md-4">
        <div class="kpi-card panel-pad">
          <div class="kpi-top">
            <div class="kpi-icon"><i class="bi bi-box-seam"></i></div>
            <div class="kpi-label">Productos con inventario</div>
          </div>
          <div class="kpi-value mono" id="kProducts">0</div>
          <div class="kpi-hint">Artículos activos de la planta actual</div>
        </div>
      </div>

      <div class="col-12 col-md-4">
        <div class="kpi-card panel-pad">
          <div class="kpi-top">
            <div class="kpi-icon"><i class="bi bi-truck-flatbed"></i></div>
            <div class="kpi-label">En tránsito saliente</div>
          </div>
          <div class="kpi-value mono" id="kTransitOut">0.000</div>
          <div class="kpi-hint">Transferencias enviadas y aún no recibidas</div>
        </div>
      </div>

      <div class="col-12 col-md-4">
        <div class="kpi-card panel-pad">
          <div class="kpi-top">
            <div class="kpi-icon"><i class="bi bi-box-arrow-in-down"></i></div>
            <div class="kpi-label">Pendiente por recibir</div>
          </div>
          <div class="kpi-value mono" id="kPendingIn">0.000</div>
          <div class="kpi-hint">Inventario que viene en camino a esta planta</div>
        </div>
      </div>
    </div>

    <div class="glass panel-pad mb-3">
      <div class="title-row">
        <div class="title-left">
          <h3>Inventario actual</h3>
          <p>Totales por producto: total, tránsito, por recibir y disponible</p>
        </div>
        <div class="badge-soft">
          <i class="bi bi-info-circle"></i>
          Inventario transferible según almacenes de la planta activa
        </div>
      </div>

      <div class="table-wrap">
        <div class="table-responsive">
          <table class="table table-hover align-middle">
            <thead>
              <tr>
                <th>SKU</th>
                <th>Producto</th>
                <th class="text-end">Total</th>
                <th class="text-end">En tránsito</th>
                <th class="text-end">Por recibir</th>
                <th class="text-end">Disponible</th>
                <th>UOM</th>
              </tr>
            </thead>
            <tbody id="tbInventory"></tbody>
          </table>
        </div>
      </div>
    </div>

    <div class="glass panel-pad">
      <div class="mail-toolbar">
        <div class="title-left">
          <h3>Bandeja de transferencias</h3>
          <p>Pendientes por recibir y transferencias recibidas, organizadas como bandeja operativa.</p>
        </div>
        <div class="mail-filter">
          <div>
            <label class="form-label mb-1">Filtrar por día</label>
            <input type="date" id="mailDateFilter" class="form-control">
          </div>
          <button type="button" class="btn toolbar-btn" id="btnMailToday">
            <i class="bi bi-calendar-event me-1"></i> Hoy
          </button>
          <button type="button" class="btn toolbar-btn" id="btnMailAll">
            <i class="bi bi-calendar3 me-1"></i> Todos
          </button>
        </div>
      </div>

      <div class="mail-shell">
        <aside class="mail-folders">
          <button type="button" class="folder-btn active" data-folder="pending" id="folderPending">
            <i class="bi bi-inbox"></i> Pendientes
            <span class="folder-count" id="countPending">0</span>
          </button>
          <button type="button" class="folder-btn" data-folder="received" id="folderReceived">
            <i class="bi bi-check2-circle"></i> Recibidas
            <span class="folder-count" id="countReceived">0</span>
          </button>
          <button type="button" class="folder-btn" data-folder="sent" id="folderSent">
            <i class="bi bi-send"></i> Enviadas
            <span class="folder-count" id="countSent">0</span>
          </button>

          <div class="mail-suggestion" id="mailSuggestion">
            Selecciona una bandeja para revisar el flujo operativo.
          </div>
        </aside>

        <section class="mail-list-panel">
          <div class="mail-panel-header">
            <div class="mail-panel-title" id="mailboxTitle">Pendientes por recibir</div>
            <div class="small muted" id="mailboxMeta">Sin transferencias</div>
          </div>
          <div class="mail-list" id="mailList"></div>
        </section>

        <section class="mail-detail-pane" id="mailDetailPane">
          <div class="mail-empty" id="mailDetailEmpty">
            <div>
              <i class="bi bi-envelope-open fs-1 d-block mb-2"></i>
              Selecciona una transferencia para consultar el detalle.
            </div>
          </div>
          <div id="mailDetailContent" class="d-none"></div>
        </section>
      </div>
    </div>

  </div>
</div>

<!-- Modal nueva transferencia -->
<div class="modal fade" id="transferModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-xl modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title"><i class="bi bi-truck me-2"></i>Nueva transferencia</h5>
        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
      </div>

      <div class="modal-body">
        <div class="row g-3 mb-3">
          <div class="col-12 col-md-4">
            <label class="form-label">Destino · misma empresa</label>
            <select id="toLoc" class="form-select"></select>
          </div>
          <div class="col-12 col-md-4">
            <label class="form-label">Chofer / conductor</label>
            <input id="driver" class="form-control" placeholder="Nombre del conductor">
          </div>
          <div class="col-12 col-md-4">
            <label class="form-label">Unidad / transporte</label>
            <input id="truck" class="form-control" placeholder="Unidad, placas, transporte">
          </div>
          <div class="col-12 col-md-4">
            <label class="form-label">Fecha / hora salida</label>
            <input id="departAt" type="datetime-local" class="form-control">
          </div>
          <div class="col-12 col-md-8">
            <label class="form-label">Notas</label>
            <input id="notes" class="form-control" placeholder="Observaciones de la transferencia">
          </div>
        </div>

        <div class="row g-3 mb-3">
          <div class="col-12 col-md-6">
            <div class="stock-box">
              <div class="cap">Disponible en origen</div>
              <div class="qty mono" id="originAvail">0.000</div>
              <div class="hint">Cantidad disponible para transferir del producto seleccionado.</div>
            </div>
          </div>
          <div class="col-12 col-md-6">
            <div class="stock-box">
              <div class="cap">Existencia actual en destino</div>
              <div class="qty mono" id="destAvail">0.000</div>
              <div class="hint">Stock actual del producto seleccionado en la ubicación destino.</div>
            </div>
          </div>
        </div>

        <div class="item-row mb-3">
          <div class="row g-3">
            <div class="col-12 col-md-4">
              <label class="form-label">Producto origen</label>
              <select id="prodSel" class="form-select"></select>
            </div>
            <div class="col-12 col-md-2">
              <label class="form-label">Cantidad</label>
              <input id="qty" class="form-control mono" value="0">
            </div>
            <div class="col-12 col-md-3">
              <label class="form-label">Conversión destino</label>
              <select id="convSel" class="form-select">
                <option value="0">Sin conversión</option>
              </select>
            </div>
            <div class="col-12 col-md-2">
              <label class="form-label">Factor</label>
              <input id="convFactor" class="form-control mono" placeholder="Ej. 4">
            </div>
            <div class="col-12 col-md-1 d-flex align-items-end">
              <button id="btnAddItem" class="btn btn-primary-soft w-100" type="button">
                <i class="bi bi-plus-lg"></i>
              </button>
            </div>
          </div>
        </div>

        <div class="table-wrap">
          <div class="table-responsive">
            <table class="table table-hover align-middle">
              <thead>
                <tr>
                  <th>SKU</th>
                  <th>Producto</th>
                  <th class="text-end">Cantidad</th>
                  <th>Conversión</th>
                  <th>UOM</th>
                </tr>
              </thead>
              <tbody id="tbItems"></tbody>
            </table>
          </div>
        </div>

        <div class="small-note mt-3">
          Agrega todos los artículos antes de enviar. La transferencia quedará en tránsito hasta que la planta destino confirme la recepción.
        </div>
      </div>

      <div class="modal-footer">
        <button id="btnCreateAndSend" class="btn btn-green-soft" type="button">
          <i class="bi bi-send-check me-1"></i> Guardar y enviar
        </button>
      </div>
    </div>
  </div>
</div>

<!-- Modal recibir -->
<div class="modal fade" id="receiveModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title"><i class="bi bi-box-arrow-in-down me-2"></i>Recibir transferencia</h5>
        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
      </div>

      <div class="modal-body">
        <input type="hidden" id="receiveTransferId">
        <label class="form-label">Fecha / hora llegada</label>
        <input id="arriveAt" type="datetime-local" class="form-control">
      </div>

      <div class="modal-footer">
        <button id="btnConfirmReceive" class="btn btn-green-soft" type="button">
          <i class="bi bi-check2-circle me-1"></i> Confirmar recibido
        </button>
      </div>
    </div>
  </div>
</div>

<!-- Modal detalle -->
<div class="modal fade" id="detailModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-xl modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title"><i class="bi bi-eye me-2"></i>Detalle de transferencia</h5>
        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
      </div>

      <div class="modal-body">
        <div class="row g-3 mb-3" id="detailBoxes"></div>

        <div class="table-wrap">
          <div class="table-responsive">
            <table class="table table-hover align-middle">
              <thead>
                <tr>
                  <th>SKU</th>
                  <th>Producto</th>
                  <th class="text-end">Cantidad</th>
                  <th>Conversión</th>
                  <th>UOM</th>
                </tr>
              </thead>
              <tbody id="detailItems"></tbody>
            </table>
          </div>
        </div>
      </div>

      <div class="modal-footer" id="detailFooterActions"></div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
const CSRF = <?=json_encode($csrf)?>;
const CAN_WRITE = <?=json_encode($canWrite)?>;
const $ = id => document.getElementById(id);

function h(s){return String(s ?? '').replace(/[&<>"]/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[m]));}
function num(v){ const n=parseFloat(v); return isNaN(n)?0:n; }
function toLocal(dt){
  if(!dt) return '';
  dt = String(dt).replace('T',' ');
  return dt.length >= 16 ? dt.slice(0,16) : dt;
}
function statusClass(st){
  st = String(st || '').toUpperCase();
  if(st === 'DRAFT') return 'status-draft';
  if(st === 'SENT') return 'status-sent';
  if(st === 'RECEIVED') return 'status-received';
  if(st === 'CANCELLED') return 'status-cancelled';
  return 'status-draft';
}
function isBarSku(sku){
  return String(sku || '').trim().toUpperCase() === 'BARRA 150 KILOS';
}
function fmtQtyBySku(sku, val){
  return isBarSku(sku) ? String(Math.round(num(val))) : num(val).toFixed(3);
}

async function apiGet(action, params={}){
  const u = new URL(location.href);
  u.search = '';
  u.searchParams.set('action', action);
  Object.entries(params).forEach(([k,v])=>u.searchParams.set(k,v));
  const r = await fetch(u.toString(), {
    credentials:'same-origin',
    headers:{ 'X-CSRF': CSRF }
  });
  const t = await r.text();
  try { return JSON.parse(t); } catch(e){ console.error(t); return {ok:false,error:'NO_JSON'}; }
}

async function apiPost(action, payload={}){
  const u = new URL(location.href);
  u.search = '';
  u.searchParams.set('action', action);
  const r = await fetch(u.toString(), {
    method:'POST',
    credentials:'same-origin',
    headers:{
      'Content-Type':'application/json',
      'X-CSRF':CSRF
    },
    body: JSON.stringify(payload)
  });
  const t = await r.text();
  try { return JSON.parse(t); } catch(e){ console.error(t); return {ok:false,error:'NO_JSON'}; }
}

let mode = 'in';
let mailboxFolder = 'pending';
let selectedMailboxId = 0;
let mailboxRows = [];
let products = [];
let destinations = [];
let conversions = [];
let workingTransferId = 0;

const transferModal = new bootstrap.Modal(document.getElementById('transferModal'));
const receiveModal  = new bootstrap.Modal(document.getElementById('receiveModal'));
const detailModal   = new bootstrap.Modal(document.getElementById('detailModal'));

async function loadCatalogs(){
  const p = await apiGet('products');
  products = p.ok ? (p.rows || []) : [];
  $('prodSel').innerHTML = products.map(x =>
    `<option value="${x.id}" data-sku="${h(x.sku)}">${h(x.sku)} · ${h(x.name)}</option>`
  ).join('');

  const d = await apiGet('destinations');
  destinations = d.ok ? (d.rows || []) : [];
  $('toLoc').innerHTML = `<option value="0">Selecciona destino</option>` +
    destinations.map(x => `<option value="${x.id}">${h(x.name)} (${h(x.code || '')})</option>`).join('');

  const c = await apiGet('product_conversions');
  conversions = c.ok ? (c.rows || []) : [];
  refreshConversionOptions();
  await refreshStockIndicators();
}

function refreshConversionOptions(){
  const productId = parseInt($('prodSel').value || '0', 10);
  const rows = conversions.filter(x => parseInt(x.from_product_id, 10) === productId);
  $('convSel').innerHTML = `<option value="0">Sin conversión</option>` + rows.map(x =>
    `<option value="${x.to_product_id}" data-factor="${x.factor}">
      ${h(x.to_sku)} · ${h(x.to_name)}
    </option>`
  ).join('');
  $('convFactor').value = '';
}

async function refreshStockIndicators(){
  const productId = parseInt($('prodSel').value || '0', 10);
  const toLocationId = parseInt($('toLoc').value || '0', 10);

  if(productId <= 0){
    $('originAvail').textContent = '0.000';
    $('destAvail').textContent = '0.000';
    return;
  }

  const selected = products.find(p => parseInt(p.id,10) === productId);
  const sku = selected?.sku || '';

  const j = await apiGet('product_stock_info', {
    product_id: productId,
    to_location_id: toLocationId
  });

  if(!j.ok){
    $('originAvail').textContent = '0.000';
    $('destAvail').textContent = '0.000';
    return;
  }

  $('originAvail').textContent = fmtQtyBySku(sku, j.origin_available);
  $('destAvail').textContent = fmtQtyBySku(sku, j.destination_available);
}

async function loadInventory(){
  const j = await apiGet('inventory_summary');
  if(!j.ok){
    alert(j.error || 'Error al cargar inventario');
    return;
  }

  const rows = j.rows || [];
  $('kProducts').textContent = String(rows.filter(r => num(r.qty_available) > 0).length);
  $('kTransitOut').textContent = rows.reduce((a,b)=>a + num(b.qty_in_transit_out), 0).toFixed(3);
  $('kPendingIn').textContent = rows.reduce((a,b)=>a + num(b.qty_pending_in), 0).toFixed(3);

  $('tbInventory').innerHTML = rows.map(r => `
    <tr>
      <td class="mono">${h(r.sku)}</td>
      <td>${h(r.name)}</td>
      <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty_total)}</td>
      <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty_in_transit_out)}</td>
      <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty_pending_in)}</td>
      <td class="text-end mono strong-num">${fmtQtyBySku(r.sku, r.qty_available)}</td>
      <td class="mono">${h(r.uom || '')}</td>
    </tr>
  `).join('');
}

function mailboxTitle(folder){
  if(folder === 'received') return 'Transferencias recibidas';
  if(folder === 'sent') return 'Transferencias enviadas';
  return 'Pendientes por recibir';
}

function mailboxDateValue(){
  return $('mailDateFilter') ? $('mailDateFilter').value.trim() : '';
}

function mailStatusLabel(status){
  const st = String(status || '').toUpperCase();
  if(st === 'SENT') return 'En tránsito';
  if(st === 'RECEIVED') return 'Recibida';
  if(st === 'DRAFT') return 'Borrador';
  if(st === 'CANCELLED') return 'Cancelada';
  return st || 'Sin estatus';
}

function renderMailSuggestion(rows, counts){
  const pending = Number(counts.pending || 0);
  let message = '';
  if(mailboxFolder === 'pending'){
    if(!rows.length){
      message = 'Sin transferencias pendientes en el periodo seleccionado.';
    }else{
      const now = Date.now();
      const old = rows.filter(r => {
        const raw = r.depart_at || r.created_at || '';
        const ts = Date.parse(String(raw).replace(' ', 'T'));
        return !Number.isNaN(ts) && ((now - ts) / 3600000) >= 24;
      }).length;
      message = old > 0
        ? `Sugerencia: ${old} transferencia(s) llevan más de 24 horas pendientes. Prioriza confirmar su recepción.`
        : `Sugerencia: hay ${pending} transferencia(s) por recibir; valida unidad y mercancía antes de confirmar.`;
    }
  }else if(mailboxFolder === 'received'){
    message = rows.length
      ? 'Sugerencia: usa el filtro por día para conciliar entradas recibidas contra inventario.'
      : 'No hay transferencias recibidas en el periodo seleccionado.';
  }else{
    message = 'Sugerencia: revisa que cada envío en tránsito sea recibido por la planta destino.';
  }
  $('mailSuggestion').textContent = message;
}

function renderMailList(rows){
  if(!rows.length){
    $('mailList').innerHTML = `
      <div class="mail-empty">
        <div><i class="bi bi-inbox fs-2 d-block mb-2"></i>No hay transferencias en esta bandeja.</div>
      </div>`;
    clearMailDetail();
    return;
  }

  $('mailList').innerHTML = rows.map(r => {
    const active = Number(r.id) === Number(selectedMailboxId) ? 'active' : '';
    const route = mailboxFolder === 'sent'
      ? `${r.to_company_name || ''} · ${r.to_location_name || ''}`
      : `${r.from_company_name || ''} · ${r.from_location_name || ''}`;
    const stamp = mailboxFolder === 'received' ? (r.arrive_at || r.received_at) : (r.depart_at || r.created_at);
    return `
      <button type="button" class="mail-item ${active}" onclick="selectMailboxTransfer(${Number(r.id)})">
        <div class="mail-item-row">
          <div class="mail-route">#${h(r.id)} · ${h(route)}</div>
          <div class="mail-time">${h(toLocal(stamp))}</div>
        </div>
        <div class="mail-subject">
          <span class="status-chip ${statusClass(r.status)}">${h(mailStatusLabel(r.status))}</span>
          <span class="ms-2">${h(r.items_count || 0)} artículo(s)</span>
        </div>
        <div class="mail-preview">${h(r.item_summary || r.notes || 'Sin detalle de artículos')}</div>
      </button>`;
  }).join('');
}

function clearMailDetail(){
  selectedMailboxId = 0;
  $('mailDetailContent').classList.add('d-none');
  $('mailDetailEmpty').classList.remove('d-none');
}

async function selectMailboxTransfer(id){
  selectedMailboxId = Number(id || 0);
  renderMailList(mailboxRows);

  const j = await apiGet('detail', {transfer_id: selectedMailboxId});
  if(!j.ok){
    alert(j.error || 'No se pudo abrir el detalle');
    return;
  }

  const t = j.transfer || {};
  const items = j.items || [];
  const canReceive = mailboxFolder === 'pending' && String(t.status || '') === 'SENT';
  const canCancel = mailboxFolder === 'sent' && ['DRAFT','SENT'].includes(String(t.status || ''));

  $('mailDetailEmpty').classList.add('d-none');
  $('mailDetailContent').classList.remove('d-none');
  $('mailDetailContent').innerHTML = `
    <div class="mail-detail-header">
      <div class="d-flex justify-content-between align-items-start gap-2 flex-wrap">
        <div>
          <div class="mail-detail-title">Transferencia #${h(t.id || '')}</div>
          <div class="small muted mt-1">${h(t.from_location_name || '')} → ${h(t.to_location_name || '')}</div>
        </div>
        <span class="status-chip ${statusClass(t.status)}">${h(mailStatusLabel(t.status))}</span>
      </div>
    </div>
    <div class="mail-detail-grid">
      <div class="mail-detail-field"><span>Origen</span><b>${h(t.from_location_name || '—')}</b></div>
      <div class="mail-detail-field"><span>Destino</span><b>${h(t.to_location_name || '—')}</b></div>
      <div class="mail-detail-field"><span>Chofer</span><b>${h(t.driver_name || '—')}</b></div>
      <div class="mail-detail-field"><span>Unidad</span><b>${h(t.truck_no || '—')}</b></div>
      <div class="mail-detail-field"><span>Salida</span><b>${h(toLocal(t.depart_at) || '—')}</b></div>
      <div class="mail-detail-field"><span>Llegada</span><b>${h(toLocal(t.arrive_at) || '—')}</b></div>
    </div>
    <div class="fw-bold mb-2">Artículos transferidos</div>
    <div class="table-responsive">
      <table class="mail-items-mini">
        <thead><tr><th>SKU</th><th>Producto</th><th class="text-end">Cantidad</th><th>UOM</th></tr></thead>
        <tbody>
          ${items.map(r => `
            <tr>
              <td class="mono">${h(r.sku)}</td>
              <td>${h(r.product_name)}</td>
              <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty)}</td>
              <td>${h(r.uom || '')}</td>
            </tr>`).join('') || '<tr><td colspan="4" class="muted">Sin artículos registrados</td></tr>'}
        </tbody>
      </table>
    </div>
    <div class="small muted mt-3">${h(t.notes || 'Sin notas')}</div>
    <div class="d-flex flex-wrap gap-2 mt-3">
      ${canReceive ? `<button type="button" class="btn btn-green-soft" onclick="openReceive(${Number(t.id)})"><i class="bi bi-check2-circle me-1"></i> Recibir</button>` : ''}
      ${canCancel ? `<button type="button" class="btn btn-danger-soft" onclick="cancelTransfer(${Number(t.id)})"><i class="bi bi-x-octagon me-1"></i> Cancelar</button>` : ''}
      <button type="button" class="btn toolbar-btn" onclick="openDetail(${Number(t.id)})"><i class="bi bi-arrows-angle-expand me-1"></i> Ver ampliado</button>
    </div>`;
}

async function loadTransfers(){
  const params = {folder: mailboxFolder};
  const selectedDate = mailboxDateValue();
  if(selectedDate !== '') params.date = selectedDate;

  const j = await apiGet('mailbox', params);
  if(!j.ok){
    alert(j.error || 'Error al cargar transferencias');
    return;
  }

  mailboxRows = j.rows || [];
  const counts = j.counts || {};

  $('countPending').textContent = String(counts.pending || 0);
  $('countReceived').textContent = String(counts.received || 0);
  $('countSent').textContent = String(counts.sent || 0);
  $('mailboxTitle').textContent = mailboxTitle(mailboxFolder);
  $('mailboxMeta').textContent = `${mailboxRows.length} transferencia(s)` + (selectedDate !== '' ? ` · ${selectedDate}` : ' · todos los días');

  document.querySelectorAll('.folder-btn').forEach(btn => {
    btn.classList.toggle('active', btn.dataset.folder === mailboxFolder);
  });

  renderMailSuggestion(mailboxRows, counts);

  if(!mailboxRows.some(r => Number(r.id) === Number(selectedMailboxId))){
    selectedMailboxId = mailboxRows.length ? Number(mailboxRows[0].id) : 0;
  }
  renderMailList(mailboxRows);
  if(selectedMailboxId > 0){
    await selectMailboxTransfer(selectedMailboxId);
  }else{
    clearMailDetail();
  }
}


async function createTransfer(){
  const toLoc = parseInt($('toLoc').value || '0', 10);
  if(toLoc <= 0){
    alert('Selecciona un destino');
    return false;
  }

  const j = await apiPost('create', {
    to_location_id: toLoc,
    notes: $('notes').value.trim()
  });

  if(!j.ok){
    alert(j.error || 'Error al crear transferencia');
    return false;
  }

  workingTransferId = parseInt(j.id, 10);
  return true;
}

async function addItem(){
  if(!workingTransferId){
    const ok = await createTransfer();
    if(!ok) return;
  }

  const productId = parseInt($('prodSel').value || '0', 10);
  const qty = parseFloat(String($('qty').value || '0').replace(',', '.')) || 0;
  const convTo = parseInt($('convSel').value || '0', 10);
  let convFactor = $('convFactor').value.trim();

  if(productId <= 0 || qty <= 0){
    alert('Captura producto y cantidad mayor a 0');
    return;
  }

  const info = await apiGet('product_stock_info', {
    product_id: productId,
    to_location_id: parseInt($('toLoc').value || '0', 10)
  });

  if(!info.ok){
    alert(info.error || 'No se pudo validar el disponible');
    return;
  }

  if(qty > num(info.origin_available)){
    alert('La cantidad excede el disponible del origen');
    return;
  }

  if(convTo > 0 && convFactor === ''){
    const opt = $('convSel').selectedOptions[0];
    convFactor = opt?.dataset?.factor || '';
  }

  const j = await apiPost('item_save', {
    transfer_id: workingTransferId,
    product_id: productId,
    qty: qty,
    conversion_to_product_id: convTo > 0 ? convTo : null,
    conversion_factor: convTo > 0 ? convFactor : null
  });

  if(!j.ok){
    alert(j.error || 'Error al guardar artículo');
    return;
  }

  $('qty').value = '0';
  $('convSel').value = '0';
  $('convFactor').value = '';
  await loadItems();
  await loadInventory();
  await refreshStockIndicators();
}

async function loadItems(){
  if(!workingTransferId){
    $('tbItems').innerHTML = '';
    return;
  }

  const j = await apiGet('items', {transfer_id: workingTransferId});
  if(!j.ok){
    alert(j.error || 'Error al cargar artículos');
    return;
  }

  $('tbItems').innerHTML = (j.rows || []).map(r => `
    <tr>
      <td class="mono">${h(r.sku)}</td>
      <td>${h(r.product_name)}</td>
      <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty)}</td>
      <td>
        ${r.conversion_to_product_id
          ? `${h(r.conv_sku || '')} · factor ${num(r.conversion_factor).toFixed(3)}`
          : '<span class="muted">Sin conversión</span>'}
      </td>
      <td class="mono">${h(r.uom || '')}</td>
    </tr>
  `).join('');
}

async function createAndSend(){
  if(!workingTransferId){
    const ok = await createTransfer();
    if(!ok) return;
  }

  const j = await apiPost('send', {
    transfer_id: workingTransferId,
    driver_name: $('driver').value.trim(),
    truck_no: $('truck').value.trim(),
    depart_at: $('departAt').value.trim()
  });

  if(!j.ok){
    alert(j.error || 'Error al enviar transferencia');
    return;
  }

  transferModal.hide();
  resetTransferModal();
  await loadInventory();
  await loadTransfers();
}

function resetTransferModal(){
  workingTransferId = 0;
  $('toLoc').value = '0';
  $('driver').value = '';
  $('truck').value = '';
  $('departAt').value = '';
  $('notes').value = '';
  $('qty').value = '0';
  $('convSel').value = '0';
  $('convFactor').value = '';
  $('tbItems').innerHTML = '';
  $('originAvail').textContent = '0.000';
  $('destAvail').textContent = '0.000';
}

function openReceive(id){
  $('receiveTransferId').value = String(id);
  $('arriveAt').value = '';
  receiveModal.show();
}

async function confirmReceive(){
  const transferId = parseInt($('receiveTransferId').value || '0', 10);
  const arriveAt = $('arriveAt').value.trim();

  if(transferId <= 0 || !arriveAt){
    alert('Captura fecha y hora de llegada');
    return;
  }

  const j = await apiPost('receive', {
    transfer_id: transferId,
    arrive_at: arriveAt
  });

  if(!j.ok){
    alert(j.error || 'Error al recibir');
    return;
  }

  receiveModal.hide();
  await loadInventory();
  await loadTransfers();
}

async function cancelTransfer(id){
  if(!confirm('¿Deseas cancelar esta transferencia?')) return;

  const j = await apiPost('cancel', {transfer_id: id});
  if(!j.ok){
    alert(j.error || 'No se pudo cancelar');
    return;
  }

  await loadInventory();
  await loadTransfers();
}

async function openDetail(id){
  const j = await apiGet('detail', {transfer_id: id});
  if(!j.ok){
    alert(j.error || 'No se pudo abrir el detalle');
    return;
  }

  const t = j.transfer || {};
  const items = j.items || [];

  const boxes = [
    ['Transferencia #', t.id || ''],
    ['Estatus', `<span class="status-chip ${statusClass(t.status)}">${h(t.status || '')}</span>`],
    ['Origen', `${h(t.from_location_name || '')} ${t.from_location_code ? '(' + h(t.from_location_code) + ')' : ''}`],
    ['Destino', `${h(t.to_location_name || '')} ${t.to_location_code ? '(' + h(t.to_location_code) + ')' : ''}`],
    ['Chofer', h(t.driver_name || '') || '—'],
    ['Unidad / Transporte', h(t.truck_no || '') || '—'],
    ['Fecha salida', h(toLocal(t.depart_at) || '—')],
    ['Fecha llegada', h(toLocal(t.arrive_at) || '—')],
    ['Fecha creación', h(toLocal(t.created_at) || '—')],
    ['Notas', h(t.notes || '—')]
  ];

  $('detailBoxes').innerHTML = boxes.map(([label, value]) => `
    <div class="col-12 col-md-6 col-lg-4">
      <div class="detail-kv">
        <div class="label">${label}</div>
        <div class="value">${value}</div>
      </div>
    </div>
  `).join('');

  $('detailItems').innerHTML = items.map(r => `
    <tr>
      <td class="mono">${h(r.sku)}</td>
      <td>${h(r.product_name)}</td>
      <td class="text-end mono">${fmtQtyBySku(r.sku, r.qty)}</td>
      <td>
        ${r.conversion_to_product_id
          ? `${h(r.conv_sku || '')} · factor ${num(r.conversion_factor).toFixed(3)}`
          : '<span class="muted">Sin conversión</span>'}
      </td>
      <td class="mono">${h(r.uom || '')}</td>
    </tr>
  `).join('');

  const canCancel = mode === 'out' && ['DRAFT','SENT'].includes(String(t.status || ''));
  const canReceive = mode === 'in' && String(t.status || '') === 'SENT';

  $('detailFooterActions').innerHTML = `
    ${canCancel ? `
      <button class="btn btn-danger-soft" type="button" onclick="detailModal.hide(); cancelTransfer(${Number(t.id)})">
        <i class="bi bi-x-octagon me-1"></i> Cancelar transferencia
      </button>
    ` : ''}
    ${canReceive ? `
      <button class="btn btn-green-soft" type="button" onclick="detailModal.hide(); openReceive(${Number(t.id)})">
        <i class="bi bi-check2-circle me-1"></i> Recibir transferencia
      </button>
    ` : ''}
  `;

  detailModal.show();
}

function setMailboxFolder(folder){
  mailboxFolder = ['pending','received','sent'].includes(folder) ? folder : 'pending';
  mode = mailboxFolder === 'sent' ? 'out' : 'in';
  selectedMailboxId = 0;
  loadTransfers();
}

document.addEventListener('DOMContentLoaded', async () => {
  await loadCatalogs();
  await loadInventory();
  await loadTransfers();

  $('btnReload').addEventListener('click', async () => {
    await loadInventory();
    await loadTransfers();
  });

  $('btnTransferir').addEventListener('click', async () => {
    resetTransferModal();
    transferModal.show();
    await refreshStockIndicators();
  });

  $('prodSel').addEventListener('change', async () => {
    refreshConversionOptions();
    await refreshStockIndicators();
  });

  $('toLoc').addEventListener('change', refreshStockIndicators);

  $('convSel').addEventListener('change', () => {
    const opt = $('convSel').selectedOptions[0];
    $('convFactor').value = opt?.dataset?.factor || '';
  });

  $('btnAddItem').addEventListener('click', addItem);
  $('btnCreateAndSend').addEventListener('click', createAndSend);
  $('btnConfirmReceive').addEventListener('click', confirmReceive);

  document.querySelectorAll('.folder-btn').forEach(btn => {
    btn.addEventListener('click', () => setMailboxFolder(btn.dataset.folder || 'pending'));
  });
  $('mailDateFilter').addEventListener('change', () => {
    selectedMailboxId = 0;
    loadTransfers();
  });
  $('btnMailToday').addEventListener('click', () => {
    $('mailDateFilter').value = new Date().toISOString().slice(0, 10);
    selectedMailboxId = 0;
    loadTransfers();
  });
  $('btnMailAll').addEventListener('click', () => {
    $('mailDateFilter').value = '';
    selectedMailboxId = 0;
    loadTransfers();
  });
});
</script>
</body>
</html>