<template>
  <div :class="['app-root', themeClass, densityClass, layoutClass]" :style="rootStyle">
    <header class="app-header">
        <div class="flex w-full items-center justify-between gap-4 px-6 py-4">
          <div class="flex items-center gap-3">
            <div class="h-10 w-10 rounded-full bg-[var(--accent)] text-black flex items-center justify-center vintage-shadow">
              <i class="fa-solid fa-wave-square"></i>
            </div>
            <div>
              <div class="section-title text-lg">PriceWatch</div>
              <div class="label">Vintage control deck</div>
            </div>
          </div>
          <div class="label">FE v{{ frontendVersion }}</div>
          <div class="label">BE v{{ backendVersion }}</div>
          <div class="relative">
            <input
              class="input pr-10"
              placeholder="Rechercher"
              v-model="searchQuery"
            />
            <i class="fa-solid fa-magnifying-glass absolute right-3 top-3 text-[var(--muted)]"></i>
          </div>
          <button
            class="icon-btn view-mode-btn"
            :class="{ 'active-view': viewMode === 'cards' }"
            title="Vue cartes"
            @click="viewMode = 'cards'"
          >
            <i class="fa-solid fa-border-all"></i>
          </button>
          <button
            class="icon-btn view-mode-btn"
            :class="{ 'active-view': viewMode === 'table' }"
            title="Vue tableau"
            @click="viewMode = 'table'"
          >
            <i class="fa-solid fa-table"></i>
          </button>
          <button class="icon-btn" title="Ajouter un produit" @click="openAddProduct">
            <i class="fa-solid fa-plus"></i>
          </button>
          <button class="icon-btn" title="Parametres" @click="toggleSettings">
            <i class="fa-solid fa-gear"></i>
          </button>
          <button class="icon-btn" title="Recharger" @click="reloadPage">
            <i class="fa-solid fa-rotate"></i>
          </button>
          <button class="icon-btn" title="Theme" @click="cycleTheme">
            <i class="fa-solid fa-circle-half-stroke"></i>
          </button>
          <div class="pill">{{ apiStatus }}</div>
        </div>
    </header>

    <div class="flex w-full gap-6 px-6 py-6">
      <aside class="sidebar">
        <div class="panel p-4 vintage-shadow">
          <div class="section-title text-base mb-3">Filtres & tri</div>
          <div class="space-y-3">
            <div>
              <div class="label">Store</div>
              <select class="input" v-model="filters.store">
                <option value="all">Tous</option>
                <option v-for="store in stores" :key="store.id" :value="store.id">
                  {{ store.name }}
                </option>
              </select>
            </div>
            <div>
              <div class="label">Stock</div>
              <select class="input" v-model="filters.stock">
                <option value="all">Tous</option>
                <option value="in_stock">En stock</option>
                <option value="out_of_stock">Rupture</option>
                <option value="unknown">Inconnu</option>
              </select>
            </div>
            <div class="grid grid-cols-2 gap-2">
              <div>
                <div class="label">Prix min</div>
                <input class="input" type="number" v-model.number="filters.priceMin" />
              </div>
              <div>
                <div class="label">Prix max</div>
                <input class="input" type="number" v-model.number="filters.priceMax" />
              </div>
            </div>
            <div>
              <div class="label">Tri</div>
              <select class="input" v-model="filters.sort">
                <option value="updated">Derniere maj</option>
                <option value="price">Prix actuel</option>
                <option value="delta">Variation</option>
                <option value="stock">Stock</option>
              </select>
            </div>
            <div>
              <div class="label">Ordre du tri</div>
              <select class="input" v-model="filters.sortOrder">
                <option value="desc">Décroissant</option>
                <option value="asc">Croissant</option>
              </select>
            </div>
          </div>
        </div>

        <div class="panel mt-4 p-4 vintage-shadow">
          <div class="section-title text-base mb-3">Comparaison</div>
          <div class="flex items-center justify-between">
            <div class="label">{{ compareIds.length }} selectionnes</div>
            <button class="icon-btn" title="Activer comparaison" @click="compareMode = !compareMode">
              <i class="fa-solid fa-code-compare"></i>
            </button>
          </div>
          <div class="label mt-3">Split view 2-4 produits</div>
        </div>
      </aside>

      <main class="flex-1">
        <div class="panel p-4 mb-4 vintage-shadow">
          <div class="flex items-center justify-between">
            <div>
              <div class="section-title text-base">Catalogue</div>
              <div class="label">Dense layout + vintage glow</div>
            </div>
            <div class="label text-sm">Choix de vue déplacé dans l'entête</div>
          </div>
        </div>

        <div v-if="apiState.error" class="panel p-3 mb-4">
          <div class="label">Erreur API: {{ apiState.error }}</div>
        </div>

        <section v-if="compareMode" class="compare-grid">
          <div v-for="product in compareProducts" :key="product.id" class="card p-4">
            <div class="flex items-center justify-between">
              <div>
                <div class="section-title text-base">{{ product.title }}</div>
                <div class="label">{{ storeLabel(product.storeId) }}</div>
              </div>
              <div class="pill">{{ product.stockStatus }}</div>
            </div>
            <div class="mt-3 text-xl">{{ formatPrice(product.price, product.currency) }}</div>
            <div class="label">Delta: {{ product.delta }}%</div>
            <div class="mt-3 h-20 rounded-lg bg-[var(--surface-2)]"></div>
          </div>
        </section>

        <section v-else-if="viewMode === 'cards'" class="product-grid">
          <div
            v-for="product in filteredProducts"
            :key="product.id"
            class="card p-4"
            :class="{ 'card-accent': product.delta < 0 }"
          >
            <div class="flex items-center justify-between">
              <div class="section-title text-base">{{ product.title }}</div>
              <button class="icon-btn" title="Voir detail" @click="selectProduct(product.id)">
                <i class="fa-solid fa-eye"></i>
              </button>
            </div>
            <div class="mt-3 flex items-center gap-3">
              <picture>
                <source :srcset="product.imageWebp" type="image/webp" />
                <source :srcset="product.imageJpg" type="image/jpeg" />
                <img
                  :src="product.imageJpg"
                  class="h-16 w-16 rounded-lg object-cover border border-white/10"
                  alt="vignette"
                  loading="lazy"
                />
              </picture>
              <div>
                <div class="text-xl">{{ formatPrice(product.price, product.currency) }}</div>
                <div class="label">{{ storeLabel(product.storeId) }} - {{ product.reference }}</div>
              </div>
            </div>
            <div class="mt-3 flex items-center justify-between">
              <div class="pill">{{ product.stockStatus }}</div>
              <div class="label">Delta {{ product.delta }}%</div>
            </div>
            <div class="mt-4 flex items-center gap-2">
              <button class="icon-btn" title="Comparer" @click="toggleCompare(product.id)">
                <i class="fa-solid" :class="compareIds.includes(product.id) ? 'fa-square-check' : 'fa-square'" ></i>
              </button>
              <button class="icon-btn" title="Supprimer" @click="deleteProduct(product.id)">
                <i class="fa-solid fa-trash"></i>
              </button>
              <button class="icon-btn" title="Relancer le scrap" @click="refreshProduct(product.id)">
                <i class="fa-solid fa-rotate"></i>
              </button>
              <button class="icon-btn" title="Ouvrir dans un nouvel onglet" @click="openProductUrl(product)">
                <i class="fa-solid fa-up-right-from-square"></i>
              </button>
            </div>
          </div>
        </section>

        <section v-else class="panel p-4">
          <div class="section-title text-base mb-3">Tableau dense</div>
          <div class="overflow-auto">
            <table class="w-full text-sm">
              <thead class="text-left text-[var(--muted)]">
                <tr>
                  <th class="pb-2">Produit</th>
                  <th class="pb-2">Store</th>
                  <th class="pb-2">Prix</th>
                  <th class="pb-2">Delta</th>
                  <th class="pb-2">Stock</th>
                  <th class="pb-2">Actions</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="product in filteredProducts" :key="product.id" class="border-t border-white/5">
                  <td class="py-3">{{ product.title }}</td>
                  <td class="py-3">{{ storeLabel(product.storeId) }}</td>
                  <td class="py-3">{{ formatPrice(product.price, product.currency) }}</td>
                  <td class="py-3">{{ product.delta }}%</td>
                  <td class="py-3">{{ product.stockStatus }}</td>
                  <td class="py-3">
                    <div class="flex items-center gap-2">
                      <button class="icon-btn" title="Detail" @click="selectProduct(product.id)">
                        <i class="fa-solid fa-eye"></i>
                      </button>
                      <button class="icon-btn" title="Comparer" @click="toggleCompare(product.id)">
                        <i class="fa-solid" :class="compareIds.includes(product.id) ? 'fa-square-check' : 'fa-square'" ></i>
                      </button>
                      <button class="icon-btn" title="Supprimer" @click="deleteProduct(product.id)">
                        <i class="fa-solid fa-trash"></i>
                      </button>
                      <button class="icon-btn" title="Relancer le scrap" @click="refreshProduct(product.id)">
                        <i class="fa-solid fa-rotate"></i>
                      </button>
                      <button class="icon-btn" title="Ouvrir dans un nouvel onglet" @click="openProductUrl(product)">
                        <i class="fa-solid fa-up-right-from-square"></i>
                      </button>
                    </div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </section>
      </main>
    </div>

    <div v-if="settingsOpen" class="fixed inset-0 bg-black/40 z-50" @click.self="toggleSettings">
      <div class="absolute right-0 top-0 h-full w-full max-w-md panel p-6 overflow-auto">
        <div class="flex items-center justify-between mb-4">
          <div class="section-title text-lg">Parametres</div>
          <button class="icon-btn" title="Fermer" @click="toggleSettings">
            <i class="fa-solid fa-xmark"></i>
          </button>
        </div>

        <div class="space-y-6">
          <section>
            <div class="section-title text-sm mb-2">Apparence</div>
            <div class="space-y-3">
              <div>
                <div class="label">Theme</div>
                <select class="input" v-model="theme">
                  <option value="gruvbox-dark">Gruvbox Dark</option>
                  <option value="gruvbox-light">Gruvbox Light</option>
                  <option value="monokai-dark">Monokai Dark</option>
                  <option value="monokai-light">Monokai Light</option>
                </select>
              </div>
              <div>
                <div class="label">Densite</div>
                <select class="input" v-model="density">
                  <option value="dense">Dense</option>
                  <option value="comfort">Confort</option>
                </select>
              </div>
              <div>
                <div class="label">Layout</div>
                <select class="input" v-model="layoutMode">
                  <option value="auto">Auto</option>
                  <option value="compact">Compact</option>
                  <option value="wide">Large</option>
                </select>
              </div>
              <div>
                <div class="label">Police titre</div>
                <select class="input" v-model="settings.fontTitle">
                  <option v-for="font in fontPresets" :key="font.id" :value="font.id">
                    {{ font.label }}
                  </option>
                </select>
              </div>
              <div>
                <div class="label">Police corps</div>
                <select class="input" v-model="settings.fontBody">
                  <option v-for="font in fontPresets" :key="font.id" :value="font.id">
                    {{ font.label }}
                  </option>
                </select>
              </div>
              <div>
                <div class="label">Police mono</div>
                <select class="input" v-model="settings.fontMono">
                  <option v-for="font in fontPresets" :key="font.id" :value="font.id">
                    {{ font.label }}
                  </option>
                </select>
              </div>
              <div>
                <div class="label">Taille texte</div>
                <div class="flex items-center gap-2">
                  <input class="input" type="range" min="12" max="20" step="1" v-model.number="settings.fontSize" />
                  <div class="label min-w-[44px] text-right">{{ settings.fontSize }}px</div>
                </div>
              </div>
              <div>
                <div class="label">Taille icones</div>
                <select class="input" v-model="settings.iconSize">
                  <option value="sm">Compact</option>
                  <option value="md">Normal</option>
                  <option value="lg">Large</option>
                </select>
              </div>
            </div>
          </section>

          <section>
            <div class="section-title text-sm mb-2">Donnees</div>
            <div class="space-y-3">
              <div>
                <div class="label">Frequence rafraichissement</div>
                <input class="input" v-model.number="settings.refreshMinutes" type="number" min="1" />
              </div>
              <div>
                <div class="label">Limite produits page</div>
                <input class="input" v-model.number="settings.pageSize" type="number" min="10" />
              </div>
              <div>
                <div class="label">Preferer WebP</div>
                <select class="input" v-model="settings.preferWebp">
                  <option :value="true">Oui</option>
                  <option :value="false">Non</option>
                </select>
              </div>
            </div>
          </section>

          <section>
            <div class="section-title text-sm mb-2">API</div>
            <div class="space-y-3">
              <div>
                <div class="label">Base URL</div>
                <input class="input" v-model="settings.apiBase" placeholder="/api" />
              </div>
              <div>
                <div class="label">Token</div>
                <input class="input" type="password" v-model="settings.apiToken" placeholder="Bearer token" />
              </div>
              <button class="input text-left" @click="fetchProducts">
                Recharger les donnees
              </button>
            </div>
          </section>

          <section>
            <div class="section-title text-sm mb-2">Stores</div>
            <div class="space-y-2">
              <div v-for="store in stores" :key="store.id" class="space-y-2 border border-white/5 rounded-lg p-2">
                <div class="label">Nom</div>
                <input class="input" v-model="store.name" />
                <div class="label">Domaine</div>
                <input class="input" v-model="store.domain" />
                <div class="flex justify-end">
                  <button class="icon-btn" title="Supprimer" @click="removeStore(store.id)">
                    <i class="fa-solid fa-trash"></i>
                  </button>
                </div>
              </div>
              <div class="mt-3">
                <div class="label">Ajouter un store</div>
                <div class="flex gap-2">
                  <input class="input" v-model="storeDraftName" placeholder="Nom" />
                  <input class="input" v-model="storeDraftDomain" placeholder="Domaine" />
                  <button class="icon-btn" title="Ajouter" @click="addStore">
                    <i class="fa-solid fa-plus"></i>
                  </button>
                </div>
              </div>
            </div>
          </section>

          <section>
            <div class="section-title text-sm mb-2">Debug</div>
            <div class="space-y-2">
              <div class="flex items-center justify-between">
                <div class="label">Afficher erreurs scraping</div>
                <input type="checkbox" v-model="settings.showDebug" />
              </div>
              <div class="flex items-center justify-between">
                <div class="label">Animations</div>
                <input type="checkbox" v-model="settings.enableAnimations" />
              </div>
            </div>
          </section>

          <section>
            <div class="section-title text-sm mb-2">Maintenance</div>
            <div class="space-y-2">
              <button class="input text-left" @click="applySettingsAndReload">
                Appliquer et recharger
              </button>
              <button class="input text-left" @click="reloadPage">
                Recharger la page
              </button>
              <button class="input text-left" @click="clearCacheAndReload">
                Vider le cache et recharger
              </button>
            </div>
          </section>
        </div>
      </div>
    </div>

    <div v-if="addProductOpen" class="fixed inset-0 bg-black/40 z-50 flex items-center justify-center p-4" @click.self="closeAddProduct">
      <div class="w-full max-w-2xl panel p-6 overflow-auto">
        <div class="flex items-center justify-between mb-4">
          <div class="section-title text-lg">Ajouter un produit</div>
          <button class="icon-btn" title="Fermer" @click="closeAddProduct">
            <i class="fa-solid fa-xmark"></i>
          </button>
        </div>
        <div class="space-y-3">
          <input class="input" v-model="addProductDraft.url" placeholder="URL" />
          <select class="input" v-model="addProductDraft.usePlaywright">
            <option :value="false">HTTP uniquement</option>
            <option :value="true">Playwright fallback</option>
          </select>
          <button class="input text-left" @click="previewAddProduct" :disabled="!addProductDraft.url || addProductState.loading">
            {{ addProductState.loading ? "Analyse en cours..." : "Ajouter" }}
          </button>
          <div v-if="addProductState.error" class="label">Erreur: {{ addProductState.error }}</div>

          <div v-if="addProductState.snapshot" class="panel p-3 space-y-3">
            <div class="section-title text-sm">Donnees scrappees</div>
            <div class="label">Store: {{ addProductState.snapshot.source }}</div>
            <div class="label">Reference: {{ addProductState.snapshot.reference || "n/a" }}</div>
            <div class="label">Titre: {{ addProductState.snapshot.title || "n/a" }}</div>
            <div class="flex items-start gap-3">
              <picture>
                <source :srcset="addProductImagePreview" type="image/webp" />
                <source :srcset="addProductImagePreview" type="image/jpeg" />
                <img
                  :src="addProductImagePreview"
                  class="h-20 w-20 rounded-lg object-cover border border-white/10"
                  alt="vignette"
                  loading="lazy"
                />
              </picture>
              <div class="flex-1">
                <div class="label">Images: {{ addProductImagesCount }}</div>
                <div v-if="addProductImageThumbs.length" class="mt-3 grid grid-cols-4 gap-2">
                  <button
                    v-for="(image, index) in addProductImageThumbs"
                    :key="`${image}-${index}`"
                    type="button"
                    class="image-toggle"
                    :class="{ selected: selectedImagesSet.has(index) }"
                    @click="toggleAddProductImage(index)"
                  >
                    <picture>
                      <source :srcset="image" type="image/webp" />
                      <img
                        :src="image"
                        class="h-16 w-full object-cover rounded-md"
                        alt="miniature"
                        loading="lazy"
                      />
                    </picture>
                  </button>
                </div>
              </div>
            </div>
            <div class="label">Prix: {{ formatPrice(addProductState.snapshot.price, addProductState.snapshot.currency) }}</div>
            <div class="label">Prix conseille: {{ formatPrice(addProductState.snapshot.msrp, addProductState.snapshot.currency) }}</div>
            <div class="label">Stock: {{ addProductState.snapshot.stock_status || "n/a" }}</div>
            <div class="label">Categorie: {{ addProductState.snapshot.category || "n/a" }}</div>
            <div class="label">Description: {{ addProductState.snapshot.description || "n/a" }}</div>
            <div class="label">Caracteristiques: {{ addProductSpecsCount }}</div>
            <div v-if="addProductSpecEntries.length === 0" class="label">n/a</div>
            <div v-else class="space-y-1">
              <div v-for="[key, value] in addProductSpecEntries" :key="key" class="label">
                {{ key }}: {{ value }}
              </div>
            </div>
            <button class="input text-left" @click="commitAddProduct">
              Enregistrer
            </button>
          </div>
        </div>
      </div>
    </div>

    <div
      v-if="activeProduct"
      class="fixed inset-0 bg-black/40 z-50 flex items-center justify-center p-4"
      @click.self="activeProductId = null"
    >
      <div class="w-full max-w-5xl panel detail-popup p-6 overflow-auto">
        <div class="flex items-center justify-between mb-4">
          <div>
            <div class="section-title text-lg">Détails produit</div>
            <div class="label">{{ activeProduct.title }}</div>
            <div class="label">{{ storeLabel(activeProduct.storeId) }}</div>
          </div>
          <button class="icon-btn" title="Fermer" @click="activeProductId = null">
            <i class="fa-solid fa-xmark"></i>
          </button>
        </div>
        <div class="grid lg:grid-cols-[1fr,1fr] gap-6">
          <div>
            <picture>
              <source :srcset="activeProduct.imageWebp" type="image/webp" />
              <source :srcset="activeProduct.imageJpg" type="image/jpeg" />
              <img
                :src="activeProduct.imageJpg"
                class="w-full rounded-xl object-cover border border-white/10"
                alt="image produit"
                loading="lazy"
              />
            </picture>
            <div class="mt-4 text-xl">{{ formatPrice(activeProduct.price, activeProduct.currency) }}</div>
            <div class="label">Dernière maj: {{ activeProduct.updatedAtLabel }}</div>
            <div class="mt-2 text-sm space-y-1">
              <div class="label">Prix conseillé: {{ formatPrice(activeProduct.msrp, activeProduct.currency) }}</div>
              <div class="label">
                Réduction: {{ formatDiscount(activeProduct.discountAmount, activeProduct.discountPercent) }}
              </div>
            </div>
            <div class="mt-3 flex items-center justify-between gap-3">
              <div class="label break-words text-xs max-w-[250px]">
                URL: {{ activeProduct.url || "n/a" }}
              </div>
              <button
                class="icon-btn"
                title="Copier l'URL"
                :class="!activeProduct.url ? 'opacity-50 pointer-events-none' : ''"
                @click="copyActiveUrl"
              >
                <i class="fa-solid fa-copy"></i>
              </button>
            </div>
          </div>

          <div class="space-y-4">
            <div>
              <div class="section-title text-sm">Description</div>
              <div class="label">{{ activeProduct.description || "n/a" }}</div>
            </div>
            <div>
              <div class="section-title text-sm">Caractéristiques</div>
              <div v-if="Object.keys(activeProduct.specs || {}).length === 0" class="label">n/a</div>
              <div v-else class="space-y-1 max-h-48 overflow-auto pr-2">
                <div v-for="(value, key) in activeProduct.specs" :key="key" class="label">
                  {{ key }}: {{ value }}
                </div>
              </div>
            </div>
            <div>
              <div class="section-title text-sm">Historique</div>
              <div class="mt-2 h-24 rounded-lg bg-[var(--surface-2)]"></div>
            </div>
            <div>
              <div class="section-title text-sm">Edition produit</div>
              <div class="mt-2 space-y-2">
                <input class="input" v-model="activeProduct.title" placeholder="Nom produit" />
                <input class="input" v-model="activeProduct.url" placeholder="URL" />
                <input class="input" v-model="activeProduct.category" placeholder="Catégorie" />
                <input class="input" v-model="activeProduct.currency" placeholder="Devise" />
                <input class="input" v-model="activeProduct.reference" placeholder="Référence" disabled />
                <input class="input" v-model.number="activeProduct.refreshHours" type="number" min="1" placeholder="Fréquence (h)" />
                <div class="flex items-center gap-2">
                  <button class="icon-btn" title="Enregistrer" @click="saveActiveProduct">
                    <i class="fa-solid fa-floppy-disk"></i>
                  </button>
                  <button class="icon-btn" title="Rafraîchir" @click="enqueueActiveProduct">
                    <i class="fa-solid fa-rotate"></i>
                  </button>
                  <button class="icon-btn" title="Supprimer" @click="deleteActiveProduct">
                    <i class="fa-solid fa-trash"></i>
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="fixed bottom-4 left-4 panel p-3 vintage-shadow flex items-center gap-3 log-status-panel">
      <div class="label">API {{ apiStatus }}</div>
      <div class="label">DB {{ apiHealth.db ? "OK" : "KO" }}</div>
      <div class="label">Redis {{ apiHealth.redis ? "OK" : "KO" }}</div>
      <button class="icon-btn" title="Logs" @click="toggleLogs">
        <i class="fa-solid fa-scroll"></i>
      </button>
    </div>

    <div v-if="logsOpen" class="fixed inset-0 bg-black/40 z-50" @click.self="toggleLogs">
      <div class="absolute bottom-0 left-0 w-full max-w-2xl panel p-6 overflow-y-auto max-h-[calc(100vh-120px)]">
        <div class="flex items-center justify-between mb-4">
          <div class="section-title text-lg">Logs</div>
          <button class="icon-btn" title="Fermer" @click="toggleLogs">
            <i class="fa-solid fa-xmark"></i>
          </button>
        </div>
        <div class="flex items-center gap-3 mb-3">
          <button class="icon-btn" title="Frontend" @click="logTab = 'frontend'">
            <i class="fa-solid fa-desktop"></i>
          </button>
          <button class="icon-btn" title="Backend" @click="logTab = 'backend'">
            <i class="fa-solid fa-server"></i>
          </button>
          <button class="icon-btn" title="Uvicorn" @click="logTab = 'uvicorn'">
            <i class="fa-solid fa-terminal"></i>
          </button>
          <button class="icon-btn" title="Erreurs" @click="logTab = 'errors'">
            <i class="fa-solid fa-triangle-exclamation"></i>
          </button>
          <button class="icon-btn" title="Rafraichir" @click="loadLogs">
            <i class="fa-solid fa-rotate"></i>
          </button>
          <button class="icon-btn" title="Copier logs" @click="copyLogs">
            <i class="fa-solid fa-copy"></i>
          </button>
        </div>
        <div class="space-y-2 max-h-[calc(100vh-220px)] overflow-y-auto">
          <div
            v-for="entry in filteredLogs"
            :key="entry.id"
            class="panel p-2 log-entry"
            :class="{ 'log-entry-error': entry.level === 'ERROR' }"
          >
            <div class="label">{{ entry.time }} - {{ entry.source }} - {{ entry.level }}</div>
            <div>{{ entry.message }}</div>
          </div>
          <div v-if="filteredLogs.length === 0" class="label">Aucun log</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed, ref, watch, onMounted, onBeforeUnmount } from "vue";

const theme = ref("gruvbox-dark");
const density = ref("dense");
const viewMode = ref("cards");
const layoutMode = ref("auto");
const compareMode = ref(false);
const settingsOpen = ref(false);
const addProductOpen = ref(false);
const logsOpen = ref(false);
const logTab = ref("frontend");
const searchQuery = ref("");

const settings = ref({
  apiBase: "/api",
  apiToken: "",
  refreshMinutes: 60,
  pageSize: 24,
  preferWebp: true,
  iconSize: "md",
  showDebug: false,
  enableAnimations: true,
  fontTitle: "space-mono",
  fontBody: "jetbrains",
  fontMono: "jetbrains",
  fontSize: 16,
});

const filters = ref({
  store: "all",
  stock: "all",
  priceMin: null,
  priceMax: null,
  sort: "updated",
  sortOrder: "desc",
});

const apiState = ref({
  loading: false,
  error: "",
});

const apiHealth = ref({
  db: false,
  redis: false,
});

const frontendVersion = "0.1.0";
const backendVersion = ref("n/a");

const defaultStores = [
  { id: "amazon", name: "Amazon", domain: "amazon.fr" },
  { id: "cdiscount", name: "Cdiscount", domain: "cdiscount.com" },
  { id: "backmarket", name: "Backmarket", domain: "backmarket.fr" },
];

const stores = ref([...defaultStores]);

const storeLookup = computed(() =>
  Object.fromEntries(stores.value.map((store) => [store.id, store.name]))
);

const products = ref([]);
const productMeta = ref({});
const compareIds = ref([]);
const activeProductId = ref(null);
const storeDraftName = ref("");
const storeDraftDomain = ref("");
const addProductDraft = ref({
  url: "",
  usePlaywright: false,
});

const addProductState = ref({
  loading: false,
  error: "",
  snapshot: null,
});

const addProductSelectedImages = ref([]);

const logEntries = ref([]);
const backendLogs = ref([]);
const uvicornLogs = ref([]);

const fontPresets = [
  {
    id: "jetbrains",
    label: "JetBrains Mono",
    stack: "\"JetBrains Mono\", \"Fira Code\", \"IBM Plex Mono\", \"SFMono-Regular\", Menlo, monospace",
  },
  {
    id: "space-mono",
    label: "Space Mono",
    stack: "\"Space Mono\", \"JetBrains Mono\", \"Fira Code\", monospace",
  },
  {
    id: "ibm-plex",
    label: "IBM Plex Mono",
    stack: "\"IBM Plex Mono\", \"JetBrains Mono\", monospace",
  },
];

const placeholderImage =
  "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><rect width='240' height='240' fill='%233c3836'/><text x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' fill='%23a89984' font-size='14'>No image</text></svg>";

const themeClass = computed(() => `theme-${theme.value}`);
const densityClass = computed(() => `density-${density.value}`);
const layoutClass = computed(() => `layout-${layoutMode.value}`);

const rootStyle = computed(() => {
  const findStack = (id) => fontPresets.find((font) => font.id === id)?.stack;
  return {
    "--font-title": findStack(settings.value.fontTitle) || fontPresets[0].stack,
    "--font-body": findStack(settings.value.fontBody) || fontPresets[0].stack,
    "--font-mono": findStack(settings.value.fontMono) || fontPresets[0].stack,
    "--font-size": `${settings.value.fontSize || 16}px`,
  };
});

const filteredProducts = computed(() => {
  return products.value
    .filter((product) => {
      if (filters.value.store !== "all" && product.storeId !== filters.value.store) {
        return false;
      }
      if (filters.value.stock !== "all" && product.stockStatus !== filters.value.stock) {
        return false;
      }
      if (filters.value.priceMin) {
        if (product.price === null || product.price === undefined) {
          return false;
        }
        if (product.price < filters.value.priceMin) {
          return false;
        }
      }
      if (filters.value.priceMax) {
        if (product.price === null || product.price === undefined) {
          return false;
        }
        if (product.price > filters.value.priceMax) {
          return false;
        }
      }
      if (searchQuery.value && !product.title.toLowerCase().includes(searchQuery.value.toLowerCase())) {
        return false;
      }
      return true;
    })
    .sort((a, b) => {
      const order = filters.value.sortOrder === "asc" ? 1 : -1;
      if (filters.value.sort === "price") {
        return order * ((a.price ?? Number.MAX_SAFE_INTEGER) - (b.price ?? Number.MAX_SAFE_INTEGER));
      }
      if (filters.value.sort === "delta") {
        return order * (a.delta - b.delta);
      }
      if (filters.value.sort === "stock") {
        return order * a.stockStatus.localeCompare(b.stockStatus);
      }
      return order * a.updatedAt.localeCompare(b.updatedAt);
    });
});

const activeProduct = computed(() =>
  products.value.find((product) => product.id === activeProductId.value)
);

const compareProducts = computed(() =>
  products.value.filter((product) => compareIds.value.includes(product.id))
);

const addProductImagesCount = computed(() => {
  if (!addProductState.value.snapshot || !addProductState.value.snapshot.images) {
    return 0;
  }
  return addProductState.value.snapshot.images.length;
});

const addProductImages = computed(() => {
  if (!addProductState.value.snapshot || !Array.isArray(addProductState.value.snapshot.images)) {
    return [];
  }
  return addProductState.value.snapshot.images.filter(Boolean);
});

const addProductImagePreview = computed(() => addProductImages.value[0] || placeholderImage);

const addProductImageThumbs = computed(() => addProductImages.value.slice(0, 4));

const selectedImagesSet = computed(() => new Set(addProductSelectedImages.value));

function toggleAddProductImage(index) {
  const selected = addProductSelectedImages.value.includes(index);
  if (selected) {
    addProductSelectedImages.value = addProductSelectedImages.value.filter((item) => item !== index);
    return;
  }
  addProductSelectedImages.value = [...addProductSelectedImages.value, index];
}

const addProductSpecsCount = computed(() => {
  if (!addProductState.value.snapshot || !addProductState.value.snapshot.specs) {
    return 0;
  }
  return Object.keys(addProductState.value.snapshot.specs).length;
});

const addProductSpecEntries = computed(() => {
  if (!addProductState.value.snapshot || !addProductState.value.snapshot.specs) {
    return [];
  }
  return Object.entries(addProductState.value.snapshot.specs).slice(0, 10);
});

const apiStatus = computed(() => {
  if (apiState.value.loading) {
    return "API...";
  }
  if (apiState.value.error) {
    return "API ERR";
  }
  return "API OK";
});

const filteredLogs = computed(() => {
  if (logTab.value === "backend") {
    return backendLogs.value;
  }
  if (logTab.value === "uvicorn") {
    return uvicornLogs.value;
  }
  if (logTab.value === "errors") {
    return logEntries.value.filter((entry) => entry.level === "ERROR");
  }
  return logEntries.value;
});

function formatPrice(value, currency = "EUR") {
  if (value === null || value === undefined) {
    return "n/a";
  }
  return `${value} ${currency}`;
}

function formatDiscount(amount, percent) {
  if (amount === null || amount === undefined || percent === null || percent === undefined) {
    return "n/a";
  }
  return `${amount.toFixed(2)} (-${percent.toFixed(1)}%)`;
}

function storeLabel(storeId) {
  return storeLookup.value[storeId] || storeId || "Inconnu";
}

function toggleSettings() {
  settingsOpen.value = !settingsOpen.value;
}

function toggleLogs() {
  logsOpen.value = !logsOpen.value;
  if (logsOpen.value) {
    loadLogs();
  }
}

function openAddProduct() {
  addProductDraft.value = {
    url: "",
    usePlaywright: false,
  };
  addProductState.value = {
    loading: false,
    error: "",
    snapshot: null,
  };
  addProductOpen.value = true;
}

function closeAddProduct() {
  addProductOpen.value = false;
}

function reloadPage() {
  if (typeof window !== "undefined") {
    window.location.reload();
  }
}

function clearCacheAndReload() {
  if (typeof window !== "undefined") {
    localStorage.removeItem("pw_webui_settings");
    localStorage.removeItem("pw_webui_product_meta");
    localStorage.removeItem("pw_webui_stores");
    window.location.reload();
  }
}

function applySettingsAndReload() {
  persistSettings();
  persistStores();
  persistProductMeta();
  reloadPage();
}

function cycleTheme() {
  const themes = ["gruvbox-dark", "gruvbox-light", "monokai-dark", "monokai-light"];
  const index = themes.indexOf(theme.value);
  theme.value = themes[(index + 1) % themes.length];
}

function selectProduct(productId) {
  activeProductId.value = productId;
}

function toggleCompare(productId) {
  if (compareIds.value.includes(productId)) {
    compareIds.value = compareIds.value.filter((id) => id !== productId);
    return;
  }
  if (compareIds.value.length >= 4) {
    return;
  }
  compareIds.value.push(productId);
}

function addStore() {
  if (!storeDraftName.value.trim()) {
    return;
  }
  const id = storeDraftName.value.toLowerCase().replace(/\s+/g, "-");
  if (stores.value.some((store) => store.id === id)) {
    return;
  }
  stores.value.push({
    id,
    name: storeDraftName.value.trim(),
    domain: storeDraftDomain.value.trim() || "custom",
  });
  storeDraftName.value = "";
  storeDraftDomain.value = "";
}

function removeStore(storeId) {
  stores.value = stores.value.filter((store) => store.id !== storeId);
}

function mergeStoresFromProducts(items) {
  const known = new Set(stores.value.map((store) => store.id));
  const additions = [];
  items.forEach((product) => {
    if (product.storeId && !known.has(product.storeId)) {
      known.add(product.storeId);
      additions.push({
        id: product.storeId,
        name: product.storeId.toUpperCase(),
        domain: "custom",
      });
    }
  });
  if (additions.length) {
    stores.value = [...stores.value, ...additions];
  }
}

function formatDate(value) {
  if (!value) {
    return "n/a";
  }
  try {
    return new Date(value).toLocaleString("fr-FR");
  } catch {
    return value;
  }
}

function resolveApiUrl(path) {
  const base = settings.value.apiBase || "/api";
  if (base.startsWith("http")) {
    return new URL(base.replace(/\/$/, "") + path).toString();
  }
  const basePath = base.endsWith("/") ? base.slice(0, -1) : base;
  return `${basePath}${path}`;
}

function apiHeaders() {
  const headers = {
    "Content-Type": "application/json",
  };
  if (settings.value.apiToken) {
    headers.Authorization = `Bearer ${settings.value.apiToken}`;
  }
  return headers;
}

function mapProduct(apiProduct) {
  const meta = productMeta.value[apiProduct.id] || {};
  const imageUrl = Array.isArray(apiProduct.images) && apiProduct.images.length > 0
    ? apiProduct.images[0]
    : placeholderImage;
  return {
    id: apiProduct.id,
    title: apiProduct.title || apiProduct.reference || "Sans titre",
    storeId: apiProduct.source || "unknown",
    reference: apiProduct.reference || "",
    price: apiProduct.latest_price ?? null,
    delta: 0,
    stockStatus: apiProduct.latest_stock_status || "unknown",
    updatedAt: apiProduct.last_updated_at || "",
    updatedAtLabel: formatDate(apiProduct.last_updated_at),
    refreshHours: meta.refreshHours ?? 24,
    url: apiProduct.url || "",
    category: apiProduct.category || "",
    currency: apiProduct.currency || "EUR",
    description: apiProduct.description || "",
    specs: apiProduct.specs || {},
    msrp: apiProduct.msrp ?? null,
    discountAmount: apiProduct.discount_amount ?? null,
    discountPercent: apiProduct.discount_percent ?? null,
    imageWebp: imageUrl,
    imageJpg: imageUrl,
  };
}

async function fetchProducts() {
  apiState.value.loading = true;
  apiState.value.error = "";
  try {
    const url = new URL(resolveApiUrl("/products"), window.location.origin);
    if (filters.value.store !== "all") {
      url.searchParams.set("source", filters.value.store);
    }
    if (filters.value.priceMin) {
      url.searchParams.set("price_min", String(filters.value.priceMin));
    }
    if (filters.value.priceMax) {
      url.searchParams.set("price_max", String(filters.value.priceMax));
    }
    if (filters.value.stock !== "all") {
      url.searchParams.set("stock_status", filters.value.stock);
    }
    if (settings.value.pageSize) {
      url.searchParams.set("limit", String(settings.value.pageSize));
    }
    const response = await fetch(url.toString(), {
      headers: apiHeaders(),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    const payload = await response.json();
    const mapped = payload.map(mapProduct);
    products.value = mapped;
    mergeStoresFromProducts(mapped);
    if (!activeProductId.value && products.value.length) {
      activeProductId.value = products.value[0].id;
    }
    if (activeProductId.value && !products.value.find((item) => item.id === activeProductId.value)) {
      activeProductId.value = products.value[0]?.id ?? null;
    }
  } catch (error) {
    apiState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "frontend", apiState.value.error);
  } finally {
    apiState.value.loading = false;
  }
}

async function saveActiveProduct() {
  if (!activeProduct.value) {
    return;
  }
  const payload = {
    url: activeProduct.value.url,
    title: activeProduct.value.title,
    category: activeProduct.value.category,
    currency: activeProduct.value.currency,
  };
  try {
    const response = await fetch(resolveApiUrl(`/products/${activeProduct.value.id}`), {
      method: "PATCH",
      headers: apiHeaders(),
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    await fetchProducts();
  } catch (error) {
    apiState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "frontend", apiState.value.error);
  }
}

async function refreshProduct(productId) {
  const product = products.value.find((item) => item.id === productId);
  if (!product || !product.url) {
    addLog("WARN", "frontend", "Impossible de relancer le scrap: URL manquante");
    return;
  }
  try {
    const response = await fetch(resolveApiUrl("/enqueue"), {
      method: "POST",
      headers: apiHeaders(),
      body: JSON.stringify({
        url: product.url,
        save_db: true,
      }),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    addLog("INFO", "backend", `Scrap relancé pour ${product.reference || product.title}`);
    await fetchProducts();
  } catch (error) {
    apiState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "frontend", apiState.value.error);
  }
}

async function deleteProduct(productId) {
  try {
    const response = await fetch(resolveApiUrl(`/products/${productId}`), {
      method: "DELETE",
      headers: apiHeaders(),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    if (activeProductId.value === productId) {
      activeProductId.value = null;
    }
    addLog("INFO", "backend", "Produit supprimé");
    await fetchProducts();
  } catch (error) {
    apiState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "backend", apiState.value.error);
  }
}

async function enqueueActiveProduct() {
  if (!activeProduct.value) {
    return;
  }
  await refreshProduct(activeProduct.value.id);
}

async function deleteActiveProduct() {
  if (!activeProduct.value) {
    return;
  }
  await deleteProduct(activeProduct.value.id);
}

function openProductUrl(product) {
  if (!product?.url) {
    addLog("WARN", "frontend", "Aucune URL disponible");
    return;
  }
  if (typeof window === "undefined") {
    return;
  }
  window.open(product.url, "_blank", "noopener");
}

async function copyActiveUrl() {
  if (!activeProduct.value?.url) {
    addLog("WARN", "frontend", "Aucune URL à copier");
    return;
  }
  if (typeof navigator === "undefined" || !navigator.clipboard) {
    addLog("WARN", "frontend", "Clipboard non disponible");
    return;
  }
  try {
    await navigator.clipboard.writeText(activeProduct.value.url);
    addLog("INFO", "frontend", "URL copiée");
  } catch (error) {
    addLog("ERROR", "frontend", "Erreur copie de l'URL");
  }
}

async function previewAddProduct() {
  if (!addProductDraft.value.url) {
    return;
  }
  addProductState.value.loading = true;
  addProductState.value.error = "";
  addProductState.value.snapshot = null;
  try {
    const response = await fetch(resolveApiUrl("/scrape/preview"), {
      method: "POST",
      headers: apiHeaders(),
      body: JSON.stringify({
        url: addProductDraft.value.url,
        use_playwright: addProductDraft.value.usePlaywright,
      }),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    const payload = await response.json();
    if (!payload.success) {
      addProductState.value.error = payload.error || "Aucune donnee";
      addLog("ERROR", "backend", addProductState.value.error);
      return;
    }
    if (!payload.snapshot) {
      addProductState.value.error = "Aucune donnee";
      addLog("ERROR", "backend", addProductState.value.error);
      return;
    }
    if (!payload.snapshot.title && !payload.snapshot.price) {
      addProductState.value.error = "Donnees incompletes";
    }
    addProductState.value.snapshot = payload.snapshot;
    const imageCount = Array.isArray(payload.snapshot.images) ? payload.snapshot.images.length : 0;
    addProductSelectedImages.value = imageCount ? [0] : [];
    addLog("INFO", "backend", "Preview scraping OK");
  } catch (error) {
    addProductState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "backend", addProductState.value.error);
  } finally {
    addProductState.value.loading = false;
  }
}

async function commitAddProduct() {
  if (!addProductState.value.snapshot) {
    return;
  }
  try {
    const response = await fetch(resolveApiUrl("/scrape/commit"), {
      method: "POST",
      headers: apiHeaders(),
      body: JSON.stringify({
        snapshot: addProductState.value.snapshot,
      }),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    addProductOpen.value = false;
    await fetchProducts();
    addLog("INFO", "backend", "Produit enregistre");
  } catch (error) {
    addProductState.value.error = error instanceof Error ? error.message : "Erreur API";
    addLog("ERROR", "backend", addProductState.value.error);
  }
}

let refreshTimer = null;

function startAutoRefresh() {
  if (refreshTimer) {
    clearInterval(refreshTimer);
    refreshTimer = null;
  }
  const minutes = Number(settings.value.refreshMinutes);
  if (Number.isFinite(minutes) && minutes > 0) {
    refreshTimer = setInterval(fetchProducts, minutes * 60 * 1000);
  }
}

function persistSettings() {
  if (typeof window === "undefined") {
    return;
  }
  const payload = {
    theme: theme.value,
    density: density.value,
    layoutMode: layoutMode.value,
    settings: settings.value,
    viewMode: viewMode.value,
    filters: filters.value,
  };
  localStorage.setItem("pw_webui_settings", JSON.stringify(payload));
}

function loadSettings() {
  if (typeof window === "undefined") {
    return;
  }
  const raw = localStorage.getItem("pw_webui_settings");
  if (!raw) {
    return;
  }
  try {
    const payload = JSON.parse(raw);
    if (payload.theme) {
      theme.value = payload.theme;
    }
    if (payload.density) {
      density.value = payload.density;
    }
    if (payload.layoutMode) {
      layoutMode.value = payload.layoutMode;
    }
    if (payload.settings) {
      settings.value = { ...settings.value, ...payload.settings };
    }
    if (payload.viewMode) {
      viewMode.value = payload.viewMode;
    }
    if (payload.filters) {
      filters.value = { ...filters.value, ...payload.filters };
    }
  } catch {
    return;
  }
}

function persistStores() {
  if (typeof window === "undefined") {
    return;
  }
  localStorage.setItem("pw_webui_stores", JSON.stringify(stores.value));
}

function loadStores() {
  if (typeof window === "undefined") {
    return;
  }
  const raw = localStorage.getItem("pw_webui_stores");
  if (!raw) {
    return;
  }
  try {
    const payload = JSON.parse(raw);
    if (Array.isArray(payload)) {
      stores.value = payload;
    }
  } catch {
    return;
  }
}

function persistProductMeta() {
  if (typeof window === "undefined") {
    return;
  }
  const payload = {};
  products.value.forEach((product) => {
    payload[product.id] = {
      refreshHours: product.refreshHours,
    };
  });
  localStorage.setItem("pw_webui_product_meta", JSON.stringify(payload));
}

async function fetchHealth() {
  try {
    const response = await fetch(resolveApiUrl("/health"), {
      headers: apiHeaders(),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    const payload = await response.json();
    apiHealth.value = payload;
  } catch (error) {
    apiHealth.value = { db: false, redis: false };
  }
}

async function fetchVersion() {
  try {
    const response = await fetch(resolveApiUrl("/version"), {
      headers: apiHeaders(),
    });
    if (!response.ok) {
      throw new Error(`API ${response.status}`);
    }
    const payload = await response.json();
    backendVersion.value = payload.api_version || "n/a";
  } catch {
    backendVersion.value = "n/a";
  }
}

async function loadLogs() {
  backendLogs.value = [];
  uvicornLogs.value = [];
  try {
    const url = new URL(resolveApiUrl("/logs"), window.location.origin);
    url.searchParams.set("limit", "10");
    const response = await fetch(url.toString(), { headers: apiHeaders() });
    if (response.ok) {
      const payload = await response.json();
      backendLogs.value = payload.map((entry, index) => ({
        id: `backend-${entry.id}-${index}`,
        time: formatDate(entry.fetched_at),
        level: entry.fetch_status?.toUpperCase() || "INFO",
        source: "backend",
        message: `${entry.source} ${entry.url} (${entry.fetch_method})`,
      }));
    }
    const backendResponse = await fetch(resolveApiUrl("/logs/backend"), {
      headers: apiHeaders(),
    });
    if (backendResponse.ok) {
      const backendPayload = await backendResponse.json();
      const backendEntries = backendPayload.map((entry, index) => ({
        id: `backend-log-${index}`,
        time: formatDate(entry.time),
        level: entry.level,
        source: "backend",
        message: entry.message,
      }));
      backendLogs.value = [...backendEntries, ...backendLogs.value];
    }
    const uvicornResponse = await fetch(resolveApiUrl("/logs/uvicorn"), {
      headers: apiHeaders(),
    });
    if (uvicornResponse.ok) {
      const uvicornPayload = await uvicornResponse.json();
      uvicornLogs.value = uvicornPayload.map((entry, index) => ({
        id: `uvicorn-log-${index}`,
        time: "",
        level: "INFO",
        source: "uvicorn",
        message: entry.line,
      }));
    }
  } catch (error) {
    addLog("ERROR", "backend", "Impossible de charger les logs");
  }
}

async function copyLogs() {
  if (typeof navigator === "undefined" || !navigator.clipboard) {
    addLog("WARN", "frontend", "Clipboard indisponible pour copier les logs");
    return;
  }
  const payload = filteredLogs.value
    .map((entry) => `${entry.time} [${entry.source}] ${entry.level} - ${entry.message}`)
    .join("\n");
  if (!payload) {
    addLog("WARN", "frontend", "Aucun log à copier");
    return;
  }
  try {
    await navigator.clipboard.writeText(payload);
    addLog("INFO", "frontend", "Logs copiés");
  } catch {
    addLog("ERROR", "frontend", "Erreur lors de la copie des logs");
  }
}

function addLog(level, source, message) {
  logEntries.value.unshift({
    id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
    time: new Date().toLocaleTimeString("fr-FR"),
    level,
    source,
    message,
  });
  if (logEntries.value.length > 200) {
    logEntries.value.pop();
  }
}

function loadProductMeta() {
  if (typeof window === "undefined") {
    return;
  }
  const raw = localStorage.getItem("pw_webui_product_meta");
  if (!raw) {
    return;
  }
  try {
    const payload = JSON.parse(raw);
    if (payload && typeof payload === "object") {
      productMeta.value = payload;
    }
  } catch {
    return;
  }
}

loadSettings();
loadStores();
loadProductMeta();

watch([theme, density, layoutMode, settings, viewMode, filters], persistSettings, { deep: true });
watch(stores, persistStores, { deep: true });
watch(products, persistProductMeta, { deep: true });
watch(() => settings.value.refreshMinutes, startAutoRefresh);
watch(
  [() => settings.value.apiBase, () => settings.value.apiToken, () => settings.value.pageSize, filters],
  () => {
    fetchProducts();
    fetchHealth();
    fetchVersion();
  },
  { deep: true }
);

onMounted(() => {
  fetchProducts();
  startAutoRefresh();
  fetchHealth();
  fetchVersion();
});

onBeforeUnmount(() => {
  if (refreshTimer) {
    clearInterval(refreshTimer);
  }
});
</script>
