<?php
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/template-engine.php';

$pageTitle  = 'Produse';
$breadcrumb = [['label' => 'Produse']];

// --- Query params: filtre + pagination + search (toate server-side) ---
// Implicit, scopul e contul activ (selectat din header). Un ?account explicit în
// URL îl poate suprascrie pentru acest view (inclusiv ?account= gol = toate).
$filterAccount  = isset($_GET['account'])  ? (int)$_GET['account']  : active_account_id();
$filterCategory = isset($_GET['category']) ? (int)$_GET['category'] : 0;
$filterBrand    = isset($_GET['brand'])    ? (int)$_GET['brand']    : 0;
$filterStatus   = $_GET['status'] ?? '';
$filterOffer    = $_GET['offer']  ?? ''; // '', '1' (activ), '0' (inactiv), '2' (EOL), 'null' (necunoscut)
$filterUnits    = $_GET['units']  ?? ''; // '', 'null', 'any', sau număr exact
$filterHotness  = trim((string)($_GET['hot'] ?? '')); // '', 'null', sau valoare exactă (HOT, COLD, ...)
$filterFav      = $_GET['fav'] ?? ''; // '1' = doar favorite (principal)
$filterSec      = $_GET['sec'] ?? ''; // '1' = doar secundare
$filterHideRoles= $_GET['hideroles'] ?? ''; // '1' = ascunde produsele cu rol (principal/secundar)
$search         = trim((string)($_GET['q'] ?? ''));
$perPageAllowed = [50, 100, 250, 500];
$perPage        = (int)($_GET['per_page'] ?? 100);
if (!in_array($perPage, $perPageAllowed, true)) $perPage = 100;
$page = max(1, (int)($_GET['page'] ?? 1));

// Export JSON (folosit de exportul Excel din client): întoarce TOATE produsele
// filtrate, fără paginare. Doar citire — nu modifică nimic.
$exportJson = (($_GET['export'] ?? '') === 'json');
if ($exportJson) { $perPage = 1000000; $page = 1; }

$products   = [];
$accounts   = [];
$phoneBrands = []; $phoneModels = []; $phoneSeries = [];
$nameTemplatesList = [];
$sessionsList = [];
$categoriesList = [];
$total      = 0;
$totalPages = 1;
$dbError    = null;

try {
    $accounts = db_select("SELECT id, account_name FROM emag_accounts ORDER BY account_name ASC");
    // Arăt TOATE sesiunile (inclusiv completed) — userul poate să adauge produse în orice
    // sesiune non-sending. Sortat: active sus (draft/partial/failed), completed/queued jos.
    $sessionsList = db_select(
        "SELECT id, name, status,
                (SELECT COUNT(*) FROM title_session_items WHERE session_id = title_sessions.id) AS items_count
         FROM title_sessions
         WHERE status != 'sending'
         ORDER BY
           CASE status
             WHEN 'draft'     THEN 1
             WHEN 'partial'   THEN 2
             WHEN 'failed'    THEN 3
             WHEN 'queued'    THEN 4
             WHEN 'completed' THEN 5
             ELSE 6
           END,
           updated_at DESC"
    );
    // Valorile distincte de units_per_box pentru dropdown
    $unitsValues = array_map('intval', array_column(
        db_select("SELECT DISTINCT units_per_box FROM products WHERE units_per_box IS NOT NULL ORDER BY units_per_box ASC"),
        'units_per_box'
    ));
    // Valorile distincte de hotness pentru dropdown (sortate de la „cald" la „rece")
    $hotnessOrder = ['SUPER HOT' => 1, 'HOT' => 2, 'WARM' => 3, 'COLD' => 4, 'SUPER COLD' => 5];
    $hotnessValues = array_column(
        db_select("SELECT DISTINCT hotness FROM products WHERE hotness IS NOT NULL AND TRIM(hotness) != ''"),
        'hotness'
    );
    usort($hotnessValues, fn($a, $b) => ($hotnessOrder[strtoupper($a)] ?? 99) <=> ($hotnessOrder[strtoupper($b)] ?? 99));
    $phoneBrands  = db_select(
        "SELECT b.id, b.name,
                (SELECT COUNT(*) FROM products p2
                 JOIN phone_series ps2 ON ps2.id = p2.phone_series_id
                 JOIN phone_models pm2 ON pm2.id = ps2.model_id
                 WHERE pm2.brand_id = b.id) AS products_count
         FROM phone_brands b
         ORDER BY b.name COLLATE NOCASE"
    );
    $phoneModels  = db_select("SELECT id, brand_id, name FROM phone_models ORDER BY name COLLATE NOCASE");
    $phoneSeries  = db_select("SELECT id, model_id, name FROM phone_series ORDER BY name COLLATE NOCASE");
    $nameTemplatesList = db_select("SELECT id, name, template FROM name_templates ORDER BY name COLLATE NOCASE");
    $categoriesList = db_select(
        "SELECT c.id, c.name, c.custom_name, c.emag_category_id,
                (SELECT COUNT(*) FROM products p WHERE p.category_id = c.id) AS products_count
         FROM categories c
         ORDER BY products_count DESC, c.name ASC"
    );

    $where  = ['1=1'];
    $params = [];

    if ($filterAccount > 0) {
        $where[] = 'p.emag_account_id = ?';
        $params[] = $filterAccount;
    }
    if ($filterCategory > 0) {
        $where[] = 'p.category_id = ?';
        $params[] = $filterCategory;
    }
    if ($filterBrand > 0) {
        // Filtru pe brand-ul telefonului PRIMARY al produsului.
        // Necesită ca produsul să aibă phone_series_id setat → JOIN-urile din template_join_sql() expun pb.id
        $where[] = 'pb.id = ?';
        $params[] = $filterBrand;
    }
    if (in_array($filterStatus, ['draft','active','synced'], true)) {
        $where[] = 'p.status = ?';
        $params[] = $filterStatus;
    }
    if ($filterOffer === 'null') {
        $where[] = 'p.emag_offer_status IS NULL';
    } elseif (in_array($filterOffer, ['0','1','2'], true)) {
        $where[] = 'p.emag_offer_status = ?';
        $params[] = (int)$filterOffer;
    }
    if ($filterUnits === 'null') {
        $where[] = 'p.units_per_box IS NULL';
    } elseif ($filterUnits === 'any') {
        $where[] = 'p.units_per_box IS NOT NULL';
    } elseif ($filterUnits !== '' && ctype_digit((string)$filterUnits)) {
        $where[] = 'p.units_per_box = ?';
        $params[] = (int)$filterUnits;
    }
    if ($filterHotness === 'null') {
        $where[] = "(p.hotness IS NULL OR p.hotness = '')";
    } elseif ($filterHotness !== '') {
        $where[] = 'p.hotness = ?';
        $params[] = $filterHotness;
    }
    // Favorite (principal) și/sau secundare — combinate cu OR dacă ambele bifate
    $roleWants = [];
    if ($filterFav === '1') $roleWants[] = 'principal';
    if ($filterSec === '1') $roleWants[] = 'secundar';
    if ($roleWants) {
        $where[] = 'p.compare_role IN (' . implode(',', array_fill(0, count($roleWants), '?')) . ')';
        foreach ($roleWants as $rw) $params[] = $rw;
    }
    if ($filterHideRoles === '1') {
        // Ascunde produsele marcate (principal sau secundar) → arată doar neutre
        $where[] = "(p.compare_role IS NULL OR p.compare_role = '')";
    }
    if ($search !== '') {
        $where[] = '(p.ean LIKE ? OR p.product_name LIKE ? OR p.pnk LIKE ? OR p.cod_oblio LIKE ? OR p.emag_id LIKE ? OR p.emag_part_number LIKE ?)';
        $q = '%' . $search . '%';
        array_push($params, $q, $q, $q, $q, $q, $q);
    }
    $whereSql = implode(' AND ', $where);

    $joinSql = template_join_sql();
    $selSql  = template_select_sql();
    // COUNT folosește ACELEAȘI JOIN-uri ca SELECT-ul de listare — necesar pentru filtre care folosesc pb.id etc.
    $total      = (int)db_scalar(
        "SELECT COUNT(*) FROM products p
         LEFT JOIN emag_accounts a ON a.id = p.emag_account_id
         LEFT JOIN categories    c ON c.id = p.category_id
         $joinSql
         WHERE $whereSql",
        $params
    );
    $totalPages = max(1, (int)ceil($total / $perPage));
    if ($page > $totalPages) $page = $totalPages;
    $offset = ($page - 1) * $perPage;

    $products = db_select(
        "SELECT p.*,
                a.account_name,
                c.name        AS cat_name,
                c.custom_name AS cat_custom_name,
                c.emag_category_id AS cat_emag_id,
                c.is_excluded AS cat_is_excluded,
                $selSql,
                (SELECT GROUP_CONCAT(phone_series_id) FROM product_phone_series_alt WHERE product_id = p.id) AS alt_series_ids,
                (SELECT GROUP_CONCAT(DISTINCT p2.pnk) FROM products p2
                  WHERE p.phone_series_id IS NOT NULL
                    AND p2.phone_series_id = p.phone_series_id
                    AND p2.pnk IS NOT NULL AND TRIM(p2.pnk) != ''
                    AND p2.pnk != p.pnk) AS linked_pnks,
                -- Cheie de grupare pe compatibilitate: cel mai mic nume de model din
                -- mulțimea {model principal} ∪ {modele compatibile}. Produsele care
                -- partajează aceeași mulțime (ex: folia iPhone 11 și folia iPhone XR,
                -- compatibile reciproc) primesc aceeași cheie → apar grupate.
                min(
                  COALESCE((SELECT pb2.name||' '||pm2.name||' '||ps2.name
                            FROM phone_series ps2
                            JOIN phone_models pm2 ON pm2.id = ps2.model_id
                            JOIN phone_brands pb2 ON pb2.id = pm2.brand_id
                            WHERE ps2.id = p.phone_series_id), 'zzzz'),
                  COALESCE((SELECT MIN(pb3.name||' '||pm3.name||' '||ps3.name)
                            FROM product_phone_series_alt a
                            JOIN phone_series ps3 ON ps3.id = a.phone_series_id
                            JOIN phone_models pm3 ON pm3.id = ps3.model_id
                            JOIN phone_brands pb3 ON pb3.id = pm3.brand_id
                            WHERE a.product_id = p.id), 'zzzz')
                ) AS cluster_key
         FROM products p
         LEFT JOIN emag_accounts a ON a.id = p.emag_account_id
         LEFT JOIN categories    c ON c.id = p.category_id
         $joinSql
         WHERE $whereSql
         ORDER BY
           (p.phone_series_id IS NULL),          -- produsele cu model de telefon primele
           cluster_key COLLATE NOCASE,           -- grupare pe compatibilitate (ecran comun)
           pb.name COLLATE NOCASE,               -- în cadrul grupului: brand…
           pm.name COLLATE NOCASE,               -- …model…
           ps.name COLLATE NOCASE,               -- …serie
           p.units_per_box,                      -- variantele (1 buc / 2 buc) lipite
           p.updated_at DESC
         LIMIT ? OFFSET ?",
        array_merge($params, [$perPage, $offset])
    );
} catch (Throwable $ex) {
    $dbError = $ex->getMessage();
}

// Răspuns export JSON (toate produsele filtrate) — înainte de a randa pagina.
if ($exportJson) {
    if ($dbError) {
        json_response(['ok' => false, 'error' => $dbError], 500);
    }
    json_response(['ok' => true, 'rows' => $products, 'total' => count($products)]);
}

/** Helper: construiește un URL cu params actualizați. */
function paged_url(array $overrides = []): string {
    $current = [
        'page'     => $_GET['page']     ?? null,
        'per_page' => $_GET['per_page'] ?? null,
        'q'        => $_GET['q']        ?? null,
        'account'  => $_GET['account']  ?? null,
        'category' => $_GET['category'] ?? null,
        'brand'    => $_GET['brand']    ?? null,
        'status'   => $_GET['status']   ?? null,
        'offer'    => $_GET['offer']    ?? null,
        'units'    => $_GET['units']    ?? null,
        'hot'      => $_GET['hot']      ?? null,
        'fav'      => $_GET['fav']      ?? null,
        'sec'      => $_GET['sec']      ?? null,
        'hideroles'=> $_GET['hideroles']?? null,
    ];
    $merged = array_filter(array_merge($current, $overrides), fn($v) => $v !== null && $v !== '');
    return '?' . http_build_query($merged);
}

$startRow = $total > 0 ? (($page - 1) * $perPage) + 1 : 0;
$endRow   = min($page * $perPage, $total);

require __DIR__ . '/includes/header.php';
?>

<!-- SortableJS pentru drag-and-drop coloane -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<!-- SheetJS pentru export Excel (.xlsx) -->
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>

<?php if ($dbError): ?>
  <div class="mb-6 bg-amber-50 border border-amber-200 rounded-xl p-4 text-sm text-amber-900">
    Eroare DB. Verifică permisiunile pe folderul <code>data/</code>.
    <details class="mt-2"><summary class="cursor-pointer">Detalii</summary><pre class="mt-2 text-xs"><?= e($dbError) ?></pre></details>
  </div>
<?php endif; ?>

<div
  x-data="productsTable({
    rows: <?= htmlspecialchars(json_encode($products), ENT_QUOTES, 'UTF-8') ?>,
    startIndex: <?= $startRow ?: 1 ?>,
    csrf: '<?= e(csrf_token()) ?>',
    phoneBrands: <?= htmlspecialchars(json_encode($phoneBrands), ENT_QUOTES, 'UTF-8') ?>,
    phoneModels: <?= htmlspecialchars(json_encode($phoneModels), ENT_QUOTES, 'UTF-8') ?>,
    phoneSeries: <?= htmlspecialchars(json_encode($phoneSeries), ENT_QUOTES, 'UTF-8') ?>,
    nameTemplates: <?= htmlspecialchars(json_encode($nameTemplatesList), ENT_QUOTES, 'UTF-8') ?>,
    sessionsList: <?= htmlspecialchars(json_encode($sessionsList), ENT_QUOTES, 'UTF-8') ?>
  })"
  x-init="init()"
>

  <!-- Header bar -->
  <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
    <div>
      <h1 class="text-2xl font-bold text-slate-900">Produse</h1>
      <p class="text-sm text-slate-500 mt-0.5">
        <?php if ($total > 0): ?>
          Afișez <strong><?= $startRow ?>–<?= $endRow ?></strong> din <strong><?= number_format($total, 0, ',', '.') ?></strong> produse
        <?php else: ?>
          Niciun produs
        <?php endif; ?>
      </p>
    </div>
    <div class="flex items-center gap-2">
      <button @click="updateReviews()" :disabled="reviewsBusy"
        class="inline-flex items-center gap-2 px-3.5 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg shadow-soft transition disabled:opacity-60 disabled:cursor-not-allowed"
        title="Extrage rating + nr. recenzii de pe paginile publice eMAG (lent, throttled)">
        <span x-show="reviewsBusy" class="spinner"></span>
        <svg x-show="!reviewsBusy" class="w-4 h-4 text-amber-500" fill="currentColor" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
        <span x-text="reviewsBusy ? 'Extrag recenzii…' : 'Actualizează recenzii'"></span>
      </button>
      <button @click="openExport()"
        class="inline-flex items-center gap-2 px-3.5 py-2 text-sm font-medium text-white bg-emerald-600 hover:bg-emerald-700 rounded-lg shadow-soft transition">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5 5-5M12 15V3"/></svg>
        Export Excel
      </button>
      <button @click="exportCsv()"
        class="inline-flex items-center gap-2 px-3.5 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg shadow-soft transition">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5 5-5M12 15V3"/></svg>
        Export CSV (pagina)
      </button>
      <a href="<?= url('add-product.php') ?>"
         class="inline-flex items-center gap-2 px-3.5 py-2 text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 rounded-lg shadow-soft transition">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
        Adaugă produs
      </a>
    </div>
  </div>

  <!-- Progres extragere recenzii -->
  <div x-show="reviewsProgress" x-cloak
       class="mb-4 flex items-center gap-2 text-sm px-4 py-2.5 rounded-lg border"
       :class="reviewsBusy ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-emerald-50 border-emerald-200 text-emerald-800'">
    <span x-show="reviewsBusy" class="spinner"></span>
    <span x-text="reviewsProgress"></span>
  </div>

  <!-- Filtre -->
  <div class="bg-white border border-slate-100 rounded-xl shadow-soft p-4 mb-4">
    <form method="get" id="filtersForm" data-ajax-filters class="space-y-3">
      <div class="grid grid-cols-1 md:grid-cols-12 gap-3">
        <!-- Search server-side, auto-submit debounced -->
        <div class="md:col-span-10 relative">
          <svg class="w-4 h-4 absolute left-3 top-3 text-slate-400" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
          <input type="text" name="q" value="<?= e($search) ?>"
            @input.debounce.400ms="$el.form.requestSubmit()"
            placeholder="Caută după EAN, nume, PNK, cod Oblio, eMAG ID, Cod produs (PN)…"
            autocomplete="off"
            class="w-full pl-9 pr-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent">
        </div>

        <!-- Columns dropdown -->
        <div class="md:col-span-2 relative" @click.outside="showColumnsMenu = false">
        <button type="button" @click="showColumnsMenu = !showColumnsMenu"
          class="w-full inline-flex items-center justify-center gap-2 px-3 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg transition">
          <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 10h16M4 14h16M4 18h16"/></svg>
          Coloane
          <span class="text-[10px] bg-slate-100 px-1.5 py-0.5 rounded-full" x-text="visibleColumns().length + '/' + columns.length"></span>
        </button>

        <div x-show="showColumnsMenu" x-cloak
             x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
             class="absolute right-0 mt-2 w-72 bg-white border border-slate-200 rounded-xl shadow-2xl z-30">
          <div class="px-4 py-3 border-b border-slate-100 flex items-center justify-between">
            <div>
              <div class="text-sm font-semibold text-slate-900">Coloane vizibile</div>
              <div class="text-xs text-slate-500">Trage pentru a reordona</div>
            </div>
            <button type="button" @click="resetColumns()" class="text-xs text-brand-600 hover:text-brand-700">Resetează</button>
          </div>

          <ul x-ref="colList" class="py-2 max-h-80 overflow-y-auto">
            <template x-for="col in columns" :key="col.key">
              <li :data-key="col.key" class="px-2 py-1.5 mx-2 rounded hover:bg-slate-50 flex items-center gap-2 cursor-move">
                <svg class="w-4 h-4 text-slate-300 flex-shrink-0" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/></svg>
                <label class="flex-1 flex items-center gap-2 cursor-pointer" @click.stop>
                  <input type="checkbox" :checked="col.visible" @change="toggleColumn(col.key)"
                    class="rounded border-slate-300 text-brand-600 focus:ring-brand-500">
                  <span class="text-sm text-slate-700" x-text="col.label"></span>
                </label>
              </li>
            </template>
          </ul>
        </div>
        </div>
      </div>

      <!-- Rândul 2: filtre dropdown -->
      <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-7 gap-3">
        <select name="account" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="0">Toate conturile</option>
          <?php foreach ($accounts as $acc): ?>
            <option value="<?= (int)$acc['id'] ?>" <?= $filterAccount === (int)$acc['id'] ? 'selected' : '' ?>>
              <?= e($acc['account_name']) ?>
            </option>
          <?php endforeach; ?>
        </select>

        <select name="brand" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="0">Toate brandurile</option>
          <?php foreach ($phoneBrands as $b):
              $count = (int)$b['products_count'];
              $countLabel = $count === 1 ? '1 produs' : ($count . ' produse');
          ?>
            <option value="<?= (int)$b['id'] ?>" <?= $filterBrand === (int)$b['id'] ? 'selected' : '' ?>>
              <?= e($b['name']) ?> · <?= $countLabel ?>
            </option>
          <?php endforeach; ?>
        </select>

        <select name="category" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="0">Toate categoriile</option>
          <?php foreach ($categoriesList as $cat):
              $label = $cat['custom_name'] ?: $cat['name'];
              $count = (int)$cat['products_count'];
              $countLabel = $count === 1 ? '1 produs' : ($count . ' produse');
          ?>
            <option value="<?= (int)$cat['id'] ?>" <?= $filterCategory === (int)$cat['id'] ? 'selected' : '' ?>>
              <?= e($label) ?><?php if ($cat['emag_category_id']): ?> (eMAG <?= (int)$cat['emag_category_id'] ?>)<?php endif; ?> · <?= $countLabel ?>
            </option>
          <?php endforeach; ?>
        </select>

        <select name="status" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="">Toate statusurile</option>
          <option value="draft"  <?= $filterStatus==='draft'  ? 'selected':'' ?>>Draft</option>
          <option value="active" <?= $filterStatus==='active' ? 'selected':'' ?>>Active</option>
          <option value="synced" <?= $filterStatus==='synced' ? 'selected':'' ?>>Sincronizate</option>
        </select>

        <select name="offer" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="">Stare eMAG: toate</option>
          <option value="1"    <?= $filterOffer==='1'    ? 'selected':'' ?>>Doar active</option>
          <option value="0"    <?= $filterOffer==='0'    ? 'selected':'' ?>>Doar inactive</option>
          <option value="2"    <?= $filterOffer==='2'    ? 'selected':'' ?>>End of life</option>
          <option value="null" <?= $filterOffer==='null' ? 'selected':'' ?>>Necunoscut (fără sync)</option>
        </select>

        <select name="units" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="">Nr. buc: toate</option>
          <option value="null" <?= $filterUnits === 'null' ? 'selected':'' ?>>Fără valoare</option>
          <option value="any"  <?= $filterUnits === 'any'  ? 'selected':'' ?>>Cu valoare (oricare)</option>
          <?php if ($unitsValues): ?>
            <option disabled>──────────</option>
            <?php foreach ($unitsValues as $val): ?>
              <option value="<?= (int)$val ?>" <?= (string)$filterUnits === (string)$val ? 'selected':'' ?>>= <?= (int)$val ?> buc</option>
            <?php endforeach; ?>
          <?php endif; ?>
        </select>

        <select name="hot" onchange="this.form.requestSubmit()"
          class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
          <option value="">Performanță: toate</option>
          <?php foreach ($hotnessValues as $hv): ?>
            <option value="<?= e($hv) ?>" <?= $filterHotness === $hv ? 'selected':'' ?>><?= e($hv) ?></option>
          <?php endforeach; ?>
          <option value="null" <?= $filterHotness === 'null' ? 'selected':'' ?>>Fără valoare</option>
        </select>
      </div>

      <!-- Rândul 3: filtre pe rol (checkmarks) — rând separat sub selectoare -->
      <div class="flex flex-wrap items-center gap-2">
        <label class="inline-flex items-center gap-2 px-3 py-1.5 text-sm border rounded-lg cursor-pointer select-none transition <?= $filterFav === '1' ? 'border-amber-300 bg-amber-50 text-amber-800' : 'border-slate-200 bg-white text-slate-700 hover:bg-slate-50' ?>"
               title="Bifează ca să afișezi doar produsele favorite (principale)">
          <input type="checkbox" name="fav" value="1" <?= $filterFav === '1' ? 'checked' : '' ?>
            onchange="this.form.requestSubmit()"
            class="rounded border-slate-300 text-amber-500 focus:ring-amber-400">
          <span class="whitespace-nowrap">★ Doar favorite</span>
        </label>
        <label class="inline-flex items-center gap-2 px-3 py-1.5 text-sm border rounded-lg cursor-pointer select-none transition <?= $filterSec === '1' ? 'border-sky-300 bg-sky-50 text-sky-800' : 'border-slate-200 bg-white text-slate-700 hover:bg-slate-50' ?>"
               title="Bifează ca să afișezi produsele secundare">
          <input type="checkbox" name="sec" value="1" <?= $filterSec === '1' ? 'checked' : '' ?>
            onchange="this.form.requestSubmit()"
            class="rounded border-slate-300 text-sky-500 focus:ring-sky-400">
          <span class="whitespace-nowrap">☆ Doar secundare</span>
        </label>
        <label class="inline-flex items-center gap-2 px-3 py-1.5 text-sm border rounded-lg cursor-pointer select-none transition <?= $filterHideRoles === '1' ? 'border-slate-400 bg-slate-100 text-slate-800' : 'border-slate-200 bg-white text-slate-700 hover:bg-slate-50' ?>"
               title="Bifează ca să ascunzi produsele marcate (principal sau secundar)">
          <input type="checkbox" name="hideroles" value="1" <?= $filterHideRoles === '1' ? 'checked' : '' ?>
            onchange="this.form.requestSubmit()"
            class="rounded border-slate-300 text-slate-500 focus:ring-slate-400">
          <span class="whitespace-nowrap">Ascunde principal/secundar</span>
        </label>
      </div>

      <!-- Hidden: păstrăm per_page la submit -->
      <input type="hidden" name="per_page" value="<?= (int)$perPage ?>">
    </form>

    <!-- Bulk actions -->
    <div x-show="selected.length > 0" x-cloak class="mt-3 pt-3 border-t border-slate-100 flex items-center gap-3 text-sm flex-wrap">
      <span class="font-medium text-slate-700"><span x-text="selected.length"></span> selectate (din această pagină)</span>
      <button @click="openCompare()" x-show="selected.length >= 2" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-indigo-700 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition" title="Compară produsele selectate">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"/></svg>
        Compară
      </button>
      <button @click="openApply()" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-emerald-700 bg-emerald-50 hover:bg-emerald-100 rounded-lg transition" title="Generează nume custom din șablon">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/></svg>
        Aplică șablon
      </button>
      <button @click="openAddSession()" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sky-700 bg-sky-50 hover:bg-sky-100 rounded-lg transition" title="Adaugă în sesiune de schimbare titluri (cu push la eMAG)">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
        Adaugă în sesiune
      </button>
      <button @click="bulkExclude()" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-amber-800 bg-amber-50 hover:bg-amber-100 rounded-lg transition" title="Mută EAN-urile în excluse + șterge produsele">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"/></svg>
        Exclude (după EAN)
      </button>
      <button @click="bulkDelete()" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-rose-700 bg-rose-50 hover:bg-rose-100 rounded-lg transition">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M1 7h22M9 7V4a1 1 0 011-1h4a1 1 0 011 1v3"/></svg>
        Șterge
      </button>
      <button @click="selected = []" class="text-slate-500 hover:text-slate-700">Anulează</button>
    </div>
  </div>

  <!-- Tabel -->
  <div class="bg-white border border-slate-100 rounded-xl shadow-soft overflow-hidden">
    <div class="overflow-x-auto">
      <table class="text-sm w-full">
        <thead class="bg-slate-50 text-xs uppercase tracking-wide text-slate-500">
          <tr class="divide-x divide-slate-200">
            <th class="px-3 py-2.5 text-left w-10">
              <input type="checkbox" @change="toggleAll($event)" :checked="selected.length === rows.length && rows.length > 0" class="rounded border-slate-300 text-brand-600 focus:ring-brand-500">
            </th>
            <th class="px-2 py-2.5 text-left font-medium w-12">#</th>
            <template x-for="col in visibleColumns()" :key="'th-'+col.key">
              <th :class="col.key === 'product_name' ? 'px-3 py-2.5 text-left font-medium' : 'px-2 py-2.5 text-left font-medium whitespace-nowrap'"
                  x-text="col.label"></th>
            </template>
            <th class="px-2 py-2.5 text-right font-medium w-24">Acțiuni</th>
          </tr>
        </thead>
        <tbody class="divide-y divide-slate-100">
          <template x-if="rows.length === 0">
            <tr><td :colspan="visibleColumns().length + 3" class="px-4 py-12 text-center text-slate-400">
              <div class="mx-auto w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center mb-3">
                <svg class="w-6 h-6 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
              </div>
              Niciun produs nu corespunde filtrelor.
            </td></tr>
          </template>

          <template x-for="(p, idx) in rows" :key="p.id">
            <tr class="row-hover align-top divide-x divide-slate-100">
              <td class="px-3 py-2.5">
                <input type="checkbox" :value="p.id" x-model="selected" class="rounded border-slate-300 text-brand-600 focus:ring-brand-500">
              </td>
              <td class="px-2 py-2.5 text-xs text-slate-400 font-mono" x-text="startIndex + idx"></td>

              <template x-for="col in visibleColumns()" :key="'td-'+p.id+'-'+col.key">
                <td :class="col.key === 'product_name' ? 'px-3 py-2.5 align-top w-full' : 'px-2 py-2.5 align-top whitespace-nowrap'">
                  <!-- product_name: arată custom_name dacă există, altfel split din product_name -->
                  <template x-if="col.key === 'product_name'">
                    <div class="min-w-[260px] cursor-pointer hover:bg-amber-50 active:bg-amber-100 -mx-1 px-1 py-0.5 rounded transition relative group"
                         @click="copyName($event, p.custom_name || p.product_name)"
                         :title="'Click pentru a copia: ' + (p.custom_name || p.product_name)">
                      <span x-show="p.compare_role === 'principal'" class="text-amber-500 mr-1" title="Principal">★</span>
                      <span x-show="p.compare_role === 'secundar'" class="text-slate-300 mr-1" title="Secundar">☆</span>
                      <template x-if="p.custom_name">
                        <div>
                          <div class="flex items-start gap-1.5">
                            <span class="inline-flex items-center gap-0.5 text-[9px] uppercase tracking-wide px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-700 font-semibold mt-0.5 flex-shrink-0" title="Nume custom (template)">
                              <svg class="w-2.5 h-2.5" fill="currentColor" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
                              custom
                            </span>
                            <div class="product-name-block flex-1 min-w-0" x-text="p.custom_name"></div>
                          </div>
                          <div class="text-[11px] text-slate-400 break-words mt-0.5 leading-snug" :title="'Nume original eMAG: ' + p.product_name" x-text="'eMAG: ' + p.product_name"></div>
                        </div>
                      </template>
                      <template x-if="!p.custom_name">
                        <div class="product-name-block" x-text="p.product_name"></div>
                      </template>
                      <span x-show="copiedNameId === p.id"
                            x-transition.opacity
                            class="absolute top-0.5 right-0.5 inline-flex items-center gap-0.5 text-[10px] font-semibold px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-700 pointer-events-none">
                        ✓ copiat
                      </span>
                    </div>
                  </template>

                  <!-- phone_link: primary + lista secondary mai mic + buton edit -->
                  <template x-if="col.key === 'phone_link'">
                    <button @click="openPhonePicker(p)"
                      class="w-full text-left group cursor-pointer hover:bg-amber-50 active:bg-amber-100 -mx-1 px-2 py-1 rounded transition">
                      <template x-if="p.phone_series_name">
                        <div>
                          <div class="text-xs text-slate-800 font-medium flex items-start gap-1">
                            <span class="text-amber-500 flex-shrink-0">★</span>
                            <span x-text="[p.phone_brand_name, p.phone_model_name, p.phone_series_name].filter(Boolean).join(' ')"></span>
                          </div>
                          <template x-if="altLabels(p).length > 0">
                            <div class="mt-1 pl-3 border-l-2 border-sky-100 space-y-0.5">
                              <template x-for="label in altLabels(p)" :key="label">
                                <div class="text-[10px] text-slate-500 leading-tight" x-text="label"></div>
                              </template>
                            </div>
                          </template>
                        </div>
                      </template>
                      <template x-if="!p.phone_series_name">
                        <span class="text-xs text-slate-300 group-hover:text-amber-700">— click pentru legare</span>
                      </template>
                    </button>
                  </template>

                  <!-- status: arată starea ofertei pe eMAG (activ / inactiv / EOL), nu statusul intern -->
                  <template x-if="col.key === 'status'">
                    <span class="inline-block text-[10px] font-medium uppercase tracking-wide px-1.5 py-0.5 rounded-full"
                          :class="{
                            'bg-emerald-50 text-emerald-700': p.emag_offer_status == 1,
                            'bg-rose-50 text-rose-700':        p.emag_offer_status == 0,
                            'bg-amber-50 text-amber-700':      p.emag_offer_status == 2,
                            'bg-slate-100 text-slate-400':     p.emag_offer_status === null || p.emag_offer_status === undefined || p.emag_offer_status === ''
                          }"
                          x-text="emagStatusLabel(p.emag_offer_status)"></span>
                  </template>

                  <!-- emag_offer_status: badge clickabil Activ / Inactiv / EOL (toggle pe eMAG) -->
                  <template x-if="col.key === 'emag_offer_status'">
                    <button type="button"
                          @click="toggleOfferStatus(p)"
                          :disabled="isSettingStatus(p.id) || !p.emag_id"
                          class="inline-flex items-center gap-1 text-[10px] font-medium uppercase tracking-wide px-1.5 py-0.5 rounded-full transition disabled:opacity-60 disabled:cursor-not-allowed"
                          :class="{
                            'bg-emerald-50 text-emerald-700 hover:bg-emerald-100': p.emag_offer_status == 1,
                            'bg-rose-50 text-rose-700 hover:bg-rose-100':          p.emag_offer_status == 0,
                            'bg-amber-50 text-amber-700 hover:bg-amber-100':       p.emag_offer_status == 2,
                            'bg-slate-100 text-slate-400':                         p.emag_offer_status === null || p.emag_offer_status === undefined || p.emag_offer_status === ''
                          }"
                          :title="!p.emag_id ? 'Fără emag_id — nu se poate modifica pe eMAG'
                                  : (p.emag_offer_status == 1 ? 'Click pentru a DEZACTIVA oferta pe eMAG'
                                                              : 'Click pentru a ACTIVA oferta pe eMAG')">
                      <span x-show="isSettingStatus(p.id)" class="spinner"></span>
                      <span x-text="emagStatusLabel(p.emag_offer_status)"></span>
                    </button>
                  </template>

                  <!-- updated_at: dată formatată -->
                  <template x-if="col.key === 'updated_at'">
                    <span class="text-xs text-slate-500" x-text="formatDate(p.updated_at)"></span>
                  </template>

                  <!-- category: nume (custom prioritar) + ID eMAG mic -->
                  <template x-if="col.key === 'category'">
                    <div>
                      <div class="text-xs text-slate-700 break-words" x-text="(p.cat_custom_name || p.cat_name || p.category) || '—'"></div>
                      <div class="text-[10px] text-slate-400 font-mono mt-0.5" x-show="p.cat_emag_id" x-text="'eMAG ' + p.cat_emag_id"></div>
                    </div>
                  </template>

                  <!-- units_per_box: inline edit -->
                  <template x-if="col.key === 'units_per_box'">
                    <div class="relative">
                      <template x-if="editingUnits !== p.id">
                        <button type="button" @click="startEditUnits(p)"
                          class="inline-flex items-center gap-1 text-xs text-slate-700 cursor-pointer hover:bg-amber-50 active:bg-amber-100 px-2 py-1 -mx-1 rounded transition min-w-[2.5rem] text-left"
                          title="Click pentru a edita">
                          <span x-show="p.units_per_box != null && p.units_per_box !== ''"
                                class="font-semibold tabular-nums" x-text="p.units_per_box"></span>
                          <span x-show="p.units_per_box == null || p.units_per_box === ''" class="text-slate-300">—</span>
                          <svg class="w-3 h-3 text-slate-300" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
                        </button>
                      </template>
                      <template x-if="editingUnits === p.id">
                        <input type="number" min="0" step="1"
                          x-model="editingUnitsValue"
                          x-init="$nextTick(() => { $el.focus(); $el.select(); })"
                          @blur="commitEditUnits(p)"
                          @keydown.enter.prevent="$el.blur()"
                          @keydown.escape="editingUnits = null"
                          class="w-20 px-2 py-1 text-xs border border-brand-400 rounded focus:outline-none focus:ring-2 focus:ring-brand-500 tabular-nums">
                      </template>
                    </div>
                  </template>

                  <!-- linked_pnk: PNK-ul produselor legate (același model telefon). Click → filtrează tabelul -->
                  <template x-if="col.key === 'linked_pnk'">
                    <div class="flex flex-col gap-0.5">
                      <template x-for="lp in (p.linked_pnks ? p.linked_pnks.split(',') : [])" :key="lp">
                        <button type="button" @click.stop="showLinkedPnk(lp)"
                          class="inline-flex items-center gap-1 font-mono text-[11px] text-brand-600 hover:text-brand-700 hover:underline text-left"
                          :title="'Arată ' + lp + ' în tabel'">
                          <svg class="w-3 h-3 flex-shrink-0 text-slate-300" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101M10.172 13.828a4 4 0 005.656 0l4-4a4 4 0 10-5.656-5.656l-1.1 1.1"/></svg>
                          <span x-text="lp"></span>
                        </button>
                      </template>
                      <span x-show="!p.linked_pnks" class="text-slate-300 text-xs">—</span>
                    </div>
                  </template>

                  <!-- price: preț de vânzare (sale_price, fără TVA) + monedă -->
                  <template x-if="col.key === 'price'">
                    <span class="text-xs tabular-nums whitespace-nowrap"
                          :class="(p.current_sale_price !== null && p.current_sale_price !== '' && p.current_sale_price !== undefined) ? 'text-slate-800 font-semibold' : 'text-slate-300'"
                          :title="(p.current_sale_price !== null && p.current_sale_price !== '') ? 'Preț de vânzare fără TVA' : ''"
                          x-text="(p.current_sale_price !== null && p.current_sale_price !== '' && p.current_sale_price !== undefined) ? (Number(p.current_sale_price).toFixed(2) + ' ' + (p.currency || '')) : '—'"></span>
                  </template>

                  <!-- price_vat: preț cu TVA (sale_price × (1 + TVA%)) + monedă -->
                  <template x-if="col.key === 'price_vat'">
                    <span class="text-xs tabular-nums whitespace-nowrap"
                          :class="(p.current_sale_price !== null && p.current_sale_price !== '' && p.current_sale_price !== undefined) ? 'text-slate-800 font-semibold' : 'text-slate-300'"
                          :title="(p.current_sale_price !== null && p.current_sale_price !== '') ? ('Preț cu TVA (' + APP_VAT_RATE + '%)') : ''"
                          x-text="(p.current_sale_price !== null && p.current_sale_price !== '' && p.current_sale_price !== undefined) ? ((Number(p.current_sale_price) * (1 + APP_VAT_RATE/100)).toFixed(2) + ' ' + (p.currency || '')) : '—'"></span>
                  </template>

                  <!-- emag_link: link către pagina produsului pe eMAG (nume slug + /pd/ + PNK) -->
                  <template x-if="col.key === 'emag_link'">
                    <span>
                      <template x-if="p.pnk">
                        <a :href="emagUrl(p)" target="_blank" rel="noopener" @click.stop
                           class="inline-flex items-center gap-1 text-xs text-brand-600 hover:text-brand-700 hover:underline whitespace-nowrap"
                           title="Deschide produsul pe eMAG">
                          <svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
                          Vezi
                        </a>
                      </template>
                      <template x-if="!p.pnk"><span class="text-slate-300 text-xs">—</span></template>
                    </span>
                  </template>

                  <!-- reviews: rating (★) + număr recenzii (din pagina publică eMAG) -->
                  <template x-if="col.key === 'reviews'">
                    <span class="text-xs whitespace-nowrap">
                      <template x-if="p.emag_reviews_count !== null && p.emag_reviews_count !== undefined && p.emag_reviews_count !== ''">
                        <span class="inline-flex items-center gap-1">
                          <span x-show="p.emag_rating" class="text-amber-500">★</span>
                          <span class="font-semibold text-slate-800" x-text="p.emag_rating ? Number(p.emag_rating).toFixed(2) : '–'"></span>
                          <span class="text-slate-400" x-text="'(' + p.emag_reviews_count + ')'"></span>
                        </span>
                      </template>
                      <template x-if="p.emag_reviews_count === null || p.emag_reviews_count === undefined || p.emag_reviews_count === ''">
                        <span class="text-slate-300">—</span>
                      </template>
                    </span>
                  </template>

                  <!-- hotness: badge colorat (Performanță produs) -->
                  <template x-if="col.key === 'hotness'">
                    <span class="text-xs whitespace-nowrap"
                          :class="p.hotness ? hotnessClass(p.hotness) + ' inline-block font-bold uppercase tracking-wide text-[10px] px-1.5 py-0.5 rounded-full' : 'text-slate-300'"
                          x-text="p.hotness || '—'"></span>
                  </template>

                  <!-- Fallback: coloane mono / text simplu (mono = click-to-copy) -->
                  <template x-if="!['product_name','status','emag_offer_status','linked_pnk','price','price_vat','hotness','reviews','emag_link','updated_at','category','units_per_box','phone_link'].includes(col.key)">
                    <span
                          :class="isMono(col.key)
                            ? 'inline-block font-mono text-xs text-slate-700 break-all cursor-pointer hover:bg-slate-100 active:bg-slate-200 px-1.5 py-0.5 -mx-1 rounded transition select-all'
                            : 'text-xs text-slate-700 break-words'"
                          @click="isMono(col.key) && p[col.key] && copyCell($event, p[col.key])"
                          :title="isMono(col.key) && p[col.key] ? 'Click pentru a copia: ' + p[col.key] : ''"
                          x-text="p[col.key] || '—'"></span>
                  </template>
                </td>
              </template>

              <td class="px-2 py-2.5 text-right whitespace-nowrap">
                <button @click="toggleFavoriteRow(p)" :disabled="roleSaving.includes(p.id)"
                        class="p-1.5 rounded transition disabled:opacity-50"
                        :class="p.compare_role === 'principal' ? 'text-amber-500 hover:bg-amber-50' : 'text-slate-300 hover:text-amber-500 hover:bg-amber-50'"
                        :title="p.compare_role === 'principal' ? 'Scoate de la favorite' : 'Setează ca favorit (se propagă la variantele aceluiași model)'">
                  <svg class="w-4 h-4" :fill="p.compare_role === 'principal' ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.196-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/></svg>
                </button>
                <button @click="openEdit(p)" class="text-brand-600 hover:text-brand-700 p-1.5 rounded hover:bg-brand-50 transition" title="Editează">
                  <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
                </button>
                <button @click="excludeOne(p)" :disabled="!p.ean"
                        class="text-amber-700 hover:text-amber-800 p-1.5 rounded hover:bg-amber-50 transition disabled:opacity-30 disabled:hover:bg-transparent disabled:cursor-not-allowed"
                        :title="p.ean ? 'Exclude EAN' : 'Necesită EAN'">
                  <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"/></svg>
                </button>
                <button @click="deleteOne(p)" class="text-rose-600 hover:text-rose-700 p-1.5 rounded hover:bg-rose-50 transition" title="Șterge">
                  <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M1 7h22M9 7V4a1 1 0 011-1h4a1 1 0 011 1v3"/></svg>
                </button>
              </td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>

    <!-- ====== Pagination footer ====== -->
    <?php if ($total > 0): ?>
    <div class="border-t border-slate-100 px-4 py-3 flex flex-col sm:flex-row items-center justify-between gap-3 bg-slate-50/50">
      <div class="text-xs text-slate-500">
        <strong><?= number_format($startRow, 0, ',', '.') ?>–<?= number_format($endRow, 0, ',', '.') ?></strong>
        din <strong><?= number_format($total, 0, ',', '.') ?></strong>
        <span class="hidden sm:inline">· pagina <?= $page ?> / <?= $totalPages ?></span>
      </div>

      <div class="flex items-center gap-3">
        <!-- Per-page selector -->
        <div class="flex items-center gap-1.5 text-xs text-slate-500">
          <span>per pagină:</span>
          <select onchange="ajaxNav(location.pathname + '<?= e(paged_url(['page' => 1, 'per_page' => '__PP__'])) ?>'.replace('__PP__', this.value))"
            class="px-2 py-1 border border-slate-200 rounded text-xs bg-white focus:outline-none focus:ring-1 focus:ring-brand-500">
            <?php foreach ($perPageAllowed as $pp): ?>
              <option value="<?= $pp ?>" <?= $perPage === $pp ? 'selected' : '' ?>><?= $pp ?></option>
            <?php endforeach; ?>
          </select>
        </div>

        <!-- Page numbers -->
        <nav class="flex items-center gap-1">
          <?php
          // Prev
          if ($page > 1):
          ?>
            <a href="<?= e(paged_url(['page' => $page - 1])) ?>"
               class="px-2.5 py-1.5 text-xs font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-100 rounded transition">
              ‹ Înapoi
            </a>
          <?php else: ?>
            <span class="px-2.5 py-1.5 text-xs font-medium text-slate-300 bg-white border border-slate-200 rounded cursor-not-allowed">‹ Înapoi</span>
          <?php endif; ?>

          <?php
          // Build smart page list: 1, ..., p-2, p-1, p, p+1, p+2, ..., last
          $pages = [];
          $window = 2;
          for ($i = 1; $i <= $totalPages; $i++) {
              if ($i === 1 || $i === $totalPages || abs($i - $page) <= $window) {
                  $pages[] = $i;
              }
          }
          $prev = 0;
          foreach ($pages as $i):
              if ($prev && $i - $prev > 1): ?>
                <span class="px-1.5 text-xs text-slate-400">…</span>
              <?php endif;
              if ($i === $page): ?>
                <span class="px-3 py-1.5 text-xs font-semibold text-white bg-brand-600 rounded"><?= $i ?></span>
              <?php else: ?>
                <a href="<?= e(paged_url(['page' => $i])) ?>"
                   class="px-3 py-1.5 text-xs font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-100 rounded transition"><?= $i ?></a>
              <?php endif;
              $prev = $i;
          endforeach; ?>

          <?php if ($page < $totalPages): ?>
            <a href="<?= e(paged_url(['page' => $page + 1])) ?>"
               class="px-2.5 py-1.5 text-xs font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-100 rounded transition">
              Înainte ›
            </a>
          <?php else: ?>
            <span class="px-2.5 py-1.5 text-xs font-medium text-slate-300 bg-white border border-slate-200 rounded cursor-not-allowed">Înainte ›</span>
          <?php endif; ?>
        </nav>
      </div>
    </div>
    <?php endif; ?>
  </div>

  <!-- ============== EXPORT EXCEL MODAL ============== -->
  <div x-show="exportOpen" x-cloak @keydown.escape.window="exportOpen = false"
       x-data="{ _bg: false }"
       @mousedown.self="_bg = true"
       @mouseup="if (_bg && $event.target === $el) exportOpen = false; _bg = false"
       class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm">
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col">
      <div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
        <div>
          <h3 class="font-semibold text-slate-900">Export Excel</h3>
          <p class="text-xs text-slate-500 mt-0.5">Alege coloanele și ce produse exporți. Fișier <code>.xlsx</code>.</p>
        </div>
        <button type="button" @click="exportOpen = false" class="text-slate-400 hover:text-slate-600 p-1">
          <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
        </button>
      </div>

      <div class="p-6 space-y-4 overflow-y-auto">
        <!-- Scop -->
        <div>
          <div class="text-xs font-medium text-slate-700 mb-1.5">Ce exporți</div>
          <div class="flex flex-wrap gap-2">
            <label class="inline-flex items-center gap-2 px-3 py-1.5 text-sm border rounded-lg cursor-pointer transition" :class="exportScope === 'all' ? 'border-emerald-300 bg-emerald-50 text-emerald-800' : 'border-slate-200 hover:bg-slate-50'">
              <input type="radio" value="all" x-model="exportScope" class="text-emerald-500 focus:ring-emerald-400">
              Toate produsele filtrate
            </label>
            <label class="inline-flex items-center gap-2 px-3 py-1.5 text-sm border rounded-lg cursor-pointer transition" :class="exportScope === 'page' ? 'border-emerald-300 bg-emerald-50 text-emerald-800' : 'border-slate-200 hover:bg-slate-50'">
              <input type="radio" value="page" x-model="exportScope" class="text-emerald-500 focus:ring-emerald-400">
              Doar pagina curentă (<span x-text="rows.length"></span>)
            </label>
          </div>
        </div>

        <!-- Coloane -->
        <div>
          <div class="flex items-center justify-between mb-1.5">
            <div>
              <span class="text-xs font-medium text-slate-700">Coloane de exportat (<span x-text="exportSelectedCount()"></span>/<span x-text="exportCols.length"></span>)</span>
              <span class="text-xs text-slate-400 ml-1">· trage de <svg class="w-3 h-3 inline -mt-0.5 text-slate-400" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/></svg> ca să reordonezi</span>
            </div>
            <div class="flex gap-2 text-xs">
              <button type="button" @click="exportToggleAll(true)" class="text-brand-600 hover:text-brand-700">Toate</button>
              <span class="text-slate-300">·</span>
              <button type="button" @click="exportToggleAll(false)" class="text-slate-500 hover:text-slate-700">Niciuna</button>
            </div>
          </div>
          <ul id="exportColList" class="border border-slate-100 rounded-lg p-2 max-h-72 overflow-y-auto space-y-0.5">
            <template x-for="col in exportCols" :key="col.key">
              <li class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-slate-50">
                <svg class="exp-drag w-4 h-4 text-slate-300 hover:text-slate-500 cursor-move flex-shrink-0" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/></svg>
                <label class="flex items-center gap-2 flex-1 min-w-0 cursor-pointer">
                  <input type="checkbox" x-model="col.checked" class="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500">
                  <span class="text-sm text-slate-700 truncate" x-text="col.label"></span>
                </label>
              </li>
            </template>
          </ul>
        </div>
      </div>

      <div class="px-6 py-3 border-t border-slate-100 flex items-center justify-end gap-2">
        <button type="button" @click="exportOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg transition">Anulează</button>
        <button type="button" @click="doExportExcel()" :disabled="exportBusy || exportSelectedCount() === 0"
                class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-emerald-600 hover:bg-emerald-700 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed">
          <span x-show="exportBusy" class="spinner"></span>
          <span x-text="exportBusy ? 'Generez…' : 'Descarcă .xlsx'"></span>
        </button>
      </div>
    </div>
  </div>

  <!-- ============== COMPARĂ PRODUSE MODAL ============== -->
  <div x-show="compareOpen" x-cloak @keydown.escape.window="compareOpen = false"
       x-data="{ _bg: false }"
       @mousedown.self="_bg = true"
       @mouseup="if (_bg && $event.target === $el) compareOpen = false; _bg = false"
       class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm">
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-6xl max-h-[90vh] flex flex-col">
      <div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
        <div>
          <h3 class="font-semibold text-slate-900">Comparație produse</h3>
          <p class="text-xs text-slate-500 mt-0.5"><strong x-text="compareProducts().length"></strong> produse · cele mai bune valori sunt evidențiate</p>
        </div>
        <button type="button" @click="compareOpen = false" class="text-slate-400 hover:text-slate-600 p-1">
          <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
        </button>
      </div>

      <div class="flex-1 overflow-auto p-4">
        <table class="text-sm border-collapse min-w-full">
          <thead>
            <tr>
              <th class="sticky left-0 z-10 bg-white text-left p-2 w-32 align-bottom"></th>
              <template x-for="p in compareProducts()" :key="'ch-'+p.id">
                <th class="p-2 text-left align-bottom border-b-2 min-w-[180px] max-w-[240px]"
                    :class="p.compare_role === 'principal' ? 'border-amber-300 bg-amber-50/40' : 'border-slate-100'">
                  <div class="flex items-center gap-1.5 mb-1.5">
                    <button type="button" @click="setComparePrimary(p)" :disabled="roleSaving.includes(p.id)"
                            class="inline-flex items-center gap-1 px-2 py-1 rounded-lg border text-[11px] font-medium transition disabled:opacity-50"
                            :class="(p.compare_role === 'principal') ? 'bg-amber-100 border-amber-300 text-amber-800 hover:bg-amber-200' : 'bg-white border-slate-200 text-slate-500 hover:bg-slate-50'"
                            :title="(p.compare_role === 'principal') ? 'Click pentru a anula principalul' : 'Setează ca principal (celelalte devin secundare)'">
                      <svg class="w-3.5 h-3.5" :fill="(p.compare_role === 'principal') ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.196-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/></svg>
                      <span x-text="(p.compare_role === 'principal') ? 'Principal' : 'Setează principal'"></span>
                    </button>
                    <span x-show="p.compare_role === 'secundar'" class="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded-full bg-slate-100 text-slate-500 font-semibold">Secundar</span>
                  </div>
                  <div class="font-semibold text-slate-800 text-xs leading-snug line-clamp-3" :title="p.custom_name || p.product_name" x-text="p.custom_name || p.product_name"></div>
                  <div class="text-[10px] text-slate-400 font-mono mt-1" x-text="p.account_name || ''"></div>
                </th>
              </template>
            </tr>
          </thead>
          <tbody class="divide-y divide-slate-100">
            <!-- Model compatibil (telefon principal) -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Model compatibil</td>
              <template x-for="p in compareProducts()" :key="'mc-'+p.id">
                <td class="p-2 text-xs font-medium text-slate-800" x-text="[p.phone_brand_name, p.phone_model_name, p.phone_series_name].filter(Boolean).join(' ') || '—'"></td>
              </template>
            </tr>
            <!-- Preț -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Preț (fără TVA)</td>
              <template x-for="p in compareProducts()" :key="'pr-'+p.id">
                <td class="p-2 tabular-nums" :class="isBest(p,'current_sale_price') ? 'bg-emerald-50 text-emerald-800 font-bold rounded' : 'text-slate-700'">
                  <span x-text="(p.current_sale_price!==null&&p.current_sale_price!==''&&p.current_sale_price!==undefined) ? (Number(p.current_sale_price).toFixed(2)+' '+(p.currency||'')) : '—'"></span>
                </td>
              </template>
            </tr>
            <!-- Performanță -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Performanță</td>
              <template x-for="p in compareProducts()" :key="'ho-'+p.id">
                <td class="p-2">
                  <span x-show="p.hotness" :class="hotnessClass(p.hotness) + ' inline-block font-bold uppercase tracking-wide text-[10px] px-1.5 py-0.5 rounded-full'" x-text="p.hotness"></span>
                  <span x-show="!p.hotness" class="text-slate-300">—</span>
                </td>
              </template>
            </tr>
            <!-- Rating -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Rating</td>
              <template x-for="p in compareProducts()" :key="'ra-'+p.id">
                <td class="p-2" :class="isBest(p,'emag_rating') ? 'bg-amber-50 rounded' : ''">
                  <span x-show="p.emag_rating"><span class="text-amber-500">★</span> <span class="font-semibold" :class="isBest(p,'emag_rating')?'text-amber-800':'text-slate-700'" x-text="p.emag_rating ? Number(p.emag_rating).toFixed(2) : ''"></span></span>
                  <span x-show="!p.emag_rating" class="text-slate-300">—</span>
                </td>
              </template>
            </tr>
            <!-- Nr. recenzii -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Nr. recenzii</td>
              <template x-for="p in compareProducts()" :key="'nr-'+p.id">
                <td class="p-2 tabular-nums" :class="isBest(p,'emag_reviews_count') ? 'bg-emerald-50 text-emerald-800 font-bold rounded' : 'text-slate-700'">
                  <span x-text="(p.emag_reviews_count!==null&&p.emag_reviews_count!==undefined&&p.emag_reviews_count!=='') ? p.emag_reviews_count : '—'"></span>
                </td>
              </template>
            </tr>
            <!-- Buy Box -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Buy Box (rang)</td>
              <template x-for="p in compareProducts()" :key="'bb-'+p.id">
                <td class="p-2 tabular-nums" :class="isBest(p,'emag_buy_button_rank') ? 'bg-emerald-50 text-emerald-800 font-bold rounded' : 'text-slate-700'">
                  <span x-text="(p.emag_buy_button_rank!==null&&p.emag_buy_button_rank!==undefined&&p.emag_buy_button_rank!=='') ? ('#'+p.emag_buy_button_rank) : '—'"></span>
                </td>
              </template>
            </tr>
            <!-- Nr. oferte -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Nr. oferte</td>
              <template x-for="p in compareProducts()" :key="'no-'+p.id">
                <td class="p-2 tabular-nums text-slate-700" x-text="(p.emag_number_of_offers!==null&&p.emag_number_of_offers!==undefined&&p.emag_number_of_offers!=='') ? p.emag_number_of_offers : '—'"></td>
              </template>
            </tr>
            <!-- Stare eMAG -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Stare eMAG</td>
              <template x-for="p in compareProducts()" :key="'st-'+p.id">
                <td class="p-2 text-xs text-slate-700" x-text="emagStatusLabel(p.emag_offer_status)"></td>
              </template>
            </tr>
            <!-- Categorie -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Categorie</td>
              <template x-for="p in compareProducts()" :key="'ca-'+p.id">
                <td class="p-2 text-xs text-slate-700" x-text="(p.cat_custom_name || p.cat_name || p.category) || '—'"></td>
              </template>
            </tr>
            <!-- EAN -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">EAN</td>
              <template x-for="p in compareProducts()" :key="'ea-'+p.id">
                <td class="p-2 font-mono text-[11px] text-slate-600" x-text="p.ean || '—'"></td>
              </template>
            </tr>
            <!-- PNK -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">PNK</td>
              <template x-for="p in compareProducts()" :key="'pk-'+p.id">
                <td class="p-2 font-mono text-[11px] text-slate-600" x-text="p.pnk || '—'"></td>
              </template>
            </tr>
            <!-- Link -->
            <tr>
              <td class="sticky left-0 bg-white p-2 text-xs font-medium text-slate-500">Link eMAG</td>
              <template x-for="p in compareProducts()" :key="'li-'+p.id">
                <td class="p-2">
                  <a x-show="p.pnk" :href="emagUrl(p)" target="_blank" rel="noopener" class="inline-flex items-center gap-1 text-xs text-brand-600 hover:underline">
                    <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
                    Vezi
                  </a>
                  <span x-show="!p.pnk" class="text-slate-300 text-xs">—</span>
                </td>
              </template>
            </tr>
          </tbody>
        </table>
      </div>

      <div class="px-6 py-3 border-t border-slate-100 flex justify-end">
        <button type="button" @click="compareOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg transition">Închide</button>
      </div>
    </div>
  </div>

  <!-- ============== APPLY TEMPLATE MODAL ============== -->
  <div x-show="applyOpen" x-cloak @keydown.escape.window="applyOpen = false"
       x-data="{ _bg: false }"
       @mousedown.self="_bg = true"
       @mouseup="if (_bg && $event.target === $el) applyOpen = false; _bg = false"
       class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm">
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col">
      <div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
        <div>
          <h3 class="font-semibold text-slate-900">Aplică șablon</h3>
          <p class="text-xs text-slate-500 mt-0.5">Va seta <code>custom_name</code> pe <strong x-text="selected.length"></strong> produse selectate.</p>
        </div>
        <button type="button" @click="applyOpen = false" class="text-slate-400 hover:text-slate-600 p-1">
          <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
        </button>
      </div>

      <div class="p-6 space-y-3 border-b border-slate-100">
        <div class="grid grid-cols-1 md:grid-cols-3 gap-3">
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Alege șablon salvat</label>
            <select x-model="applyTemplateId" @change="onTemplateSelect()"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-brand-500">
              <option value="">— sau introdu manual —</option>
              <template x-for="t in nameTemplates" :key="t.id">
                <option :value="t.id" x-text="t.name"></option>
              </template>
            </select>
          </div>
          <div class="md:col-span-2">
            <label class="block text-xs font-medium text-slate-700 mb-1">Template *</label>
            <input type="text" x-model="applyTemplate" @input.debounce.300ms="refreshPreview()"
              placeholder="ex: Husa silicon pentru {brand} {model} {series}"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
        </div>
        <div x-show="applyMissingPhone > 0" class="bg-amber-50 border border-amber-200 rounded-lg px-3 py-2 text-xs text-amber-800">
          ⚠ <strong x-text="applyMissingPhone"></strong> produse nu sunt legate la o serie de telefon — placeholderii {brand}/{model}/{series} vor fi goi pentru ele.
        </div>
      </div>

      <!-- Preview list -->
      <div class="flex-1 overflow-y-auto px-6 py-4">
        <template x-if="applyBusy && applyPreview.length === 0">
          <div class="text-center py-8 text-sm text-slate-400">Calculez preview…</div>
        </template>
        <template x-if="!applyBusy && applyPreview.length === 0 && applyTemplate.trim()">
          <div class="text-center py-8 text-sm text-slate-400">Apasă „Preview" sau modifică template.</div>
        </template>
        <template x-if="!applyTemplate.trim()">
          <div class="text-center py-8 text-sm text-slate-400">Introdu un template ca să vezi preview.</div>
        </template>
        <template x-if="applyPreview.length > 0">
          <table class="w-full text-xs">
            <thead class="text-[10px] uppercase tracking-wide text-slate-500">
              <tr class="border-b border-slate-100">
                <th class="px-2 py-2 text-left font-medium w-1/2">Nume curent</th>
                <th class="px-2 py-2 text-left font-medium">Nume nou (după aplicare)</th>
              </tr>
            </thead>
            <tbody class="divide-y divide-slate-100">
              <template x-for="r in applyPreview" :key="r.id">
                <tr :class="r.warning ? 'bg-amber-50/40' : ''">
                  <td class="px-2 py-2 text-slate-500 break-words" x-text="r.current_name"></td>
                  <td class="px-2 py-2 text-slate-900 font-medium break-words">
                    <span x-text="r.new_name"></span>
                    <span x-show="r.warning" class="text-amber-600 text-[10px] ml-1" title="Lipsesc date de telefon">⚠</span>
                  </td>
                </tr>
              </template>
            </tbody>
          </table>
        </template>
      </div>

      <div class="px-6 py-4 border-t border-slate-100 bg-slate-50 flex items-center justify-end gap-2 rounded-b-2xl">
        <button type="button" @click="applyOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-lg transition">Anulează</button>
        <button type="button" @click="refreshPreview()" :disabled="applyBusy || !applyTemplate.trim()"
          class="px-4 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 rounded-lg transition disabled:opacity-50">
          🔄 Refresh preview
        </button>
        <button type="button" @click="confirmApply()" :disabled="applyBusy || !applyTemplate.trim() || applyPreview.length === 0"
          class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-emerald-600 hover:bg-emerald-700 rounded-lg shadow-soft transition disabled:opacity-50">
          <span x-show="applyBusy" class="spinner"></span>
          Aplică pe <span x-text="selected.length"></span> produse
        </button>
      </div>
    </div>
  </div>

  <!-- ============== ADD TO SESSION MODAL ============== -->
  <div x-show="addSessionOpen" x-cloak @keydown.escape.window="addSessionOpen = false"
       x-data="{ _bg: false }"
       @mousedown.self="_bg = true"
       @mouseup="if (_bg && $event.target === $el) addSessionOpen = false; _bg = false"
       class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm">
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-lg">
      <form @submit.prevent="submitAddSession()">
        <div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
          <div>
            <h3 class="font-semibold text-slate-900">Adaugă în sesiune titluri</h3>
            <p class="text-xs text-slate-500 mt-0.5"><strong x-text="selected.length"></strong> produse vor fi pregătite pentru push la eMAG.</p>
          </div>
          <button type="button" @click="addSessionOpen = false" class="text-slate-400 hover:text-slate-600 p-1">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
          </button>
        </div>
        <div class="p-6 space-y-4">
          <!-- Toggle existing vs new -->
          <div class="flex gap-2 border border-slate-200 rounded-lg p-1 bg-slate-50">
            <button type="button" @click="addSessionMode = 'existing'" :disabled="sessionsList.length === 0"
              class="flex-1 px-3 py-1.5 text-xs font-medium rounded transition disabled:opacity-50"
              :class="addSessionMode === 'existing' ? 'bg-white shadow text-slate-900' : 'text-slate-600 hover:text-slate-800'">
              Sesiune existentă (<span x-text="sessionsList.length"></span>)
            </button>
            <button type="button" @click="addSessionMode = 'new'"
              class="flex-1 px-3 py-1.5 text-xs font-medium rounded transition"
              :class="addSessionMode === 'new' ? 'bg-white shadow text-slate-900' : 'text-slate-600 hover:text-slate-800'">
              Sesiune nouă
            </button>
          </div>

          <template x-if="addSessionMode === 'existing'">
            <div>
              <label class="block text-xs font-medium text-slate-700 mb-1">Sesiune</label>
              <select x-model="addSessionExistingId" required
                class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
                <template x-for="s in sessionsList" :key="s.id">
                  <option :value="s.id" x-text="s.name + ' · ' + s.status.toUpperCase() + ' · ' + s.items_count + ' items'"></option>
                </template>
              </select>
              <p class="text-xs text-slate-500 mt-1">Poți adăuga și în sesiuni completed sau failed — vor primi noile produse ca pending și statusul se recalculează.</p>
            </div>
          </template>
          <template x-if="addSessionMode === 'new'">
            <div>
              <label class="block text-xs font-medium text-slate-700 mb-1">Nume sesiune nouă *</label>
              <input type="text" x-model="addSessionNewName" required maxlength="255"
                placeholder="ex: Rebranding huse — Mai 2026"
                class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
            </div>
          </template>

          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Titlul nou (inițial)</label>
            <select x-model="addSessionTitleMode"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
              <option value="custom_if_set">Titlul curent afișat (custom_name dacă există, altfel cel eMAG)</option>
              <option value="manual">Gol — îl voi completa manual în sesiune</option>
            </select>
            <p class="text-xs text-slate-500 mt-1">Vei putea edita fiecare titlu individual în pagina sesiunii înainte de trimitere.</p>
          </div>
        </div>
        <div class="px-6 py-4 border-t border-slate-100 bg-slate-50 flex items-center justify-end gap-2 rounded-b-2xl">
          <button type="button" @click="addSessionOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-lg transition">Anulează</button>
          <button type="submit" :disabled="addSessionBusy"
            class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 rounded-lg shadow-soft transition disabled:opacity-50">
            <span x-show="addSessionBusy" class="spinner"></span>
            <span x-text="addSessionBusy ? 'Adaug…' : 'Adaugă'"></span>
          </button>
        </div>
      </form>
    </div>
  </div>

  <!-- ============== PHONE PICKER MODAL (multi-select) ============== -->
  <div x-show="pickerOpen" x-cloak @keydown.escape.window="pickerOpen = false"
       x-data="{ _bg: false }"
       @mousedown.self="_bg = true"
       @mouseup="if (_bg && $event.target === $el) pickerOpen = false; _bg = false"
       class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm">
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-3xl max-h-[90vh] flex flex-col">
      <div class="px-6 py-4 border-b border-slate-100 flex items-start justify-between gap-3">
        <div class="min-w-0 flex-1">
          <div class="text-[11px] uppercase tracking-wide text-slate-500 font-medium">Legare modele telefon pentru</div>
          <div class="text-base font-semibold text-slate-900 mt-0.5 break-words" x-text="pickerProductName"></div>
          <div class="text-[11px] text-slate-400 font-mono mt-0.5" x-show="pickerProductEan">EAN: <span x-text="pickerProductEan"></span></div>
          <p class="text-[11px] text-slate-500 mt-2 pt-2 border-t border-slate-100">
            <strong class="text-amber-600">★ Principal</strong> = folosit în șabloane pentru {brand}/{model}/{series}.
            Restul = modele compatibile (alternative).
          </p>
        </div>
        <button type="button" @click="pickerOpen = false" class="text-slate-400 hover:text-slate-600 p-1 flex-shrink-0">
          <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
        </button>
      </div>

      <!-- Search + summary -->
      <div class="px-6 py-3 border-b border-slate-100 space-y-2">
        <div class="relative">
          <svg class="w-4 h-4 absolute left-3 top-2.5 text-slate-400" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
          <input type="text" x-model="pickerSearch" placeholder="Caută model..."
            class="w-full pl-9 pr-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
        </div>
        <div class="flex items-center gap-2 text-xs flex-wrap" x-show="pickerPrimaryId || pickerSecondaryIds.length > 0">
          <span class="font-medium text-slate-600">Selectate:</span>
          <template x-for="label in selectedSeriesNames()" :key="label">
            <span class="px-2 py-0.5 bg-amber-100 text-amber-800 rounded-full" x-text="label"></span>
          </template>
          <button @click="clearPicker()" class="text-xs text-slate-500 hover:text-rose-600 underline">șterge tot</button>
        </div>
      </div>

      <!-- Tree grupat brand → model → serii -->
      <div class="flex-1 overflow-y-auto px-6 py-4">
        <template x-if="groupedSeries().length === 0">
          <div class="text-center py-8 text-sm text-slate-400">
            <template x-if="pickerSearch">Niciun rezultat pentru „<span x-text="pickerSearch"></span>".</template>
            <template x-if="!pickerSearch">Niciun model definit. <a href="<?= url('phone-models.php') ?>" class="text-brand-600 hover:underline">Adaugă</a>.</template>
          </div>
        </template>
        <template x-for="brand in groupedSeries()" :key="brand.id">
          <div class="mb-4">
            <div class="text-xs uppercase tracking-wide font-semibold text-slate-500 mb-1.5" x-text="brand.name"></div>
            <template x-for="model in brand.models" :key="model.id">
              <div class="ml-2 mb-2">
                <div class="text-[11px] text-slate-400 mb-1" x-text="model.name"></div>
                <div class="ml-2 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-1">
                  <template x-for="s in model.series" :key="s.id">
                    <div class="flex items-center gap-1 px-2 py-1 rounded hover:bg-slate-50 group"
                         :class="{
                           'bg-amber-50': pickerPrimaryId === s.id,
                           'bg-sky-50': isSecondary(s.id)
                         }">
                      <button @click="setPrimary(s.id)" type="button"
                              class="text-base leading-none w-5 transition"
                              :class="pickerPrimaryId === s.id ? 'text-amber-500' : 'text-slate-300 hover:text-amber-400'"
                              :title="pickerPrimaryId === s.id ? 'Demarchează principal' : 'Marchează ca principal'">
                        <span x-show="pickerPrimaryId === s.id">★</span>
                        <span x-show="pickerPrimaryId !== s.id">☆</span>
                      </button>
                      <label class="flex-1 flex items-center gap-1.5 cursor-pointer text-sm select-none">
                        <input type="checkbox" :checked="isSecondary(s.id) || pickerPrimaryId === s.id"
                               @change="toggleSecondary(s.id)"
                               class="rounded border-slate-300 text-brand-600 focus:ring-brand-500">
                        <span x-text="s.name"></span>
                      </label>
                    </div>
                  </template>
                </div>
              </div>
            </template>
          </div>
        </template>
      </div>

      <div class="px-6 py-4 border-t border-slate-100 bg-slate-50 flex items-center justify-between gap-2 rounded-b-2xl">
        <div class="text-xs text-slate-500 min-w-0 flex-1">
          <template x-if="autoSuggestion">
            <div class="text-emerald-700 bg-emerald-50 border border-emerald-200 rounded px-2 py-1 text-[11px] inline-block toast-enter" x-text="autoSuggestion"></div>
          </template>
          <template x-if="!autoSuggestion">
            <span>
              <strong x-text="(pickerPrimaryId ? 1 : 0) + pickerSecondaryIds.length"></strong> selectate
              <span x-show="pickerPrimaryId" class="text-amber-600 ml-2">(★ 1 principal)</span>
              <span x-show="pickerSecondaryIds.length > 0" class="text-sky-600 ml-2">(<span x-text="pickerSecondaryIds.length"></span> compatibile)</span>
            </span>
          </template>
        </div>
        <div class="flex gap-2">
          <button type="button" @click="pickerOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-lg transition">Anulează</button>
          <button type="button" @click="savePhonePicker()" :disabled="pickerSaving"
            class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 rounded-lg shadow-soft transition disabled:opacity-50">
            <span x-show="pickerSaving" class="spinner"></span>
            <span x-text="pickerSaving ? 'Salvez…' : 'Salvează'"></span>
          </button>
        </div>
      </div>
    </div>
  </div>

  <!-- ============== EDIT MODAL ============== -->
  <div
    x-show="modalOpen"
    x-cloak
    x-data="{ _bg: false }"
    @keydown.escape.window="modalOpen = false"
    @mousedown.self="_bg = true"
    @mouseup="if (_bg && $event.target === $el) modalOpen = false; _bg = false"
    class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm"
  >
    <div class="modal-enter bg-white rounded-2xl shadow-2xl w-full max-w-2xl">
      <form @submit.prevent="saveEdit()">
        <div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
          <div>
            <h3 class="font-semibold text-slate-900" x-text="editing.id ? 'Editare produs' : 'Produs nou'"></h3>
            <p class="text-xs text-slate-500 mt-0.5">Câmpurile marcate cu * sunt obligatorii.</p>
          </div>
          <button type="button" @click="modalOpen = false" class="text-slate-400 hover:text-slate-600 p-1">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
          </button>
        </div>

        <div class="p-6 grid grid-cols-1 md:grid-cols-2 gap-4 max-h-[60vh] overflow-y-auto">
          <div class="md:col-span-2">
            <label class="block text-xs font-medium text-slate-700 mb-1">Nume produs *</label>
            <input type="text" x-model="editing.product_name" required maxlength="500"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">EAN</label>
            <input type="text" x-model="editing.ean" maxlength="20"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">PNK</label>
            <input type="text" x-model="editing.pnk" maxlength="100"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Cod Oblio</label>
            <input type="text" x-model="editing.cod_oblio" maxlength="100"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Nr. bucăți / cutie</label>
            <input type="number" x-model="editing.units_per_box" min="0" step="1"
              placeholder="ex: 12"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
            <p class="text-xs text-slate-500 mt-1">Editat manual — NU se suprascrie la sync eMAG.</p>
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">eMAG ID</label>
            <input type="text" x-model="editing.emag_id" maxlength="100"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Cod produs (PN)</label>
            <input type="text" x-model="editing.emag_part_number" maxlength="100"
              placeholder="ex: EA_S25ULTRA_2B_V3"
              class="w-full px-3 py-2 text-sm font-mono border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
            <p class="text-xs text-slate-500 mt-1">SKU intern (în eMAG: <code>part_number</code>).</p>
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Categorie</label>
            <input type="text" x-model="editing.category" maxlength="255"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Cont eMAG</label>
            <select x-model="editing.emag_account_id" class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
              <option value="">— Niciun cont —</option>
              <?php foreach ($accounts as $acc): ?>
                <option value="<?= (int)$acc['id'] ?>"><?= e($acc['account_name']) ?></option>
              <?php endforeach; ?>
            </select>
          </div>
          <div>
            <label class="block text-xs font-medium text-slate-700 mb-1">Status</label>
            <select x-model="editing.status" class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white">
              <option value="draft">Draft</option>
              <option value="active">Active</option>
              <option value="synced">Synced</option>
            </select>
          </div>
          <!-- Phone link (cascade) -->
          <div class="md:col-span-2 border-t border-slate-100 pt-4">
            <div class="text-xs font-semibold uppercase tracking-wide text-slate-500 mb-2">📱 Legare la model telefon</div>
            <div class="grid grid-cols-3 gap-2">
              <select x-model="editingBrandId" @change="onBrandChange()"
                class="px-3 py-2 text-sm border border-slate-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-brand-500">
                <option value="">— Brand —</option>
                <template x-for="b in phoneBrands" :key="b.id">
                  <option :value="b.id" x-text="b.name"></option>
                </template>
              </select>
              <select x-model="editingModelId" @change="onModelChange()" :disabled="!editingBrandId"
                class="px-3 py-2 text-sm border border-slate-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-brand-500 disabled:bg-slate-50">
                <option value="">— Model —</option>
                <template x-for="m in modelsForBrand()" :key="m.id">
                  <option :value="m.id" x-text="m.name"></option>
                </template>
              </select>
              <select x-model="editing.phone_series_id" :disabled="!editingModelId"
                class="px-3 py-2 text-sm border border-slate-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-brand-500 disabled:bg-slate-50">
                <option value="">— Serie —</option>
                <template x-for="s in seriesForModel()" :key="s.id">
                  <option :value="s.id" x-text="s.name"></option>
                </template>
              </select>
            </div>
            <p class="text-xs text-slate-500 mt-1">Folosit pentru placeholders {brand} {model} {series} în șabloane.</p>
          </div>

          <!-- Custom name -->
          <div class="md:col-span-2">
            <label class="block text-xs font-medium text-slate-700 mb-1">Nume custom (afișat în loc de cel eMAG)</label>
            <input type="text" x-model="editing.custom_name" maxlength="500"
              placeholder="Lasă gol pentru a folosi numele eMAG; sau aplică un șablon din bulk action"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500">
            <p class="text-xs text-slate-500 mt-1">Păstrat permanent — NU se suprascrie la sync.</p>
          </div>

          <div class="md:col-span-2">
            <label class="block text-xs font-medium text-slate-700 mb-1">Notes</label>
            <textarea x-model="editing.notes" rows="3"
              class="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500"></textarea>
          </div>
        </div>

        <div class="px-6 py-4 border-t border-slate-100 bg-slate-50 flex items-center justify-end gap-2 rounded-b-2xl">
          <button type="button" @click="modalOpen = false" class="px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-lg transition">Anulează</button>
          <button type="submit" :disabled="saving"
            class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 rounded-lg shadow-soft transition disabled:opacity-50">
            <span x-show="saving" class="spinner"></span>
            <span x-text="saving ? 'Salvez…' : 'Salvează'"></span>
          </button>
        </div>
      </form>
    </div>
  </div>

</div>

<script>
const APP_VAT_RATE = <?= json_encode(vat_rate()) ?>; // cota TVA (%) pentru calcul preț cu TVA
const PRODUCTS_COLUMNS_KEY = 'products.columnsV5';
const PRODUCTS_DEFAULT_COLUMNS = [
  { key: 'ean',              label: 'EAN',              visible: true,  mono: true },
  { key: 'product_name',     label: 'Nume produs',      visible: true,  mono: false },
  { key: 'pnk',              label: 'PNK',              visible: true,  mono: true },
  { key: 'linked_pnk',       label: 'PNK legat',        visible: true,  mono: false },
  { key: 'emag_part_number', label: 'Cod produs (PN)',  visible: true,  mono: true },
  { key: 'cod_oblio',        label: 'Cod Oblio',        visible: true,  mono: true },
  { key: 'units_per_box',    label: 'Nr. buc',          visible: true,  mono: false },
  { key: 'phone_link',       label: 'Telefon',          visible: true,  mono: false },
  { key: 'emag_id',          label: 'eMAG ID',          visible: true,  mono: true },
  { key: 'emag_link',        label: 'Link eMAG',        visible: true,  mono: false },
  { key: 'category',         label: 'Categorie',        visible: false, mono: false },
  { key: 'account_name',     label: 'Cont',             visible: true,  mono: false },
  { key: 'status',           label: 'Status',           visible: true,  mono: false },
  { key: 'emag_offer_status',label: 'Stare eMAG',       visible: true,  mono: false },
  { key: 'price',                label: 'Preț fără TVA', visible: true,  mono: false },
  { key: 'price_vat',            label: 'Preț cu TVA',   visible: true,  mono: false },
  { key: 'hotness',              label: 'Performanță', visible: true,  mono: false },
  { key: 'reviews',              label: 'Recenzii',    visible: true,  mono: false },
  { key: 'emag_buy_button_rank', label: 'Buy Box',     visible: false, mono: false },
  { key: 'emag_number_of_offers',label: 'Oferte',      visible: false, mono: false },
  { key: 'updated_at',       label: 'Modificat',        visible: false, mono: false },
];

function productsTable({ rows, startIndex, csrf, phoneBrands, phoneModels, phoneSeries, nameTemplates, sessionsList }) {
  return {
    rows,
    startIndex,
    csrf,
    phoneBrands: phoneBrands || [],
    phoneModels: phoneModels || [],
    phoneSeries: phoneSeries || [],
    nameTemplates: nameTemplates || [],
    sessionsList: sessionsList || [],
    // Add-to-session modal
    addSessionOpen: false,
    addSessionMode: 'existing',   // 'existing' | 'new'
    addSessionExistingId: '',
    addSessionNewName: '',
    addSessionTitleMode: 'custom_if_set',
    addSessionBusy: false,
    selected: [],
    columns: [],
    showColumnsMenu: false,
    reviewsBusy: false,
    reviewsProgress: '',
    compareOpen: false,
    roleSaving: [],
    exportOpen: false,
    exportCols: [],
    exportScope: 'all', // 'all' = toate produsele filtrate · 'page' = doar pagina curentă
    exportBusy: false,
    modalOpen: false,
    saving: false,
    editing: {},
    // Cascade state pentru phone selector în modal
    editingBrandId: null,
    editingModelId: null,
    // Apply template modal
    applyOpen: false,
    applyTemplate: '',
    applyTemplateId: '',
    applyPreview: [],
    applyMissingPhone: 0,
    applyBusy: false,
    editingUnits: null,    // ID-ul produsului în edit-mode pentru units_per_box
    editingUnitsValue: '', // valoarea din input
    copiedNameId: null,    // ID-ul produsului al cărui nume a fost copiat (pentru badge "✓ copiat")
    // Phone picker (multi-select)
    pickerOpen: false,
    pickerProductId: null,
    pickerProductName: '',
    pickerProductEan: '',
    pickerPrimaryId: null,
    pickerSecondaryIds: [],
    pickerSearch: '',
    pickerSaving: false,

    init() {
      this.columns = this.loadColumns();
      this.$nextTick(() => this.initSortable());
    },

    loadColumns() {
      try {
        const saved = JSON.parse(localStorage.getItem(PRODUCTS_COLUMNS_KEY) || 'null');
        if (!Array.isArray(saved)) return [...PRODUCTS_DEFAULT_COLUMNS];

        // Reconstruim păstrând ordinea din storage și adăugând coloane noi la final
        const result = [];
        const seen = new Set();
        for (const s of saved) {
          const def = PRODUCTS_DEFAULT_COLUMNS.find(d => d.key === s.key);
          if (def) {
            result.push({ ...def, visible: !!s.visible });
            seen.add(s.key);
          }
        }
        for (const def of PRODUCTS_DEFAULT_COLUMNS) {
          if (!seen.has(def.key)) result.push({ ...def });
        }
        return result;
      } catch {
        return [...PRODUCTS_DEFAULT_COLUMNS];
      }
    },

    saveColumns() {
      localStorage.setItem(PRODUCTS_COLUMNS_KEY,
        JSON.stringify(this.columns.map(c => ({ key: c.key, visible: c.visible })))
      );
    },

    visibleColumns() {
      return this.columns.filter(c => c.visible);
    },

    isMono(key) {
      const c = this.columns.find(c => c.key === key);
      return c ? !!c.mono : false;
    },


    toggleColumn(key) {
      const col = this.columns.find(c => c.key === key);
      if (col) {
        col.visible = !col.visible;
        this.saveColumns();
      }
    },

    resetColumns() {
      localStorage.removeItem(PRODUCTS_COLUMNS_KEY);
      this.columns = [...PRODUCTS_DEFAULT_COLUMNS];
      this.$nextTick(() => this.initSortable());
    },

    initSortable() {
      const el = this.$refs.colList;
      if (!el || !window.Sortable) return;
      // Distruge instance veche dacă există (la resetColumns)
      if (el._sortable) { el._sortable.destroy(); }
      el._sortable = Sortable.create(el, {
        animation: 150,
        ghostClass: 'bg-brand-50',
        onEnd: (evt) => {
          const moved = this.columns.splice(evt.oldIndex, 1)[0];
          this.columns.splice(evt.newIndex, 0, moved);
          this.saveColumns();
        }
      });
    },

    formatDate(s) {
      if (!s) return '—';
      const d = new Date(s.replace(' ', 'T'));
      return d.toLocaleDateString('ro-RO') + ' ' + d.toLocaleTimeString('ro-RO', {hour:'2-digit', minute:'2-digit'});
    },

    // Eticheta pentru statusul ofertei eMAG (1=activ, 0=inactiv, 2=end of life).
    emagStatusLabel(v) {
      if (v === null || v === undefined || v === '') return '—';
      if (v == 1) return 'Activ';
      if (v == 0) return 'Inactiv';
      if (v == 2) return 'EOL';
      return String(v);
    },

    // Setează produsul `p` ca PRINCIPAL în comparația curentă; celelalte produse
    // comparate devin SECUNDARE. Re-click pe principal → resetează toate la neutru.
    async setComparePrimary(p) {
      if (this.roleSaving.includes(p.id)) return;
      const group = this.compareProducts();
      const ids = group.map(x => x.id);
      const primaryId = (p.compare_role === 'principal') ? 0 : p.id; // toggle off dacă deja principal
      this.roleSaving.push(p.id);
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('primary_id', primaryId);
        ids.forEach(id => fd.append('ids[]', id));
        const r = await fetch('<?= url('api/set-primary.php') ?>', { method: 'POST', body: fd });
        const d = await r.json();
        if (!d.ok) throw new Error(d.error || 'Eroare');
        // Sincronizez reactiv TOATE rândurile afectate (inclusiv variantele de model
        // propagate — ex: 1 buc / 2 buc — care pot fi și în afara comparației).
        const byId = {};
        (d.affected || []).forEach(a => { byId[a.id] = a.role; });
        this.rows.forEach(row => {
          if (Object.prototype.hasOwnProperty.call(byId, row.id)) row.compare_role = byId[row.id];
        });
      } catch (e) {
        alert('Nu am putut salva rolul: ' + e.message);
      } finally {
        this.roleSaving = this.roleSaving.filter(id => id !== p.id);
      }
    },

    // Setează/anulează favorit (principal) direct din tabel, fără comparație.
    // Reutilizează set-primary cu un singur produs → setează principal + propagă
    // la variantele aceluiași model de telefon (1 buc / 2 buc). Re-click → anulează.
    async toggleFavoriteRow(p) {
      if (this.roleSaving.includes(p.id)) return;
      const makeFav = (p.compare_role !== 'principal');
      const primaryId = makeFav ? p.id : 0;
      this.roleSaving.push(p.id);
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('primary_id', primaryId);
        fd.append('ids[]', p.id);
        const r = await fetch('<?= url('api/set-primary.php') ?>', { method: 'POST', body: fd });
        const d = await r.json();
        if (!d.ok) throw new Error(d.error || 'Eroare');
        const byId = {};
        (d.affected || []).forEach(a => { byId[a.id] = a.role; });
        this.rows.forEach(row => {
          if (Object.prototype.hasOwnProperty.call(byId, row.id)) row.compare_role = byId[row.id];
        });
      } catch (e) {
        alert('Nu am putut salva favoritul: ' + e.message);
      } finally {
        this.roleSaving = this.roleSaving.filter(id => id !== p.id);
      }
    },

    // Click pe un PNK legat → filtrează tabelul la acel PNK (folosește căutarea).
    showLinkedPnk(pnk) {
      if (!pnk) return;
      const url = location.pathname + '?q=' + encodeURIComponent(pnk.trim());
      if (window.ajaxNav) window.ajaxNav(url);
      else window.location = url;
    },

    // --- Comparație produse selectate (totul client-side, din rows) ---
    openCompare() {
      if (this.selected.length < 2) { alert('Selectează cel puțin 2 produse pentru comparație.'); return; }
      this.compareOpen = true;
    },
    compareProducts() {
      // Checkbox-urile produc id-uri ca string; rows au id number → comparăm pe string.
      const ids = this.selected.map(x => String(x));
      // păstrează ordinea din tabel
      return this.rows.filter(r => ids.includes(String(r.id)));
    },
    _cmpNum(v) {
      if (v === null || v === undefined || v === '') return null;
      const n = Number(v);
      return isNaN(n) ? null : n;
    },
    // Returnează „cea mai bună" valoare dintre produsele comparate pentru un câmp.
    // Preț + buy_button_rank: mai mic = mai bun. Rating + recenzii: mai mare = mai bun.
    compareBest(field) {
      const lowerBetter = (field === 'current_sale_price' || field === 'emag_buy_button_rank');
      const vals = this.compareProducts().map(p => this._cmpNum(p[field])).filter(v => v !== null);
      if (!vals.length) return null;
      return lowerBetter ? Math.min(...vals) : Math.max(...vals);
    },
    isBest(p, field) {
      if (this.compareProducts().length < 2) return false;
      const v = this._cmpNum(p[field]);
      if (v === null) return false;
      const best = this.compareBest(field);
      return best !== null && v === best;
    },

    // Extragere recenzii (rating + nr.) de pe paginile publice eMAG, în loturi
    // throttled. Reapelez endpointul până când has_more === false, apoi reîncarc
    // tabelul. ATENȚIE: scraping — lent și cu risc de blocare la volum mare.
    async updateReviews() {
      if (this.reviewsBusy) return;
      if (!confirm('Extrag recenziile de pe paginile publice eMAG pentru toate produsele cu PNK?\n\nE lent (o cerere per produs, throttled ~1.5s) și, la volum mare, eMAG îți poate bloca temporar IP-ul. Continui?')) return;
      this.reviewsBusy = true;
      this.reviewsProgress = 'Pornesc extragerea recenziilor…';
      let runStart = '';
      let done = 0, withReviews = 0, failed = 0, safety = 2000;
      try {
        while (safety-- > 0) {
          const fd = new FormData();
          fd.append('csrf_token', this.csrf);
          fd.append('limit', '3');
          if (runStart) fd.append('run_start', runStart);
          const r = await fetch('<?= url('api/reviews-sync.php') ?>', { method: 'POST', body: fd });
          const data = await r.json();
          if (!data.ok) throw new Error(data.error || 'Eroare necunoscută');
          runStart     = data.run_start;
          done        += data.processed;
          withReviews += data.stats.with_reviews;
          failed      += data.stats.failed;
          this.reviewsProgress = `Procesate ${done} produse · ${withReviews} cu recenzii · ${failed} eșuate · rămase ~${data.remaining}`;
          if (!data.has_more) break;
        }
        this.reviewsProgress = `✓ Gata: ${done} produse, ${withReviews} cu recenzii, ${failed} eșuate. Reîncarc tabelul…`;
        if (window.ajaxNav) { await window.ajaxNav(location.href, false); }
        else { window.location.reload(); }
      } catch (e) {
        this.reviewsProgress = '✗ Eroare: ' + e.message;
      } finally {
        this.reviewsBusy = false;
      }
    },

    // Construiește URL-ul produsului pe eMAG: /<nume-slug>/pd/<PNK>
    emagSlug(name) {
      return String(name || '')
        .normalize('NFD').replace(/[̀-ͯ]/g, '') // elimină diacriticele
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')   // orice non-alfanumeric -> „-”
        .replace(/^-+|-+$/g, '')        // taie „-” de la capete
        || 'produs';
    },
    emagUrl(p) {
      return 'https://www.emag.ro/' + this.emagSlug(p.product_name) + '/pd/' + encodeURIComponent(p.pnk);
    },

    // Culoare badge pentru performanța produsului (hotness eMAG)
    hotnessClass(v) {
      const k = String(v || '').toUpperCase().replace(/\s+/g, ' ').trim();
      switch (k) {
        case 'SUPER HOT': return 'bg-rose-100 text-rose-700';
        case 'HOT':       return 'bg-orange-100 text-orange-700';
        case 'WARM':      return 'bg-amber-100 text-amber-700';
        case 'COLD':      return 'bg-sky-100 text-sky-700';
        case 'SUPER COLD':return 'bg-blue-100 text-blue-700';
        default:          return 'bg-slate-100 text-slate-600';
      }
    },

    // --- Activare / dezactivare ofertă pe eMAG (click pe badge) ---
    offerStatusSending: [],
    isSettingStatus(id) { return this.offerStatusSending.includes(id); },
    async toggleOfferStatus(p) {
      if (!p.emag_id) { alert('Produsul nu are emag_id — nu poate fi modificat pe eMAG.'); return; }
      if (this.isSettingStatus(p.id)) return;
      // Țintă: activează (1) dacă acum e inactiv/EOL/necunoscut; altfel dezactivează (0).
      const target = (p.emag_offer_status == 1) ? 0 : 1;
      if (target === 0 && !confirm('Dezactivezi oferta „' + (p.custom_name || p.product_name || '').slice(0, 60) + '" pe eMAG?')) return;
      this.offerStatusSending.push(p.id);
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('id', p.id);
        fd.append('status', target);
        const r = await fetch('<?= url('api/set-offer-status.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error || 'Eroare');
        p.emag_offer_status = data.emag_offer_status;
      } catch (e) {
        alert('Eroare modificare stare: ' + e.message);
      } finally {
        this.offerStatusSending = this.offerStatusSending.filter(x => x !== p.id);
      }
    },

    // Inline edit pentru units_per_box (Nr. buc).
    startEditUnits(p) {
      this.editingUnits = p.id;
      this.editingUnitsValue = (p.units_per_box ?? '') + '';
    },
    async commitEditUnits(p) {
      if (this.editingUnits !== p.id) return;
      const raw = this.editingUnitsValue.trim();
      const newVal = raw === '' ? null : Math.max(0, parseInt(raw, 10) || 0);
      const oldVal = p.units_per_box;
      this.editingUnits = null;

      // Optimistic update + early return dacă nu s-a schimbat
      if ((newVal ?? '') === (oldVal ?? '')) return;
      p.units_per_box = newVal;

      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        Object.entries(p).forEach(([k, v]) => {
          // Trimit doar câmpurile care există în save-product (skip cele JOIN-ed)
          if (k.startsWith('cat_') || k === 'account_name') return;
          fd.append(k, v ?? '');
        });
        const r = await fetch('<?= url('api/save-product.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error);
        // Sincronizare câmpuri de pe server
        Object.assign(p, data.product);
      } catch (e) {
        p.units_per_box = oldVal; // rollback
        alert('Eroare salvare: ' + e.message);
      }
    },

    // Copy nume produs (container cu structură internă) — folosește badge separat,
    // nu rescrie textContent ca să nu distrugă layout-ul split head/rest.
    async copyName(evt, value) {
      if (!value) return;
      try {
        if (navigator.clipboard && window.isSecureContext) {
          await navigator.clipboard.writeText(value);
        } else {
          const ta = document.createElement('textarea');
          ta.value = value; ta.style.position = 'fixed'; ta.style.left = '-9999px';
          document.body.appendChild(ta); ta.select();
          document.execCommand('copy');
          document.body.removeChild(ta);
        }
        const p = this.rows.find(r => (r.custom_name || r.product_name) === value);
        if (p) {
          this.copiedNameId = p.id;
          setTimeout(() => { if (this.copiedNameId === p.id) this.copiedNameId = null; }, 1000);
        }
      } catch (e) {
        console.warn('Copy failed', e);
      }
    },

    // Copy-to-clipboard cu feedback vizual scurt pe elementul click-uit.
    async copyCell(evt, value) {
      const el = evt.currentTarget;
      try {
        if (navigator.clipboard && window.isSecureContext) {
          await navigator.clipboard.writeText(value);
        } else {
          // Fallback pentru HTTP / browsere vechi
          const ta = document.createElement('textarea');
          ta.value = value; ta.style.position = 'fixed'; ta.style.left = '-9999px';
          document.body.appendChild(ta); ta.select();
          document.execCommand('copy');
          document.body.removeChild(ta);
        }
        const original = el.textContent;
        el.textContent = '✓ copiat';
        el.classList.add('!bg-emerald-100', '!text-emerald-700');
        setTimeout(() => {
          el.textContent = original;
          el.classList.remove('!bg-emerald-100', '!text-emerald-700');
        }, 900);
      } catch (e) {
        console.warn('Copy failed', e);
      }
    },

    toggleAll(e) {
      this.selected = e.target.checked ? this.rows.map(p => p.id) : [];
    },

    openEdit(p) {
      this.editing = JSON.parse(JSON.stringify(p));
      if (this.editing.emag_account_id === null) this.editing.emag_account_id = '';
      if (this.editing.phone_series_id === null) this.editing.phone_series_id = '';
      if (this.editing.custom_name === null) this.editing.custom_name = '';
      // Resolve cascade start state din phone_series_id
      this.editingBrandId = null;
      this.editingModelId = null;
      if (this.editing.phone_series_id) {
        const s = this.phoneSeries.find(x => x.id == this.editing.phone_series_id);
        if (s) {
          this.editingModelId = s.model_id;
          const m = this.phoneModels.find(x => x.id == s.model_id);
          if (m) this.editingBrandId = m.brand_id;
        }
      }
      this.modalOpen = true;
    },

    // Cascade helpers
    modelsForBrand() {
      if (!this.editingBrandId) return [];
      return this.phoneModels.filter(m => m.brand_id == this.editingBrandId)
        .sort((a,b) => a.name.localeCompare(b.name, 'ro', {numeric: true}));
    },
    seriesForModel() {
      if (!this.editingModelId) return [];
      return this.phoneSeries.filter(s => s.model_id == this.editingModelId)
        .sort((a,b) => a.name.localeCompare(b.name, 'ro', {numeric: true}));
    },
    onBrandChange() {
      this.editingModelId = null;
      this.editing.phone_series_id = '';
    },
    onModelChange() {
      this.editing.phone_series_id = '';
    },

    // ---------- Add to title session ----------
    openAddSession() {
      if (!this.selected.length) return;
      this.addSessionMode = this.sessionsList.length > 0 ? 'existing' : 'new';
      this.addSessionExistingId = this.sessionsList[0]?.id || '';
      this.addSessionNewName = '';
      this.addSessionTitleMode = 'custom_if_set';
      this.addSessionOpen = true;
    },

    async submitAddSession() {
      this.addSessionBusy = true;
      try {
        let sessionId = this.addSessionExistingId;

        // Dacă mod „new", creez sesiunea întâi
        if (this.addSessionMode === 'new') {
          if (!this.addSessionNewName.trim()) throw new Error('Numele sesiunii este obligatoriu.');
          const fd1 = new FormData();
          fd1.append('csrf_token', this.csrf);
          fd1.append('name', this.addSessionNewName);
          const r1 = await fetch('<?= url('api/save-session.php') ?>', { method: 'POST', body: fd1 });
          const d1 = await r1.json();
          if (!d1.ok) throw new Error(d1.error);
          sessionId = d1.session.id;
          this.sessionsList.unshift({ id: d1.session.id, name: d1.session.name, status: 'draft' });
        }

        if (!sessionId) throw new Error('Trebuie să alegi sau să creezi o sesiune.');

        // Adaug items
        const fd2 = new FormData();
        fd2.append('csrf_token', this.csrf);
        fd2.append('session_id', sessionId);
        fd2.append('new_title_mode', this.addSessionTitleMode);
        this.selected.forEach(id => fd2.append('product_ids[]', id));

        const r2 = await fetch('<?= url('api/add-to-session.php') ?>', { method: 'POST', body: fd2 });
        const d2 = await r2.json();
        if (!d2.ok) throw new Error(d2.error);

        this.addSessionOpen = false;
        this.selected = [];

        const goNow = confirm(`✓ ${d2.added} produse adăugate (${d2.skipped} deja existau).\n\nMergi la sesiune acum?`);
        if (goNow) {
          window.location.href = '<?= url('title-session.php') ?>?id=' + sessionId;
        }
      } catch (e) {
        alert('Eroare: ' + e.message);
      } finally {
        this.addSessionBusy = false;
      }
    },

    // ---------- Phone picker (multi-select cu primary) ----------
    altIds(p) {
      if (!p.alt_series_ids) return [];
      return p.alt_series_ids.split(',').map(s => parseInt(s, 10)).filter(Boolean);
    },

    // Returnează numele complete ale seriilor secundare pentru afișare în tabel.
    altLabels(p) {
      return this.altIds(p)
        .map(id => {
          const s = this.phoneSeries.find(x => x.id === id);
          if (!s) return null;
          return this.fullSeriesLabel(s);
        })
        .filter(Boolean);
    },

    openPhonePicker(p) {
      this.pickerProductId = p.id;
      this.pickerProductName = p.custom_name || p.product_name || '(fără nume)';
      this.pickerProductEan = p.ean || '';
      this.pickerPrimaryId = p.phone_series_id ? parseInt(p.phone_series_id, 10) : null;
      this.pickerSecondaryIds = this.altIds(p);
      this.pickerSearch = '';
      this.pickerOpen = true;
    },

    // Grupare ierarhică pentru afișare în picker
    groupedSeries() {
      const q = this.pickerSearch.trim().toLowerCase();
      const result = [];
      for (const b of this.phoneBrands) {
        const models = this.phoneModels
          .filter(m => m.brand_id === b.id)
          .map(m => {
            const seriesForModel = this.phoneSeries
              .filter(s => s.model_id === m.id)
              .map(s => ({ ...s, _label: `${b.name} ${m.name} ${s.name}` }))
              .filter(s => !q || s._label.toLowerCase().includes(q));
            return { ...m, series: seriesForModel };
          })
          .filter(m => m.series.length > 0)
          .sort((a, b) => a.name.localeCompare(b.name, 'ro', { numeric: true }));
        if (models.length > 0) result.push({ ...b, models });
      }
      return result.sort((a, b) => a.name.localeCompare(b.name, 'ro'));
    },

    isSecondary(seriesId) {
      return this.pickerSecondaryIds.includes(seriesId);
    },

    toggleSecondary(seriesId) {
      // Dacă e primary, nu pot fi și secondary
      if (this.pickerPrimaryId === seriesId) {
        this.pickerPrimaryId = null;
      }
      const idx = this.pickerSecondaryIds.indexOf(seriesId);
      if (idx >= 0) this.pickerSecondaryIds.splice(idx, 1);
      else this.pickerSecondaryIds.push(seriesId);
    },

    autoSuggestion: '', // notificare scurtă afișată în footer modal

    async setPrimary(seriesId) {
      const isUnsetting = this.pickerPrimaryId === seriesId;
      this.pickerPrimaryId = isUnsetting ? null : seriesId;
      const idx = this.pickerSecondaryIds.indexOf(seriesId);
      if (idx >= 0) this.pickerSecondaryIds.splice(idx, 1);
      this.autoSuggestion = '';

      if (isUnsetting || !seriesId) return;

      // Auto-sugerare: pe baza altor produse care au această serie ca primary/secondary,
      // colectăm celelalte serii ale lor și le adăugăm ca secondaries (additive — nu scoatem nimic).
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('series_id', seriesId);
        if (this.pickerProductId) fd.append('exclude_product_id', this.pickerProductId);

        const r = await fetch('<?= url('api/related-series.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok || !Array.isArray(data.related)) return;

        const newOnes = data.related.filter(rid =>
          rid !== this.pickerPrimaryId && !this.pickerSecondaryIds.includes(rid)
        );
        if (newOnes.length === 0) return;

        newOnes.forEach(rid => this.pickerSecondaryIds.push(rid));
        this.autoSuggestion = `✨ ${newOnes.length} ${newOnes.length === 1 ? 'compatibilă sugerată' : 'compatibile sugerate'} pe baza altor produse`;
        // Mesajul dispare după 4s
        setTimeout(() => { this.autoSuggestion = ''; }, 4000);
      } catch (e) {
        // Silent fail — fără sugestii e ok
      }
    },

    clearPicker() {
      this.pickerPrimaryId = null;
      this.pickerSecondaryIds = [];
    },

    selectedSeriesNames() {
      const out = [];
      if (this.pickerPrimaryId) {
        const s = this.phoneSeries.find(x => x.id === this.pickerPrimaryId);
        if (s) out.push('★ ' + this.fullSeriesLabel(s));
      }
      for (const sid of this.pickerSecondaryIds) {
        const s = this.phoneSeries.find(x => x.id === sid);
        if (s) out.push(this.fullSeriesLabel(s));
      }
      return out;
    },

    fullSeriesLabel(s) {
      const m = this.phoneModels.find(x => x.id === s.model_id);
      const b = m ? this.phoneBrands.find(x => x.id === m.brand_id) : null;
      return [b?.name, m?.name, s.name].filter(Boolean).join(' ');
    },

    async savePhonePicker() {
      this.pickerSaving = true;
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('product_id', this.pickerProductId);
        if (this.pickerPrimaryId) fd.append('primary_series_id', this.pickerPrimaryId);
        this.pickerSecondaryIds.forEach(id => fd.append('secondary_ids[]', id));

        const r = await fetch('<?= url('api/save-phone-link.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error);

        // Update local row
        const row = this.rows.find(x => x.id === this.pickerProductId);
        if (row) {
          row.phone_series_id   = data.primary?.id || null;
          row.phone_brand_name  = data.primary?.brand_name  || null;
          row.phone_model_name  = data.primary?.model_name  || null;
          row.phone_series_name = data.primary?.series_name || null;
          row.alt_series_ids    = data.alt_ids.length ? data.alt_ids.join(',') : null;
        }
        this.pickerOpen = false;
      } catch (e) {
        alert('Eroare: ' + e.message);
      } finally {
        this.pickerSaving = false;
      }
    },

    // ---------- Apply template (bulk) ----------
    openApply() {
      if (!this.selected.length) return;
      this.applyTemplate = '';
      this.applyTemplateId = '';
      this.applyPreview = [];
      this.applyMissingPhone = 0;
      this.applyOpen = true;
    },

    onTemplateSelect() {
      const t = this.nameTemplates.find(x => x.id == this.applyTemplateId);
      if (t) this.applyTemplate = t.template;
      this.refreshPreview();
    },

    async refreshPreview() {
      if (!this.applyTemplate.trim() || !this.selected.length) {
        this.applyPreview = [];
        return;
      }
      this.applyBusy = true;
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('action', 'preview');
        fd.append('template', this.applyTemplate);
        this.selected.forEach(id => fd.append('ids[]', id));
        const r = await fetch('<?= url('api/apply-template.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error);
        this.applyPreview = data.results;
        this.applyMissingPhone = data.missing_phone;
      } catch (e) {
        alert('Eroare preview: ' + e.message);
      } finally {
        this.applyBusy = false;
      }
    },

    async confirmApply() {
      if (!this.applyTemplate.trim() || !this.selected.length) return;
      this.applyBusy = true;
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        fd.append('action', 'apply');
        fd.append('template', this.applyTemplate);
        this.selected.forEach(id => fd.append('ids[]', id));
        const r = await fetch('<?= url('api/apply-template.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error);

        // Update local rows cu noile custom_name
        data.results.forEach(res => {
          const row = this.rows.find(r => r.id == res.id);
          if (row) row.custom_name = res.new_name;
        });
        this.applyOpen = false;
        this.selected = [];
        alert(`✓ ${data.updated} produse actualizate.`);
      } catch (e) {
        alert('Eroare apply: ' + e.message);
      } finally {
        this.applyBusy = false;
      }
    },

    async saveEdit() {
      this.saving = true;
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        Object.entries(this.editing).forEach(([k, v]) => fd.append(k, v ?? ''));
        const r = await fetch('<?= url('api/save-product.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error || 'Eroare necunoscută');

        const idx = this.rows.findIndex(x => x.id == data.product.id);
        if (idx >= 0) this.rows[idx] = data.product;
        // dacă e produs nou, nu-l adăugăm aici (e oricum în pagina 1 sortată după updated_at — la refresh va apărea)
        this.modalOpen = false;
      } catch (e) {
        alert('Eroare: ' + e.message);
      } finally {
        this.saving = false;
      }
    },

    async deleteOne(p) {
      if (!confirm(`Ștergi „${p.product_name}"?`)) return;
      await this._delete([p.id]);
    },

    async bulkDelete() {
      if (!this.selected.length) return;
      if (!confirm(`Ștergi ${this.selected.length} produse?`)) return;
      await this._delete(this.selected);
    },

    async _delete(ids) {
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        ids.forEach(id => fd.append('ids[]', id));
        const r = await fetch('<?= url('api/delete-product.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error || 'Eroare');
        this.rows = this.rows.filter(p => !ids.includes(p.id));
        this.selected = [];
      } catch (e) {
        alert('Eroare ștergere: ' + e.message);
      }
    },

    async excludeOne(p) {
      if (!p.ean) { alert('Produsul nu are EAN — folosește Șterge.'); return; }
      const reason = prompt(`Exclude EAN „${p.ean}" (${p.product_name})?\n\nMotiv (opțional):`, '');
      if (reason === null) return;
      await this._exclude([p.id], reason || '');
    },

    async bulkExclude() {
      if (!this.selected.length) return;
      const withEan = this.rows.filter(p => this.selected.includes(p.id) && p.ean);
      const noEan   = this.selected.length - withEan.length;
      if (withEan.length === 0) { alert('Niciun produs selectat nu are EAN.'); return; }
      const msg = `Exclude ${withEan.length} produse după EAN?` +
                  (noEan > 0 ? `\n\n${noEan} fără EAN vor fi ignorate.` : '');
      const reason = prompt(msg + '\n\nMotiv (opțional):', '');
      if (reason === null) return;
      await this._exclude(withEan.map(p => p.id), reason || '');
    },

    async _exclude(ids, reason) {
      try {
        const fd = new FormData();
        fd.append('csrf_token', this.csrf);
        if (reason) fd.append('reason', reason);
        ids.forEach(id => fd.append('ids[]', id));
        const r = await fetch('<?= url('api/exclude.php') ?>', { method: 'POST', body: fd });
        const data = await r.json();
        if (!data.ok) throw new Error(data.error);
        this.rows = this.rows.filter(p => !ids.includes(p.id));
        this.selected = [];
        let m = `✓ ${data.excluded_added} EAN-uri excluse, ${data.products_deleted} produse șterse.`;
        if (data.already_excluded > 0) m += `\n${data.already_excluded} erau deja excluse.`;
        if (data.skipped_no_ean > 0)   m += `\n${data.skipped_no_ean} fără EAN ignorate.`;
        alert(m);
      } catch (e) {
        alert('Eroare exclude: ' + e.message);
      }
    },

    // --- Export Excel (.xlsx) cu selecție de coloane ---
    openExport() {
      // Lista de coloane = coloanele tabelului; bifate implicit = cele vizibile.
      this.exportCols = this.columns.map(c => ({ key: c.key, label: c.label, checked: c.visible }));
      this.exportScope = 'all';
      this.exportOpen = true;
      this.$nextTick(() => this.initExportSortable());
    },
    // Drag-and-drop pentru ordinea coloanelor în export (la fel ca meniul „Coloane").
    initExportSortable() {
      const el = document.getElementById('exportColList');
      if (!el || !window.Sortable) return;
      if (el._sortable) el._sortable.destroy();
      el._sortable = Sortable.create(el, {
        animation: 150,
        handle: '.exp-drag',
        ghostClass: 'bg-emerald-50',
        onEnd: (evt) => {
          if (evt.oldIndex === evt.newIndex) return;
          const moved = this.exportCols.splice(evt.oldIndex, 1)[0];
          this.exportCols.splice(evt.newIndex, 0, moved);
        }
      });
    },
    exportToggleAll(val) { this.exportCols.forEach(c => c.checked = val); },
    exportSelectedCount() { return this.exportCols.filter(c => c.checked).length; },

    // Valoarea „curată" pentru export, per coloană (rezolvă coloanele calculate).
    cellExportValue(key, p) {
      switch (key) {
        case 'product_name':      return p.custom_name || p.product_name || '';
        case 'linked_pnk':        return p.linked_pnks || '';
        case 'price':             return (p.current_sale_price != null && p.current_sale_price !== '') ? Number(Number(p.current_sale_price).toFixed(2)) : '';
        case 'price_vat':         return (p.current_sale_price != null && p.current_sale_price !== '') ? Number((Number(p.current_sale_price) * (1 + APP_VAT_RATE/100)).toFixed(2)) : '';
        case 'hotness':           return p.hotness || '';
        case 'reviews':           return (p.emag_reviews_count != null && p.emag_reviews_count !== '') ? ((p.emag_rating ? Number(p.emag_rating).toFixed(2) : '') + (p.emag_reviews_count ? ' (' + p.emag_reviews_count + ')' : '')) : '';
        case 'emag_offer_status': return this.emagStatusLabel(p.emag_offer_status);
        case 'emag_link':         return p.pnk ? this.emagUrl(p) : '';
        case 'phone_link':        return [p.phone_brand_name, p.phone_model_name, p.phone_series_name].filter(Boolean).join(' ');
        case 'category':          return p.cat_custom_name || p.cat_name || p.category || '';
        case 'updated_at':        return p.updated_at ? this.formatDate(p.updated_at) : '';
        default:                  return (p[key] != null) ? p[key] : '';
      }
    },

    async buildExportRows() {
      if (this.exportScope === 'page') return this.rows;
      // 'all' → toate produsele filtrate, luate de pe server (doar citire)
      const url = location.pathname + location.search + (location.search ? '&' : '?') + 'export=json';
      const r = await fetch(url, { credentials: 'same-origin' });
      const d = await r.json();
      if (!d.ok) throw new Error(d.error || 'Eroare la preluarea datelor');
      return d.rows || [];
    },

    async doExportExcel() {
      if (typeof XLSX === 'undefined') { alert('Biblioteca de export nu s-a încărcat încă. Reîncearcă în câteva secunde.'); return; }
      const cols = this.exportCols.filter(c => c.checked);
      if (!cols.length) { alert('Selectează cel puțin o coloană.'); return; }
      this.exportBusy = true;
      try {
        const rows = await this.buildExportRows();
        const header = ['#', ...cols.map(c => c.label)];
        const aoa = [header, ...rows.map((p, i) => [i + 1, ...cols.map(c => this.cellExportValue(c.key, p))])];
        const ws = XLSX.utils.aoa_to_sheet(aoa);
        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, 'Produse');
        XLSX.writeFile(wb, 'produse.xlsx');
        this.exportOpen = false;
      } catch (e) {
        alert('Export eșuat: ' + e.message);
      } finally {
        this.exportBusy = false;
      }
    },

    exportCsv() {
      const cols = this.visibleColumns();
      const header = ['#', ...cols.map(c => c.label)];
      const rows = [header];
      this.rows.forEach((p, i) => {
        const r = [this.startIndex + i];
        cols.forEach(c => {
          let v = p[c.key];
          if (c.key === 'updated_at') v = this.formatDate(v);
          r.push(v ?? '');
        });
        rows.push(r);
      });
      const csv = rows.map(r => r.map(c => `"${String(c).replace(/"/g,'""')}"`).join(',')).join('\n');
      const blob = new Blob(['﻿' + csv], { type: 'text/csv;charset=utf-8' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url; a.download = 'produse-pagina-<?= $page ?>.csv'; a.click();
      URL.revokeObjectURL(url);
    }
  };
}
</script>

<?php require __DIR__ . '/includes/footer.php'; ?>
