<template>
  <q-page class="q-pa-xs pricing-page order-price-list-page">
    <teleport to="body">
      <div
        v-if="pageBusy"
        class="page-busy-overlay"
        @click.stop
        @mousedown.stop
        @mouseup.stop
        @touchstart.stop
        @wheel.stop
      >
        <q-spinner-gears size="56px" color="primary" />
        <div class="page-busy-label">Yukleniyor...</div>
      </div>
    </teleport>

    <div class="top-bar row items-center justify-between q-mb-xs">
      <div class="text-subtitle1 text-weight-bold">Fiyat Listesi</div>
      <div class="top-actions">
        <div class="row items-center q-gutter-xs top-actions-row top-actions-row--filters">
          <q-select
            v-model="topUrunIlkGrubu"
            dense
            outlined
            clearable
            emit-value
            map-options
            use-input
            :options="topUrunIlkGrubuOptions"
            :loading="Boolean(serverFilterLoading.urunIlkGrubu)"
            :disable="pageBusy"
            label="Urun Ilk Grubu"
            style="min-width: 220px"
            @filter="onTopFilterSearchUrunIlkGrubu"
            @update:model-value="onTopUrunIlkGrubuChange"
          />
          <q-select
            v-model="topUrunAnaGrubu"
            dense
            outlined
            clearable
            emit-value
            map-options
            use-input
            :options="topUrunAnaGrubuOptions"
            :loading="Boolean(serverFilterLoading.urunAnaGrubu)"
            :disable="pageBusy"
            label="Urun Ana Grubu"
            style="min-width: 240px"
            @filter="onTopFilterSearchUrunAnaGrubu"
            @update:model-value="onTopUrunAnaGrubuChange"
          />
          <q-btn
            color="primary"
            icon="filter_alt"
            label="Listeyi Getir"
            :disable="pageBusy || !canFetch"
            :loading="loading"
            @click="reloadData({ page: 1 })"
          />
          <q-btn
            flat
            color="grey-7"
            icon="restart_alt"
            label="Secimleri Sifirla"
            :disable="pageBusy"
            @click="resetSelections"
          />
        </div>

        <div class="row items-center q-gutter-xs top-actions-row top-actions-row--actions">
          <div class="toolbar-group">
            <q-btn-dropdown dense color="secondary" outline icon="view_module" label="Fiyat Secimi" :auto-close="false" :disable="pageBusy">
              <q-list dense class="currency-menu-list">
                <q-item clickable @click="selectAllPrices">
                  <q-item-section>Tumunu Sec</q-item-section>
                </q-item>
                <q-item clickable @click="clearAllPrices">
                  <q-item-section>Tumunu Temizle</q-item-section>
                </q-item>
                <q-separator />
                <q-item v-for="option in priceOptions" :key="option.value" clickable @click="togglePriceOption(option.value)">
                  <q-item-section avatar>
                    <q-checkbox
                      dense
                      :model-value="selectedPriceSet.has(option.value)"
                      :disable="pageBusy"
                      @click.stop
                      @update:model-value="() => togglePriceOption(option.value)"
                    />
                  </q-item-section>
                  <q-item-section>{{ option.label }}</q-item-section>
                </q-item>
              </q-list>
            </q-btn-dropdown>

            <q-btn
              dense
              flat
              color="grey-8"
              icon="view_sidebar"
              :label="leftDetailsExpanded ? 'Detaylari Gizle' : 'Detaylari Goster'"
              :disable="pageBusy"
              @click="leftDetailsExpanded = !leftDetailsExpanded"
            />
            <q-toggle
              v-model="showInStockOnly"
              dense
              color="primary"
              label="Sadece stogu olanlar"
              :disable="pageBusy"
            />
          </div>

          <div class="toolbar-group">
            <q-btn-dropdown dense color="primary" outline icon="download" label="Cikti Al" :disable="pageBusy || filteredRows.length === 0">
              <q-list dense style="min-width: 220px;">
                <q-item clickable @click="exportVisibleExcel">
                  <q-item-section avatar><q-icon name="grid_on" /></q-item-section>
                  <q-item-section>Excel'e Aktar</q-item-section>
                </q-item>
                <q-item clickable @click="printVisibleRows">
                  <q-item-section avatar><q-icon name="picture_as_pdf" /></q-item-section>
                  <q-item-section>PDF / Yazdir</q-item-section>
                </q-item>
              </q-list>
            </q-btn-dropdown>
          </div>

          <q-space />

          <div class="toolbar-group toolbar-group--paging">
            <q-pagination
              v-model="currentPage"
              color="primary"
              :max="Math.max(1, totalPages || 1)"
              :max-pages="8"
              boundary-links
              direction-links
              :disable="pageBusy"
              @update:model-value="onPageChange"
            />
            <div class="text-caption text-grey-8">
              Sayfa {{ currentPage }} / {{ Math.max(1, totalPages || 1) }} - {{ filteredRows.length }} satir
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="table-wrap" :style="{ '--sticky-scroll-comp': `${stickyScrollComp}px` }">
      <div v-if="showGuidanceOverlay" class="empty-overlay">
        <div class="empty-overlay-inner">
          <div class="text-subtitle1 text-weight-bold">Liste Icin Filtre Secin</div>
          <div class="text-body2 q-mt-xs">
            Urun Ilk Grubu, Urun Ana Grubu veya Urun Kodu secip <b>LISTEYI GETIR</b>'e basin.
          </div>
        </div>
      </div>
      <div
        ref="topScrollRef"
        class="top-x-scroll"
        @scroll.passive="onTopScroll"
      >
        <div
          class="top-x-scroll-inner"
          :style="{ width: `${tableMinWidth}px` }"
        />
      </div>
      <q-table
        ref="mainTableRef"
        class="pane-table pricing-table order-price-list-table"
        flat
        dense
        row-key="rowKey"
        :rows="filteredRows"
        :columns="visibleColumns"
        :loading="loading"
        :rows-per-page-options="[0]"
        :pagination="{ rowsPerPage: 0 }"
        hide-bottom
        :table-style="tableStyle"
      >
        <template #header="props">
          <q-tr :props="props" class="header-row-fixed">
            <q-th
              v-for="col in props.cols"
              :key="col.name"
              :props="props"
              :class="[col.headerClasses, { 'sticky-col': isStickyCol(col.name), 'sticky-boundary': isStickyBoundary(col.name) }]"
              :style="getHeaderCellStyle(col)"
            >
              <div class="header-with-filter">
                <span>{{ col.label }}</span>
                <q-btn
                  v-if="col.name === 'productCode'"
                  flat
                  dense
                  round
                  size="8px"
                  icon="filter_alt"
                  :color="selectedProductCodes.length > 0 ? 'primary' : 'grey-7'"
                  :disable="pageBusy"
                  class="header-filter-btn"
                  @click.stop
                >
                  <q-badge v-if="selectedProductCodes.length > 0" color="primary" floating rounded>
                    {{ selectedProductCodes.length }}
                  </q-badge>
                  <q-menu
                    class="product-code-filter-menu"
                    anchor="bottom left"
                    self="top left"
                    @before-show="onProductCodeMenuShow"
                  >
                    <div class="q-pa-sm filter-menu-panel">
                      <q-input
                        v-model="filterSearch.productCode"
                        dense
                        outlined
                        debounce="250"
                        placeholder="Urun kodu ara"
                        :disable="pageBusy"
                        @update:model-value="onProductCodeSearchText"
                      >
                        <template #prepend>
                          <q-icon name="search" />
                        </template>
                      </q-input>
                      <div class="row items-center justify-between q-mt-xs">
                        <q-btn flat dense size="sm" label="Tumunu Sec" :disable="pageBusy || productCodeOptions.length === 0" @click="selectAllProductCodeOptions" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy || selectedProductCodes.length === 0" @click="clearProductCodeOptions" />
                      </div>
                      <q-separator class="q-my-xs" />
                      <q-scroll-area style="height: 260px; width: 320px;">
                        <q-list dense>
                          <q-item
                            v-for="option in productCodeOptions"
                            :key="option.value"
                            clickable
                            :disable="pageBusy"
                            @click="toggleProductCodeValue(option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                :model-value="selectedProductCodeSet.has(option.value)"
                                :disable="pageBusy"
                                @click.stop
                                @update:model-value="() => toggleProductCodeValue(option.value)"
                              />
                            </q-item-section>
                            <q-item-section>{{ option.label }}</q-item-section>
                          </q-item>
                        </q-list>
                      </q-scroll-area>
                      <q-separator class="q-my-xs" />
                      <div class="row items-center justify-end q-gutter-xs">
                        <q-btn v-close-popup dense flat label="Kapat" />
                        <q-btn v-close-popup dense color="primary" label="Uygula" :disable="pageBusy" @click="reloadData({ page: 1 })" />
                      </div>
                    </div>
                  </q-menu>
                </q-btn>
                <q-btn
                  v-else-if="col.name === 'campaignLabel'"
                  flat
                  dense
                  round
                  size="8px"
                  icon="filter_alt"
                  :color="selectedCampaignLabels.length > 0 ? 'primary' : 'grey-7'"
                  :disable="pageBusy"
                  class="header-filter-btn"
                  @click.stop
                >
                  <q-badge v-if="selectedCampaignLabels.length > 0" color="primary" floating rounded>
                    {{ selectedCampaignLabels.length }}
                  </q-badge>
                  <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                    <div class="excel-filter-menu">
                      <q-input
                        v-model="campaignFilterSearch"
                        dense
                        outlined
                        clearable
                        class="excel-filter-select"
                        placeholder="Kampanya ara"
                      />
                      <div class="excel-filter-actions row items-center justify-between q-pt-xs">
                        <q-btn flat dense size="sm" label="Tumunu Sec" :disable="pageBusy || filteredCampaignOptions.length === 0" @click="selectAllCampaignOptions" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy || selectedCampaignLabels.length === 0" @click="clearCampaignOptions" />
                      </div>
                      <q-virtual-scroll
                        v-if="filteredCampaignOptions.length > 0"
                        class="excel-filter-options"
                        :items="filteredCampaignOptions"
                        :virtual-scroll-item-size="32"
                        separator
                      >
                        <template #default="{ item: option }">
                          <q-item
                            :key="`campaign-${option.value}`"
                            dense
                            clickable
                            :disable="pageBusy"
                            class="excel-filter-option"
                            @click="toggleCampaignValue(option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                size="sm"
                                :model-value="selectedCampaignLabelSet.has(option.value)"
                                :disable="pageBusy"
                                @update:model-value="() => toggleCampaignValue(option.value)"
                                @click.stop
                              />
                            </q-item-section>
                            <q-item-section>
                              <q-item-label>{{ option.label }}</q-item-label>
                            </q-item-section>
                          </q-item>
                        </template>
                      </q-virtual-scroll>
                      <div v-else class="excel-filter-empty">Sonuc yok</div>
                    </div>
                  </q-menu>
                </q-btn>
                <q-btn
                  v-else-if="col.name === 'variantCodes'"
                  flat
                  dense
                  round
                  size="8px"
                  icon="filter_alt"
                  :color="selectedVariantCodes.length > 0 ? 'primary' : 'grey-7'"
                  :disable="pageBusy"
                  class="header-filter-btn"
                  @click.stop
                >
                  <q-badge v-if="selectedVariantCodes.length > 0" color="primary" floating rounded>
                    {{ selectedVariantCodes.length }}
                  </q-badge>
                  <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                    <div class="excel-filter-menu">
                      <q-input
                        v-model="variantFilterSearch"
                        dense
                        outlined
                        clearable
                        class="excel-filter-select"
                        placeholder="Varyant ara"
                      />
                      <div class="excel-filter-actions row items-center justify-between q-pt-xs">
                        <q-btn flat dense size="sm" label="Tumunu Sec" :disable="pageBusy || filteredVariantOptions.length === 0" @click="selectAllVariantOptions" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy || selectedVariantCodes.length === 0" @click="clearVariantOptions" />
                      </div>
                      <q-virtual-scroll
                        v-if="filteredVariantOptions.length > 0"
                        class="excel-filter-options"
                        :items="filteredVariantOptions"
                        :virtual-scroll-item-size="32"
                        separator
                      >
                        <template #default="{ item: option }">
                          <q-item
                            :key="`variant-${option.value}`"
                            dense
                            clickable
                            :disable="pageBusy"
                            class="excel-filter-option"
                            @click="toggleVariantValue(option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                size="sm"
                                :model-value="selectedVariantCodeSet.has(option.value)"
                                :disable="pageBusy"
                                @update:model-value="() => toggleVariantValue(option.value)"
                                @click.stop
                              />
                            </q-item-section>
                            <q-item-section>
                              <q-item-label>{{ option.label }}</q-item-label>
                            </q-item-section>
                          </q-item>
                        </template>
                      </q-virtual-scroll>
                      <div v-else class="excel-filter-empty">Sonuc yok</div>
                    </div>
                  </q-menu>
                </q-btn>
                <q-btn
                  v-else-if="isLocalFilterableColumn(col.name)"
                  flat
                  dense
                  round
                  size="8px"
                  icon="filter_alt"
                  :color="getColumnFilterValues(col.name).length > 0 ? 'primary' : 'grey-7'"
                  :disable="pageBusy"
                  class="header-filter-btn"
                  @click.stop
                >
                  <q-badge v-if="getColumnFilterValues(col.name).length > 0" color="primary" floating rounded>
                    {{ getColumnFilterValues(col.name).length }}
                  </q-badge>
                  <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                    <div class="excel-filter-menu">
                      <q-input
                        :model-value="columnFilterSearch[col.name] || ''"
                        dense
                        outlined
                        clearable
                        class="excel-filter-select"
                        placeholder="Filtre ara"
                        @update:model-value="(val) => setColumnFilterSearch(col.name, val)"
                      />
                      <div class="excel-filter-actions row items-center justify-between q-pt-xs">
                        <q-btn flat dense size="sm" label="Tumunu Sec" :disable="pageBusy || getFilteredColumnOptions(col).length === 0" @click="selectAllColumnOptions(col)" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy || getColumnFilterValues(col.name).length === 0" @click="clearColumnOptions(col.name)" />
                      </div>
                      <q-virtual-scroll
                        v-if="getFilteredColumnOptions(col).length > 0"
                        class="excel-filter-options"
                        :items="getFilteredColumnOptions(col)"
                        :virtual-scroll-item-size="32"
                        separator
                      >
                        <template #default="{ item: option }">
                          <q-item
                            :key="`${col.name}-${option.value}`"
                            dense
                            clickable
                            :disable="pageBusy"
                            class="excel-filter-option"
                            @click="toggleColumnFilterValue(col.name, option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                size="sm"
                                :model-value="getColumnFilterSet(col.name).has(option.value)"
                                :disable="pageBusy"
                                @update:model-value="() => toggleColumnFilterValue(col.name, option.value)"
                                @click.stop
                              />
                            </q-item-section>
                            <q-item-section>
                              <q-item-label>{{ option.label || '-' }}</q-item-label>
                            </q-item-section>
                          </q-item>
                        </template>
                      </q-virtual-scroll>
                      <div v-else class="excel-filter-empty">Sonuc yok</div>
                    </div>
                  </q-menu>
                </q-btn>
                <span v-else class="header-filter-ghost"></span>
              </div>
            </q-th>
          </q-tr>
        </template>

        <template #body-cell="props">
          <q-td
            :props="props"
            :class="[props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
            :style="getBodyCellStyle(props.col)"
          >
            {{ props.value }}
          </q-td>
        </template>

        <template #body-cell-image="props">
          <q-td
            :props="props"
            :class="['image-cell', props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
            :style="getBodyCellStyle(props.col)"
          >
            <q-img
              v-if="props.row.imageUrl"
              :src="props.row.imageUrl"
              class="product-thumb cursor-pointer"
              fit="cover"
              no-spinner
              @click.stop="openProductCard(props.row)"
            />
            <div v-else class="product-thumb-placeholder cursor-pointer" @click.stop="openProductCard(props.row)">
              <q-icon name="image_not_supported" size="24px" color="grey-6" />
            </div>
          </q-td>
        </template>

        <template #body-cell-campaignLabel="props">
          <q-td
            :props="props"
            :class="[props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
            :style="getBodyCellStyle(props.col)"
          >
            <div class="campaign-cell-content">
              <span v-if="props.row.campaignLabel" class="campaign-text" :title="props.row.campaignLabel">
                {{ props.row.campaignLabel }}
              </span>
            </div>
          </q-td>
        </template>

        <template #body-cell-campaignRate="props">
          <q-td
            :props="props"
            :class="[props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
            :style="getBodyCellStyle(props.col)"
          >
            <span class="cell-text campaign-rate-text" :title="String(props.row.campaignRate ?? '')">
              {{ props.row.campaignRate ? formatPrice(props.row.campaignRate) : '' }}
            </span>
          </q-td>
        </template>

        <template v-for="name in priceColumnNames" #[`body-cell-${name}`]="props" :key="name">
          <q-td
            :props="props"
            :class="['text-right', props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
            :style="getBodyCellStyle(props.col)"
          >
            <span :class="['cell-text', 'price-cell-text', { 'campaign-price-text': name.endsWith('Campaign') }]" :title="formatPrice(props.row[name])">
              {{ formatPrice(props.row[name]) }}
            </span>
          </q-td>
        </template>
      </q-table>
    </div>

    <q-dialog v-model="productCardDialog" maximized @hide="onProductCardDialogHide">
      <q-card class="product-card-dialog">
        <q-card-section class="row items-center q-pb-sm">
          <div class="text-h6">Urun Karti</div>
          <q-space />
          <q-btn icon="close" flat round dense v-close-popup />
        </q-card-section>

        <q-separator />

        <q-card-section class="q-pt-md">
          <div class="product-card-content">
            <div class="product-card-images">
              <q-carousel
                v-if="productCardImages.length"
                v-model="productCardSlide"
                animated
                swipeable
                navigation
                arrows
                height="100%"
                class="product-card-carousel rounded-borders"
              >
                <q-carousel-slide
                  v-for="(img, idx) in productCardImages"
                  :key="'img-' + idx"
                  :name="idx"
                  class="column no-wrap flex-center"
                >
                  <div class="dialog-image-stage cursor-pointer" @click="openProductImageFullscreen(img)">
                    <q-img :src="img" fit="contain" class="dialog-image" />
                  </div>
                </q-carousel-slide>
              </q-carousel>
              <div v-else class="dialog-image-empty">
                <q-icon name="image_not_supported" size="36px" color="grey-6" />
              </div>
            </div>

            <div class="product-card-fields">
              <div class="field-row field-row-head"><span class="k">Urun</span><span class="v">{{ productCardData.productCode || '-' }} / {{ productCardData.variantCodes || '-' }}</span></div>
              <div class="field-row"><span class="k">Urun Kodu</span><span class="v">{{ productCardData.productCode || '-' }}</span></div>
              <div class="field-row"><span class="k">Varyant</span><span class="v">{{ productCardData.variantCodes || '-' }}</span></div>
              <div class="field-row"><span class="k">Marka</span><span class="v">{{ productCardData.marka || '-' }}</span></div>
              <div class="field-row"><span class="k">Marka Grubu</span><span class="v">{{ productCardData.brandGroupSelection || '-' }}</span></div>
              <div class="field-row"><span class="k">Kategori</span><span class="v">{{ productCardData.kategori || '-' }}</span></div>
              <div class="field-row"><span class="k">Urun Ilk Grubu</span><span class="v">{{ productCardData.urunIlkGrubu || '-' }}</span></div>
              <div class="field-row"><span class="k">Urun Ana Grubu</span><span class="v">{{ productCardData.urunAnaGrubu || '-' }}</span></div>
              <div class="field-row"><span class="k">Urun Alt Grubu</span><span class="v">{{ productCardData.urunAltGrubu || '-' }}</span></div>
              <div class="field-row"><span class="k">Icerik</span><span class="v">{{ productCardData.icerik || '-' }}</span></div>
              <div class="field-row"><span class="k">Karisim</span><span class="v">{{ productCardData.karisim || '-' }}</span></div>
              <div class="field-row"><span class="k">Kampanya</span><span class="v">{{ productCardData.campaignLabel || '-' }}</span></div>
              <div class="field-row"><span class="k">Stok</span><span class="v">{{ formatStock(productCardData.stockQty || 0) }}</span></div>

              <div class="product-card-section">
                <div class="product-card-section-title">Fiyat Bilgileri</div>
                <div v-if="productCardPriceRows.length" class="price-info-grid">
                  <div v-for="item in productCardPriceRows" :key="item.key" class="price-info-row">
                    <span class="price-label">{{ item.label }}</span>
                    <span class="price-value">{{ item.price || '-' }}</span>
                    <span class="price-campaign">{{ item.campaignPrice || '-' }}</span>
                  </div>
                </div>
                <div v-else class="product-card-empty-text">Secili fiyat kolonu yok.</div>
              </div>

              <div class="product-card-section">
                <div class="product-card-section-title">Beden Stoklari</div>
                <q-inner-loading :showing="productCardStockLoading">
                  <q-spinner size="24px" color="primary" />
                </q-inner-loading>
                <div v-if="productCardSizeRows.length" class="size-stock-grid">
                  <div v-for="item in productCardSizeRows" :key="item.size" class="size-stock-cell">
                    <span class="size-label">{{ item.size }}</span>
                    <span class="size-qty">{{ formatStock(item.qty) }}</span>
                  </div>
                </div>
                <div v-else-if="!productCardStockLoading" class="product-card-empty-text">Beden stogu bulunamadi.</div>
              </div>
            </div>
          </div>
        </q-card-section>
      </q-card>
    </q-dialog>

    <q-dialog v-model="productImageFullscreenDialog" maximized>
      <q-card class="image-fullscreen-dialog">
        <q-card-section class="row items-center q-pb-sm">
          <div class="text-h6">Urun Fotografi</div>
          <q-space />
          <q-btn icon="close" flat round dense v-close-popup />
        </q-card-section>
        <q-separator />
        <q-card-section class="image-fullscreen-body">
          <q-carousel
            v-if="fullscreenImages.length"
            v-model="productImageFullscreenSlide"
            animated
            swipeable
            navigation
            arrows
            height="calc(100vh - 120px)"
            class="image-fullscreen-carousel"
          >
            <q-carousel-slide
              v-for="(img, idx) in fullscreenImages"
              :key="'full-img-' + idx"
              :name="idx"
              class="column no-wrap flex-center"
            >
              <div class="image-fullscreen-stage">
                <q-img :src="img" fit="contain" class="image-fullscreen-img" />
              </div>
            </q-carousel-slide>
          </q-carousel>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { Notify } from 'quasar'
import api from 'src/services/api'

const PAGE_LIMIT = 250
const GUIDANCE_MSG = 'Liste icin filtre secin.'

const priceOptions = ['USD', 'EUR', 'TRY'].flatMap((cur) => [1, 2, 3, 4, 5, 6].map((lv) => ({
  label: `${cur} ${lv}`,
  value: `${cur.toLowerCase()}${lv}`
})))
const campaignPairs = priceOptions.map((x) => ({ base: x.value, derived: `${x.value}Campaign` }))
const priceColumnNames = campaignPairs.flatMap((p) => [p.base, p.derived])

const topUrunIlkGrubu = ref(null)
const topUrunAnaGrubu = ref(null)
const selectedProductCodes = ref([])
const selectedCampaignLabels = ref([])
const selectedVariantCodes = ref([])
const campaignFilterSearch = ref('')
const variantFilterSearch = ref('')
const columnFilters = ref({})
const columnFilterSearch = ref({})
const selectedPriceOptions = ref(['usd5', 'try5'])
const leftDetailsExpanded = ref(true)
const showInStockOnly = ref(false)

const rows = ref([])
const loading = ref(false)
const renderPending = ref(false)
const error = ref(GUIDANCE_MSG)
const currentPage = ref(1)
const totalPages = ref(1)
const totalCount = ref(0)

const serverFilterOptionMap = ref({})
const serverFilterLoading = ref({})
const serverFilterLastQuery = ref({})
const filterSearch = ref({ productCode: '', urunIlkGrubu: '', urunAnaGrubu: '' })
const imageCache = new Map()
const imageListCache = new Map()
const variantCodeCollator = new Intl.Collator('tr', { numeric: true, sensitivity: 'base' })
const mainTableRef = ref(null)
const topScrollRef = ref(null)
let syncingScroll = false

const productCardDialog = ref(false)
const productCardData = ref({})
const productCardImages = ref([])
const productCardSlide = ref(0)
const productCardStockLoading = ref(false)
const productCardSizeRows = ref([])
const productImageFullscreenDialog = ref(false)
const productImageFullscreenSlide = ref(0)
const fullscreenImages = computed(() => productCardImages.value || [])

const selectedPriceSet = computed(() => new Set(selectedPriceOptions.value || []))
const productCardPriceRows = computed(() => {
  const row = productCardData.value || {}
  return priceOptions
    .filter((option) => selectedPriceSet.value.has(option.value))
    .map((option) => ({
      key: option.value,
      label: option.label,
      price: formatPrice(row?.[option.value]),
      campaignPrice: formatPrice(row?.[`${option.value}Campaign`])
    }))
})
const selectedProductCodeSet = computed(() => new Set(selectedProductCodes.value || []))
const selectedCampaignLabelSet = computed(() => new Set(selectedCampaignLabels.value || []))
const selectedVariantCodeSet = computed(() => new Set(selectedVariantCodes.value || []))
const pageBusy = computed(() => loading.value || renderPending.value)
const canFetch = computed(() => Boolean(topUrunIlkGrubu.value || topUrunAnaGrubu.value || selectedProductCodes.value.length > 0))
const showGuidanceOverlay = computed(() => !loading.value && rows.value.length === 0 && error.value === GUIDANCE_MSG)

const topUrunIlkGrubuOptions = computed(() => serverFilterOptionMap.value.urunIlkGrubu || [])
const topUrunAnaGrubuOptions = computed(() => serverFilterOptionMap.value.urunAnaGrubu || [])
const productCodeOptions = computed(() => serverFilterOptionMap.value.productCode || [])
const campaignOptions = computed(() => {
  const uniq = new Set()
  for (const row of rows.value || []) {
    const val = toText(row?.campaignLabel)
    if (val) uniq.add(val)
  }
  return Array.from(uniq)
    .sort((a, b) => a.localeCompare(b, 'tr'))
    .map((value) => ({ label: value, value }))
})
const filteredCampaignOptions = computed(() => {
  const q = toText(campaignFilterSearch.value).toLocaleLowerCase('tr')
  const list = campaignOptions.value
  return q ? list.filter((x) => x.label.toLocaleLowerCase('tr').includes(q)) : list
})
const variantOptions = computed(() => {
  const uniq = new Set()
  for (const row of rows.value || []) {
    const val = toText(row?.variantCodes)
    if (val) uniq.add(val)
  }
  return Array.from(uniq)
    .sort((a, b) => variantCodeCollator.compare(a, b))
    .map((value) => ({ label: value, value }))
})
const filteredVariantOptions = computed(() => {
  const q = toText(variantFilterSearch.value).toLocaleLowerCase('tr')
  const list = variantOptions.value
  return q ? list.filter((x) => x.label.toLocaleLowerCase('tr').includes(q)) : list
})

function toText (value) {
  return String(value ?? '').trim()
}

function toNumber (value) {
  const n = Number(String(value ?? '0').replace(/\./g, '').replace(',', '.'))
  return Number.isFinite(n) ? n : 0
}

function round2 (value) {
  const n = Number(value)
  return Number.isFinite(n) ? Number(n.toFixed(2)) : null
}

function formatPrice (value) {
  const n = Number(value)
  if (!Number.isFinite(n) || n === 0) return ''
  return n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}

function formatStock (value) {
  const n = Number(value)
  if (!Number.isFinite(n)) return ''
  return n.toLocaleString('tr-TR', { maximumFractionDigits: 2 })
}

function parseStockNumber (value) {
  if (typeof value === 'number') return Number.isFinite(value) ? value : 0
  const text = String(value ?? '').trim()
  if (!text) return 0
  const normalized = text.replace(/\./g, '').replace(',', '.')
  const n = Number.parseFloat(normalized)
  return Number.isFinite(n) ? n : 0
}

function normalizeCardToken (value) {
  return String(value ?? '').trim().toUpperCase()
}

function parseVariantTokens (variantCode) {
  const parts = String(variantCode || '').split('-').map((x) => normalizeCardToken(x)).filter(Boolean)
  return {
    color: parts[0] || '',
    dim2: parts.length > 1 ? parts.slice(1).join('-') : ''
  }
}

function stockRowText (row, ...keys) {
  for (const key of keys) {
    const value = String(row?.[key] ?? '').trim()
    if (value) return value
  }
  return ''
}

function matchesProductCardVariant (stockRow, cardRow) {
  const tokens = parseVariantTokens(cardRow?.variantCodes)
  if (!tokens.color && !tokens.dim2) return true
  const color = normalizeCardToken(stockRowText(stockRow, 'Renk_Kodu', 'ColorCode', 'colorCode'))
  const dim2 = normalizeCardToken(stockRowText(stockRow, 'Yaka', 'ItemDim2Code', 'itemDim2Code', 'Renk2'))
  if (tokens.color && color !== tokens.color) return false
  if (tokens.dim2 && dim2 !== tokens.dim2) return false
  return true
}

function buildSizeStockRows (stockRows, cardRow) {
  const totals = new Map()
  for (const item of stockRows || []) {
    if (!matchesProductCardVariant(item, cardRow)) continue
    const size = stockRowText(item, 'Beden', 'Size', 'ItemDim1Code', 'itemDim1Code')
    if (!size) continue
    const qty = parseStockNumber(item?.Kullanilabilir_Envanter ?? item?.StockQty ?? item?.qty)
    totals.set(size, (totals.get(size) || 0) + qty)
  }
  return Array.from(totals.entries())
    .map(([size, qty]) => ({ size, qty }))
    .sort((a, b) => variantCodeCollator.compare(a.size, b.size))
}

function mapProductRow (raw, index) {
  const row = {
    id: index + 1,
    productCode: toText(raw?.ProductCode),
    stockQty: toNumber(raw?.StockQty),
    stockEntryDate: toText(raw?.StockEntryDate),
    lastPricingDate: toText(raw?.LastPricingDate),
    askiliYan: toText(raw?.AskiliYan),
    kategori: toText(raw?.Kategori),
    urunIlkGrubu: toText(raw?.UrunIlkGrubu),
    urunAnaGrubu: toText(raw?.UrunAnaGrubu),
    urunAltGrubu: toText(raw?.UrunAltGrubu),
    icerik: toText(raw?.Icerik),
    karisim: toText(raw?.Karisim),
    marka: toText(raw?.Marka),
    brandGroupSelection: toText(raw?.BrandGroupSec)
  }
  for (const p of priceOptions) row[p.value] = toNumber(raw?.[p.value.toUpperCase()])
  return row
}

function applyCampaignPrices (row) {
  const rate = Number(row?.campaignRate || 0)
  for (const p of campaignPairs) {
    const base = Number(row?.[p.base] || 0)
    row[p.derived] = rate > 0 && base > 0 ? round2(base * (1 - rate / 100)) : null
  }
}

function buildCampaignLabel (variant) {
  const code = toText(variant?.campaign_code)
  const title = toText(variant?.campaign_title)
  if (code && title) return `${code} - ${title}`
  return code || title
}

function buildRows (products, variants) {
  const byCode = new Map()
  for (const v of variants || []) {
    const code = toText(v?.product_code)
    if (!code) continue
    if (!byCode.has(code)) byCode.set(code, [])
    byCode.get(code).push(v)
  }

  const out = []
  for (const p of products) {
    const list = byCode.get(p.productCode) || []
    if (list.length === 0) {
      const row = {
        ...p,
        rowKey: `${p.productCode}|0|0`,
        imageUrl: '',
        variantCodes: '',
        variantStocks: formatStock(p.stockQty),
        campaignLabel: '',
        campaignRate: null,
        lastCampaignDate: ''
      }
      applyCampaignPrices(row)
      out.push(row)
      continue
    }
    list.sort((a, b) => variantCodeCollator.compare(toText(a?.variant_code), toText(b?.variant_code)))
    for (const v of list) {
      const d1 = Number(v?.dim1 || 0)
      const d3 = v?.dim3 == null ? 0 : Number(v?.dim3 || 0)
      const row = {
        ...p,
        rowKey: `${p.productCode}|${d1}|${d3}`,
        imageUrl: '',
        dim1: d1,
        dim3: d3,
        variantCodes: toText(v?.variant_code),
        variantStocks: formatStock(v?.stock_qty),
        stockQty: Number(v?.stock_qty ?? 0),
        campaignLabel: buildCampaignLabel(v),
        campaignRate: Number(v?.discount_rate || 0) || null,
        lastCampaignDate: toText(v?.campaign_last_dttm)
      }
      applyCampaignPrices(row)
      out.push(row)
    }
  }
  return out
}

async function fetchServerFilterOptions (field, q = '') {
  const query = toText(q)
  const last = toText(serverFilterLastQuery.value[field])
  const cached = Array.isArray(serverFilterOptionMap.value[field]) && serverFilterOptionMap.value[field].length > 0
  if (cached && last === query) return
  serverFilterLoading.value = { ...serverFilterLoading.value, [field]: true }
  serverFilterLastQuery.value = { ...serverFilterLastQuery.value, [field]: query }
  try {
    const params = { field, q: query, limit: field === 'productCode' ? 200 : 160 }
    if (field === 'urunAnaGrubu' && topUrunIlkGrubu.value) params.urun_ilk_grubu = topUrunIlkGrubu.value
    const res = await api.get('/order/price-list/options', { params })
    const items = Array.isArray(res?.data?.items) ? res.data.items : []
    serverFilterOptionMap.value = {
      ...serverFilterOptionMap.value,
      [field]: items.map((x) => ({
        label: toText(x?.label ?? x?.value),
        value: toText(x?.value ?? x?.label)
      })).filter((x) => x.value)
    }
  } finally {
    serverFilterLoading.value = { ...serverFilterLoading.value, [field]: false }
  }
}

function onTopFilterSearchUrunIlkGrubu (val, update) {
  update(() => {
    filterSearch.value.urunIlkGrubu = toText(val)
    void fetchServerFilterOptions('urunIlkGrubu', val)
  })
}

function onTopFilterSearchUrunAnaGrubu (val, update) {
  update(() => {
    filterSearch.value.urunAnaGrubu = toText(val)
    void fetchServerFilterOptions('urunAnaGrubu', val)
  })
}

function onProductCodeMenuShow () {
  void fetchServerFilterOptions('productCode', filterSearch.value.productCode)
}

function onProductCodeSearchText (val) {
  void fetchServerFilterOptions('productCode', val)
}

function toggleProductCodeValue (value) {
  const v = toText(value)
  if (!v) return
  const set = new Set(selectedProductCodes.value || [])
  if (set.has(v)) set.delete(v)
  else set.add(v)
  selectedProductCodes.value = Array.from(set).sort((a, b) => a.localeCompare(b, 'tr'))
}

function selectAllProductCodeOptions () {
  const set = new Set(selectedProductCodes.value || [])
  for (const option of productCodeOptions.value) {
    const v = toText(option.value)
    if (v) set.add(v)
  }
  selectedProductCodes.value = Array.from(set).sort((a, b) => a.localeCompare(b, 'tr'))
}

function clearProductCodeOptions () {
  selectedProductCodes.value = []
}

function toggleCampaignValue (value) {
  const v = toText(value)
  if (!v) return
  const set = new Set(selectedCampaignLabels.value || [])
  if (set.has(v)) set.delete(v)
  else set.add(v)
  selectedCampaignLabels.value = Array.from(set).sort((a, b) => a.localeCompare(b, 'tr'))
}

function selectAllCampaignOptions () {
  const set = new Set(selectedCampaignLabels.value || [])
  for (const option of filteredCampaignOptions.value) {
    const v = toText(option.value)
    if (v) set.add(v)
  }
  selectedCampaignLabels.value = Array.from(set).sort((a, b) => a.localeCompare(b, 'tr'))
}

function clearCampaignOptions () {
  selectedCampaignLabels.value = []
}

function toggleVariantValue (value) {
  const v = toText(value)
  if (!v) return
  const set = new Set(selectedVariantCodes.value || [])
  if (set.has(v)) set.delete(v)
  else set.add(v)
  selectedVariantCodes.value = Array.from(set).sort((a, b) => variantCodeCollator.compare(a, b))
}

function selectAllVariantOptions () {
  const set = new Set(selectedVariantCodes.value || [])
  for (const option of filteredVariantOptions.value) {
    const v = toText(option.value)
    if (v) set.add(v)
  }
  selectedVariantCodes.value = Array.from(set).sort((a, b) => variantCodeCollator.compare(a, b))
}

function clearVariantOptions () {
  selectedVariantCodes.value = []
}

function isLocalFilterableColumn (name) {
  if (!name || ['image', 'productCode', 'variantCodes', 'campaignLabel'].includes(name)) return false
  return true
}

function getColumnFilterValues (name) {
  const list = columnFilters.value?.[name]
  return Array.isArray(list) ? list : []
}

function getColumnFilterSet (name) {
  return new Set(getColumnFilterValues(name))
}

function setColumnFilterSearch (name, value) {
  columnFilterSearch.value = { ...columnFilterSearch.value, [name]: toText(value) }
}

function getColumnOptions (col) {
  const uniq = new Set()
  for (const row of rows.value || []) {
    uniq.add(exportCell(row, col))
  }
  return Array.from(uniq)
    .sort((a, b) => String(a).localeCompare(String(b), 'tr', { numeric: true, sensitivity: 'base' }))
    .map((value) => ({ label: value || '-', value }))
}

function getFilteredColumnOptions (col) {
  const q = toText(columnFilterSearch.value?.[col.name]).toLocaleLowerCase('tr')
  const list = getColumnOptions(col)
  return q ? list.filter((x) => String(x.label || '').toLocaleLowerCase('tr').includes(q)) : list
}

function toggleColumnFilterValue (name, value) {
  const set = new Set(getColumnFilterValues(name))
  if (set.has(value)) set.delete(value)
  else set.add(value)
  columnFilters.value = { ...columnFilters.value, [name]: Array.from(set) }
}

function selectAllColumnOptions (col) {
  const set = new Set(getColumnFilterValues(col.name))
  for (const option of getFilteredColumnOptions(col)) set.add(option.value)
  columnFilters.value = { ...columnFilters.value, [col.name]: Array.from(set) }
}

function clearColumnOptions (name) {
  columnFilters.value = { ...columnFilters.value, [name]: [] }
}

function onTopUrunIlkGrubuChange () {
  topUrunAnaGrubu.value = null
  void fetchServerFilterOptions('urunAnaGrubu', '')
}

function onTopUrunAnaGrubuChange () {}

function requestParams (page) {
  const params = {
    page,
    limit: Math.min(500, Math.max(PAGE_LIMIT, selectedProductCodes.value.length || 0)),
    include_total: 1
  }
  if (topUrunIlkGrubu.value) params.urun_ilk_grubu = topUrunIlkGrubu.value
  if (topUrunAnaGrubu.value) params.urun_ana_grubu = topUrunAnaGrubu.value
  if (selectedProductCodes.value.length > 0) params.product_code = selectedProductCodes.value.join(',')
  return params
}

async function reloadData ({ page = 1 } = {}) {
  if (!canFetch.value) {
    rows.value = []
    error.value = GUIDANCE_MSG
    return
  }
  loading.value = true
  renderPending.value = true
  try {
    const productRes = await api.request({
      method: 'GET',
      url: '/order/price-list/products',
      params: requestParams(page),
      timeout: 180000
    })
    const products = (Array.isArray(productRes?.data) ? productRes.data : []).map(mapProductRow)
    totalCount.value = Number(productRes?.headers?.['x-total-count'] || products.length || 0)
    totalPages.value = Math.max(1, Number(productRes?.headers?.['x-total-pages'] || 1))
    currentPage.value = Math.max(1, Number(productRes?.headers?.['x-page'] || page))

    const codes = products.map((x) => x.productCode).filter(Boolean)
    let variants = []
    if (codes.length > 0) {
      const variantRes = await api.request({
        method: 'GET',
        url: '/order/price-list/variant-rows',
        params: { product_code: codes.join(',') },
        timeout: 180000
      })
      variants = Array.isArray(variantRes?.data) ? variantRes.data : []
    }
    rows.value = buildRows(products, variants)
    error.value = ''
    void loadImagesForRows(rows.value)
    await nextTick()
  } catch (err) {
    rows.value = []
    error.value = err?.response?.data || err?.message || 'Fiyat listesi alinamadi'
    Notify.create({ type: 'negative', message: error.value })
  } finally {
    loading.value = false
    setTimeout(() => { renderPending.value = false }, 120)
  }
}

async function loadImagesForRows (list) {
  const targets = []
  const seen = new Set()
  for (const row of list) {
    const key = `${row.productCode}|${row.dim1 || 0}|${row.dim3 || 0}`
    if (!row.productCode || seen.has(key)) continue
    seen.add(key)
    targets.push({ row, key })
  }
  const concurrency = 12
  let cursor = 0
  let loaded = 0
  const workers = Array.from({ length: Math.min(concurrency, targets.length) }, async () => {
    for (;;) {
      const target = targets[cursor]
      cursor += 1
      if (!target) return
      const { row, key } = target
    if (imageCache.has(key)) {
      row.imageUrl = imageCache.get(key)
      continue
    }
    try {
      const res = await api.get('/product-images', {
        params: {
          code: row.productCode,
          dim1_id: row.dim1 || '',
          dim3_id: row.dim3 || ''
        },
        timeout: 15000
      })
      const first = Array.isArray(res?.data) ? res.data[0] : null
      const url = resolveProductImageUrl(first)
      imageCache.set(key, url)
      row.imageUrl = url
      imageListCache.set(key, Array.isArray(res?.data) ? res.data : [])
    } catch {
      imageCache.set(key, '')
    }
      loaded += 1
      if (loaded % 12 === 0) rows.value = [...rows.value]
    }
  })
  await Promise.all(workers)
  rows.value = [...rows.value]
}

function normalizeUploadsPath (storagePath) {
  const raw = toText(storagePath)
  if (!raw) return ''
  const normalized = raw.replace(/\\/g, '/')
  const idx = normalized.toLowerCase().indexOf('/uploads/')
  if (idx >= 0) return normalized.slice(idx)
  if (normalized.toLowerCase().startsWith('uploads/')) return `/${normalized}`
  return ''
}

function resolveProductImageUrl (item) {
  if (!item || typeof item !== 'object') return ''
  const imageId = Number(item.id || item.ID || 0)
  if (Number.isFinite(imageId) && imageId > 0) return `/api/product-images/${imageId}/content`
  const thumbUrl = toText(item.thumb_url || item.thumbUrl)
  if (thumbUrl) return thumbUrl
  const fullUrl = toText(item.full_url || item.fullUrl)
  if (fullUrl) return fullUrl
  const contentUrl = toText(item.content_url || item.ContentURL)
  if (contentUrl) return contentUrl.startsWith('/api/') ? contentUrl : contentUrl
  const uploadsPath = normalizeUploadsPath(item.storage_path || item.storage)
  if (uploadsPath) return uploadsPath
  const fileName = toText(item.file_name || item.FileName)
  return fileName ? `/uploads/image/${fileName}` : ''
}

async function fetchImageListForRow (row) {
  const key = `${row.productCode}|${row.dim1 || 0}|${row.dim3 || 0}`
  if (imageListCache.has(key)) return imageListCache.get(key) || []
  const res = await api.get('/product-images', {
    params: {
      code: row.productCode,
      dim1_id: row.dim1 || '',
      dim3_id: row.dim3 || ''
    },
    timeout: 15000
  })
  const list = Array.isArray(res?.data) ? res.data : []
  imageListCache.set(key, list)
  return list
}

async function openProductCard (row) {
  if (!row) return
  productCardData.value = { ...row }
  productCardSizeRows.value = []
  productCardDialog.value = true
  productCardSlide.value = 0
  productCardStockLoading.value = true
  try {
    const [list, stockRes] = await Promise.all([
      fetchImageListForRow(row),
      api.get('/product-stock-query', { params: { code: row.productCode }, timeout: 30000 })
    ])
    const images = list.map(resolveProductImageUrl).filter(Boolean)
    if (row.imageUrl && !images.includes(row.imageUrl)) images.unshift(row.imageUrl)
    productCardImages.value = Array.from(new Set(images))
    productCardSizeRows.value = buildSizeStockRows(Array.isArray(stockRes?.data) ? stockRes.data : [], row)
  } catch {
    productCardImages.value = row.imageUrl ? [row.imageUrl] : []
    productCardSizeRows.value = []
  } finally {
    productCardStockLoading.value = false
  }
}

function openProductImageFullscreen (src) {
  const value = toText(src)
  if (!value) return
  const idx = Math.max(0, fullscreenImages.value.findIndex((x) => toText(x) === value))
  productImageFullscreenSlide.value = idx
  productImageFullscreenDialog.value = true
}

function onProductCardDialogHide () {
  productImageFullscreenDialog.value = false
  productCardStockLoading.value = false
  productCardSizeRows.value = []
}

function resetSelections () {
  topUrunIlkGrubu.value = null
  topUrunAnaGrubu.value = null
  selectedProductCodes.value = []
  selectedCampaignLabels.value = []
  selectedVariantCodes.value = []
  columnFilters.value = {}
  columnFilterSearch.value = {}
  rows.value = []
  error.value = GUIDANCE_MSG
  currentPage.value = 1
  totalPages.value = 1
  totalCount.value = 0
}

function onPageChange (page) {
  void reloadData({ page })
}

function togglePriceOption (value) {
  const set = new Set(selectedPriceOptions.value || [])
  if (set.has(value)) set.delete(value)
  else set.add(value)
  selectedPriceOptions.value = priceOptions.map((x) => x.value).filter((x) => set.has(x))
}

function selectAllPrices () {
  selectedPriceOptions.value = priceOptions.map((x) => x.value)
}

function clearAllPrices () {
  selectedPriceOptions.value = []
}

function col (name, label, field, width, extra = {}) {
  return {
    name,
    label,
    field,
    align: extra.align || 'left',
    sortable: !!extra.sortable,
    style: `width:${width}px;min-width:${width}px;max-width:${width}px;`,
    headerStyle: `width:${width}px;min-width:${width}px;max-width:${width}px;`,
    classes: extra.classes || '',
    headerClasses: extra.headerClasses || extra.classes || ''
  }
}

const allColumns = [
  col('image', '', 'imageUrl', 108, { align: 'center', classes: 'image-col sticky-col' }),
  col('brandGroupSelection', 'MARKA GRUBU', 'brandGroupSelection', 86, { classes: 'ps-col sticky-col' }),
  col('marka', 'MARKA', 'marka', 72, { sortable: true, classes: 'ps-col sticky-col' }),
  col('productCode', 'URUN KODU', 'productCode', 112, { sortable: true, classes: 'ps-col product-code-col sticky-col' }),
  col('variantCodes', 'VARYANT', 'variantCodes', 82, { align: 'center', classes: 'ps-col variant-col sticky-col center-col' }),
  col('variantStocks', 'STOK', 'stockQty', 64, { align: 'center', sortable: true, classes: 'ps-col variant-stock-col sticky-col center-col' }),
  col('campaignLabel', 'KAMPANYA', 'campaignLabel', 118, { align: 'center', classes: 'ps-col campaign-col sticky-col center-col' }),
  col('campaignRate', 'IND %', 'campaignRate', 56, { align: 'center', classes: 'ps-col campaign-rate-col sticky-col center-col' }),
  col('askiliYan', 'ASKILI YAN', 'askiliYan', 58, { sortable: true, classes: 'ps-col center-col' }),
  col('kategori', 'KATEGORI', 'kategori', 72, { sortable: true, classes: 'ps-col center-col' }),
  col('urunIlkGrubu', 'URUN ILK GRUBU', 'urunIlkGrubu', 72, { sortable: true, classes: 'ps-col center-col' }),
  col('urunAnaGrubu', 'URUN ANA GRUBU', 'urunAnaGrubu', 84, { sortable: true, classes: 'ps-col center-col' }),
  col('urunAltGrubu', 'URUN ALT GRUBU', 'urunAltGrubu', 84, { sortable: true, classes: 'ps-col center-col' }),
  col('icerik', 'ICERIK', 'icerik', 92, { sortable: true, classes: 'ps-col' }),
  col('karisim', 'KARISIM', 'karisim', 88, { sortable: true, classes: 'ps-col karisim-wrap-col' }),
  ...campaignPairs.flatMap((p) => [
    col(p.base, p.base.toUpperCase().replace(/([A-Z]+)(\d)/, '$1 $2'), p.base, 78, { align: 'right', classes: `${p.base.slice(0, 3)}-col` }),
    col(p.derived, `${p.base.toUpperCase().replace(/([A-Z]+)(\d)/, '$1 $2')} KMP`, p.derived, 88, { align: 'right', classes: `${p.base.slice(0, 3)}-col campaign-price-col` })
  ])
]

const compactHiddenColumnNames = new Set([
  'variantStocks',
  'campaignLabel',
  'campaignRate',
  'askiliYan',
  'kategori',
  'urunIlkGrubu',
  'urunAnaGrubu',
  'urunAltGrubu',
  'icerik',
  'karisim'
])

const visibleColumns = computed(() => allColumns.filter((c) => {
  if (/^(usd|eur|try)[1-6]$/.test(c.name)) return selectedPriceSet.value.has(c.name)
  if (/^(usd|eur|try)[1-6]Campaign$/.test(c.name)) return selectedPriceSet.value.has(c.name.replace(/Campaign$/, ''))
  if (!leftDetailsExpanded.value && compactHiddenColumnNames.has(c.name)) return false
  return true
}))

const filteredRows = computed(() => {
  const campaignSet = selectedCampaignLabelSet.value
  const variantSet = selectedVariantCodeSet.value
  const localFilters = columnFilters.value || {}
  let list = rows.value || []
  if (showInStockOnly.value) {
    list = list.filter((row) => Number(row?.stockQty ?? 0) > 0)
  }
  if (campaignSet.size > 0) {
    list = list.filter((row) => campaignSet.has(toText(row?.campaignLabel)))
  }
  if (variantSet.size > 0) {
    list = list.filter((row) => variantSet.has(toText(row?.variantCodes)))
  }
  const active = Object.entries(localFilters).filter(([, values]) => Array.isArray(values) && values.length > 0)
  if (active.length > 0) {
    const byName = new Map(allColumns.map((c) => [c.name, c]))
    list = list.filter((row) => active.every(([name, values]) => {
      const col = byName.get(name)
      if (!col) return true
      return new Set(values).has(exportCell(row, col))
    }))
  }
  return list
})
const tableMinWidth = computed(() => visibleColumns.value.reduce((sum, c) => sum + extractWidth(c.style), 0))
const tableStyle = computed(() => ({
  width: `${tableMinWidth.value}px`,
  minWidth: `${tableMinWidth.value}px`,
  tableLayout: 'fixed'
}))
const stickyColumnNames = computed(() => {
  const visible = new Set(visibleColumns.value.map((x) => x.name))
  const expanded = [
    'image',
    'brandGroupSelection',
    'marka',
    'productCode',
    'variantCodes',
    'variantStocks',
    'campaignLabel',
    'campaignRate',
    'askiliYan',
    'kategori',
    'urunIlkGrubu',
    'urunAnaGrubu',
    'urunAltGrubu',
    'icerik',
    'karisim'
  ]
  const compact = ['image', 'brandGroupSelection', 'marka', 'productCode', 'variantCodes']
  return (leftDetailsExpanded.value ? expanded : compact).filter((x) => visible.has(x))
})
const stickyBoundaryColumnName = computed(() => {
  const list = stickyColumnNames.value
  return list.length ? list[list.length - 1] : ''
})
const stickyColumnNameSet = computed(() => new Set(stickyColumnNames.value))
const stickyLeftMap = computed(() => {
  const map = {}
  let left = 0
  for (const name of stickyColumnNames.value) {
    const colDef = allColumns.find((x) => x.name === name)
    if (!colDef) continue
    map[name] = left
    left += extractWidth(colDef.style)
  }
  return map
})
const stickyScrollComp = computed(() => {
  const boundaryName = stickyBoundaryColumnName.value
  const boundaryCol = allColumns.find((x) => x.name === boundaryName)
  return ((stickyLeftMap.value[boundaryName] || 0) + extractWidth(boundaryCol?.style)) * 1.2
})

function isStickyCol (name) {
  return stickyColumnNameSet.value.has(name)
}

function isStickyBoundary (name) {
  return name === stickyBoundaryColumnName.value
}

function getHeaderCellStyle (col) {
  const base = col.headerStyle || col.style || ''
  if (!isStickyCol(col.name)) return base
  return `${base};left:${stickyLeftMap.value[col.name] || 0}px;`
}

function getBodyCellStyle (col) {
  const base = col.style || ''
  if (!isStickyCol(col.name)) return base
  return `${base};left:${stickyLeftMap.value[col.name] || 0}px;`
}

function extractWidth (style) {
  const m = String(style || '').match(/width:(\d+)px/)
  return m ? Number(m[1]) : 80
}

function exportCell (row, col) {
  if (col.name === 'image') return row.imageUrl || ''
  if (priceColumnNames.includes(col.name)) return formatPrice(row[col.field])
  if (col.name === 'variantStocks') return formatStock(row.stockQty)
  if (col.name === 'campaignRate') return row.campaignRate ? formatPrice(row.campaignRate) : ''
  return toText(row[col.field])
}

function isExcelNumericColumn (col) {
  return priceColumnNames.includes(col.name) || col.name === 'campaignRate'
}

function excelNumericCell (value) {
  const n = Number(value)
  if (!Number.isFinite(n) || n === 0) return '<td></td>'
  const display = n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
  const raw = n.toFixed(2)
  return `<td style="mso-number-format:'0.00';text-align:right;" x:num="${raw}">${escapeHtml(display)}</td>`
}

function exportExcelCellHtml (row, col) {
  if (priceColumnNames.includes(col.name)) return excelNumericCell(row[col.field])
  if (col.name === 'campaignRate') return excelNumericCell(row.campaignRate)
  return `<td>${escapeHtml(exportCell(row, col))}</td>`
}

function exportVisibleExcel () {
  const cols = visibleColumns.value.filter((c) => c.name !== 'image')
  const body = filteredRows.value.map((row) => `<tr>${cols.map((c) => {
    return exportExcelCellHtml(row, c)
  }).join('')}</tr>`).join('')
  const html = `<!doctype html><html xmlns:x="urn:schemas-microsoft-com:office:excel"><head><meta charset="utf-8"></head><body><table border="1"><thead><tr>${cols.map((c) => `<th>${escapeHtml(c.label)}</th>`).join('')}</tr></thead><tbody>${body}</tbody></table></body></html>`
  const blob = new Blob([html], { type: 'application/vnd.ms-excel;charset=utf-8' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `fiyat_listesi_${new Date().toISOString().slice(0, 10)}.xls`
  document.body.appendChild(a)
  a.click()
  a.remove()
  URL.revokeObjectURL(url)
  void notifyExportTaken('excel')
}

function printVisibleRows () {
  const cols = visibleColumns.value
  const body = filteredRows.value.map((row) => `<tr>${cols.map((c) => {
    if (c.name === 'image' && row.imageUrl) return `<td><img src="${row.imageUrl}" class="thumb"></td>`
    return `<td class="${isExcelNumericColumn(c) ? 'num' : ''}">${escapeHtml(exportCell(row, c))}</td>`
  }).join('')}</tr>`).join('')
  const html = `<!doctype html><html><head><meta charset="utf-8"><title>Fiyat Listesi</title><style>
    @page { size: A3 landscape; margin: 8mm; }
    body { font-family: Arial, sans-serif; font-size: 8px; }
    h1 { font-size: 16px; margin: 0 0 8px; }
    table { border-collapse: collapse; width: 100%; }
    th { background: #957116; color: #fff; }
    th, td { border: 1px solid #ccc; padding: 3px; vertical-align: middle; }
    .num { text-align: right; }
    .thumb { width: 100px; height: 100px; object-fit: cover; }
  </style></head><body><h1>Fiyat Listesi</h1><table><thead><tr>${cols.map((c) => `<th>${escapeHtml(c.label || 'Gorsel')}</th>`).join('')}</tr></thead><tbody>${body}</tbody></table><script>window.onload=function(){window.print()}<\/script></body></html>`
  const win = window.open('', '_blank')
  if (!win) return
  win.document.open()
  win.document.write(html)
  win.document.close()
  void notifyExportTaken('pdf')
}

async function notifyExportTaken (format) {
  try {
    await api.post('/order/price-list/export-notify', {
      format,
      row_count: filteredRows.value.length,
      price_fields: [...selectedPriceOptions.value],
      product_codes: [...selectedProductCodes.value],
      campaign_labels: [...selectedCampaignLabels.value],
      first_groups: Array.from(new Set(filteredRows.value.map((row) => toText(row.urunIlkGrubu)).filter(Boolean))).sort((a, b) => a.localeCompare(b, 'tr')),
      urun_ilk_grubu: topUrunIlkGrubu.value || '',
      urun_ana_grubu: topUrunAnaGrubu.value || ''
    }, { timeout: 30000 })
  } catch (err) {
    console.warn('[order-price-list][ui] export notify failed', err?.response?.data || err?.message || err)
  }
}

function getTableMiddleEl () {
  return mainTableRef.value?.$el?.querySelector?.('.q-table__middle') || null
}

function onTopScroll () {
  if (syncingScroll) return
  const middle = getTableMiddleEl()
  const top = topScrollRef.value
  if (!middle || !top) return
  syncingScroll = true
  middle.scrollLeft = top.scrollLeft
  requestAnimationFrame(() => { syncingScroll = false })
}

function bindTableScrollSync () {
  const middle = getTableMiddleEl()
  if (!middle || middle.__orderPriceListScrollBound) return
  middle.__orderPriceListScrollBound = true
  middle.addEventListener('scroll', () => {
    if (syncingScroll) return
    const top = topScrollRef.value
    if (!top) return
    syncingScroll = true
    top.scrollLeft = middle.scrollLeft
    requestAnimationFrame(() => { syncingScroll = false })
  }, { passive: true })
}

function escapeHtml (value) {
  return String(value ?? '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}

watch(selectedProductCodes, (list) => {
  const clean = Array.isArray(list) ? [...new Set(list.map(toText).filter(Boolean))] : []
  if (clean.join('\u0001') !== (Array.isArray(list) ? list.join('\u0001') : '')) {
    selectedProductCodes.value = clean
  }
})

watch([tableMinWidth, rows], async () => {
  await nextTick()
  bindTableScrollSync()
})

onMounted(() => {
  void fetchServerFilterOptions('urunIlkGrubu', '')
  void fetchServerFilterOptions('urunAnaGrubu', '')
  void fetchServerFilterOptions('productCode', '')
  void nextTick(bindTableScrollSync)
})
</script>

<style scoped>
.order-price-list-page {
  position: relative;
  height: calc(100vh - 58px);
  display: flex;
  flex-direction: column;
  --pricing-row-height: 108px;
  --pricing-header-height: 88px;
  --pricing-table-height: calc(100vh - 156px);
}

.top-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  flex: 1 1 auto;
  min-width: 0;
}

.top-actions-row {
  width: 100%;
  justify-content: flex-start;
}

.top-bar {
  position: sticky;
  top: 0;
  z-index: 60;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
  background: #fff;
  padding-top: 2px;
  padding-bottom: 4px;
}

.top-actions-row--filters,
.top-actions-row--actions {
  justify-content: flex-start;
}

.toolbar-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 4px;
  background: #fff;
}

.toolbar-group--paging {
  gap: 10px;
}

.toolbar-group :deep(.q-btn) {
  min-height: 32px;
  white-space: nowrap;
}

.toolbar-group :deep(.q-btn__content) {
  gap: 6px;
  padding: 0;
}

.toolbar-group :deep(.q-btn__wrapper) {
  padding: 4px 10px;
}

.table-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  --top-scroll-height: 22px;
}

.product-thumb {
  width: 100px;
  height: 100px;
  border-radius: 4px;
  background: #f4f4f4;
}

.product-thumb-placeholder {
  width: 100px;
  height: 100px;
  border-radius: 4px;
  background: #f4f4f4;
  border: 1px dashed rgba(0, 0, 0, 0.16);
  display: flex;
  align-items: center;
  justify-content: center;
}

.image-cell {
  padding: 4px !important;
}

.top-x-scroll {
  flex: 0 0 var(--top-scroll-height);
  height: var(--top-scroll-height);
  overflow-x: auto;
  overflow-y: hidden;
  background: #f8fafc;
  border-bottom: 1px solid rgba(0, 0, 0, 0.14);
  scrollbar-gutter: stable;
  z-index: 20;
}

.top-x-scroll-inner {
  height: 18px;
}

.top-x-scroll::-webkit-scrollbar {
  height: 14px;
}

.top-x-scroll::-webkit-scrollbar-track {
  background: #edf1f5;
}

.top-x-scroll::-webkit-scrollbar-thumb {
  background: rgba(25, 118, 210, 0.42);
  border: 3px solid #edf1f5;
  border-radius: 999px;
}

.pane-table {
  height: 100%;
  width: 100%;
}

.pricing-table :deep(.q-table__middle) {
  height: calc(var(--pricing-table-height) - var(--top-scroll-height));
  min-height: calc(var(--pricing-table-height) - var(--top-scroll-height));
  max-height: calc(var(--pricing-table-height) - var(--top-scroll-height));
  overflow: auto !important;
  scrollbar-gutter: stable both-edges;
  overscroll-behavior: contain;
}

.pricing-table :deep(.q-table) {
  width: max-content;
  min-width: 100%;
  table-layout: fixed;
  font-size: 11px;
  border-collapse: separate;
  border-spacing: 0;
  margin-right: var(--sticky-scroll-comp, 0px);
}

.pricing-table :deep(.q-table__container) {
  border: none !important;
  box-shadow: none !important;
  background: transparent !important;
  height: 100% !important;
}

.pricing-table :deep(td),
.pricing-table :deep(.q-table tbody tr) {
  height: var(--pricing-row-height) !important;
  min-height: var(--pricing-row-height) !important;
  max-height: var(--pricing-row-height) !important;
  line-height: 1.25;
  padding: 0 !important;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08) !important;
}

.pricing-table :deep(td > div),
.pricing-table :deep(td > .q-td) {
  height: 100% !important;
  display: flex !important;
  align-items: center !important;
}

.pricing-table :deep(td) {
  overflow: hidden !important;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle !important;
}

.pricing-table :deep(th),
.pricing-table :deep(.q-table thead tr),
.pricing-table :deep(.q-table thead tr.header-row-fixed),
.pricing-table :deep(.q-table thead th),
.pricing-table :deep(.q-table thead tr.header-row-fixed > th) {
  height: var(--pricing-header-height) !important;
  min-height: var(--pricing-header-height) !important;
  max-height: var(--pricing-header-height) !important;
}

.pricing-table :deep(th) {
  padding-top: 0;
  padding-bottom: 0;
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
  overflow: hidden;
  text-align: center;
  font-size: 10px;
  font-weight: 800;
  line-height: 1.15;
}

.pricing-table :deep(.q-table thead th) {
  position: sticky;
  top: 0;
  z-index: 30;
  background: #fff;
  vertical-align: middle !important;
}

.pricing-table :deep(.sticky-col) {
  position: sticky !important;
  background-clip: padding-box;
}

.pricing-table :deep(thead .sticky-col) {
  z-index: 35 !important;
}

.pricing-table :deep(tbody .sticky-col) {
  z-index: 12 !important;
  background: #fff !important;
}

.pricing-table :deep(.sticky-boundary) {
  border-right: 2px solid rgba(25, 118, 210, 0.18) !important;
  box-shadow: 8px 0 12px -10px rgba(15, 23, 42, 0.55);
}

.pricing-table :deep(tbody td:not(.sticky-col)) {
  position: relative;
  z-index: 1 !important;
}

.pricing-table :deep(tbody td.sticky-col)::after,
.pricing-table :deep(thead th.sticky-col)::after {
  content: '';
  position: absolute;
  inset: 0;
  background: inherit;
  z-index: -1;
}

.pricing-table :deep(th.ps-col),
.pricing-table :deep(td.ps-col) {
  background: #fff;
  color: var(--q-primary);
  font-weight: 700;
}

.pricing-table :deep(td.ps-col),
.pricing-table :deep(td.usd-col),
.pricing-table :deep(td.eur-col),
.pricing-table :deep(td.try-col) {
  vertical-align: middle !important;
}

.pricing-table :deep(th.usd-col),
.pricing-table :deep(td.usd-col) {
  background: #ecf9f0;
  color: #178a3e;
  font-weight: 700;
}

.pricing-table :deep(th.eur-col),
.pricing-table :deep(td.eur-col) {
  background: #fdeeee;
  color: #c62828;
  font-weight: 700;
}

.pricing-table :deep(th.try-col),
.pricing-table :deep(td.try-col) {
  background: #edf4ff;
  color: #1e63c6;
  font-weight: 700;
}

.pricing-table :deep(th.usd-col),
.pricing-table :deep(th.eur-col),
.pricing-table :deep(th.try-col),
.pricing-table :deep(td.usd-col),
.pricing-table :deep(td.eur-col),
.pricing-table :deep(td.try-col) {
  font-size: 10px;
}

.pricing-table :deep(td.campaign-price-col),
.pricing-table :deep(th.campaign-price-col) {
  background: #fff3f1;
  color: #c62828;
  font-weight: 800;
  letter-spacing: 0;
}

.pricing-table :deep(td.center-col) {
  text-align: center !important;
}

.pricing-table :deep(td.karisim-wrap-col) {
  white-space: normal !important;
  text-overflow: clip !important;
  word-break: break-word;
  overflow-wrap: anywhere;
  vertical-align: top !important;
  text-align: left !important;
  font-size: 9px;
  line-height: 1.15;
  padding: 6px 4px !important;
  align-content: flex-start;
}

.pricing-table :deep(td.karisim-wrap-col .q-td__content),
.pricing-table :deep(td.karisim-wrap-col > div) {
  align-items: flex-start !important;
  justify-content: flex-start !important;
  white-space: normal !important;
  overflow: hidden;
}

.header-with-filter {
  display: grid;
  grid-template-columns: 1fr 20px;
  align-items: center;
  column-gap: 4px;
  height: 100%;
  line-height: 1.25;
  overflow: hidden;
}

.header-with-filter > span {
  min-width: 0;
  width: 100%;
  overflow: hidden;
  text-align: center;
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
  font-weight: 800;
  line-height: 1.12;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
}

.cell-text {
  display: block;
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  line-height: 1.1;
  padding-top: 0;
}

.price-cell-text {
  width: 100%;
  text-align: right;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.campaign-price-text {
  color: #c62828;
  font-weight: 900;
}

.campaign-cell-content {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 22px;
}

.campaign-text {
  display: block;
  max-width: 100%;
  color: #c62828;
  font-weight: 900;
  line-height: 1.12;
  text-align: center;
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.campaign-rate-text {
  width: 100%;
  color: #c62828;
  font-weight: 900;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.header-filter-btn {
  width: 20px;
  height: 20px;
  min-width: 20px;
  justify-self: end;
}

.header-filter-ghost {
  opacity: 0;
  pointer-events: none;
}

.excel-filter-menu,
.filter-menu-panel {
  min-width: 230px;
  padding: 8px;
}

.excel-filter-select :deep(.q-field__control) {
  min-height: 30px;
}

.excel-filter-options {
  max-height: 220px;
  margin-top: 8px;
  overflow: auto;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 4px;
}

.excel-filter-option {
  min-height: 32px;
}

.excel-filter-empty {
  padding: 10px 8px;
  color: #607d8b;
  font-size: 11px;
}

.page-busy-overlay {
  position: fixed;
  inset: 0;
  z-index: 30000;
  background: rgba(255, 255, 255, 0.68);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 10px;
  pointer-events: all;
}

.page-busy-label {
  font-weight: 600;
  color: #4c4c4c;
}

.empty-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.72);
}

.empty-overlay-inner {
  border: 1px solid #ddd;
  background: #fff;
  border-radius: 6px;
  padding: 16px 20px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.product-card-dialog {
  --pc-media-h: calc(100vh - 180px);
  --pc-media-w: min(74vw, 1220px);
  background: #f9f8f5;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

:deep(.product-card-dialog > .q-card__section:last-child) {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.product-card-content {
  display: grid;
  grid-template-columns: minmax(360px, 420px) minmax(760px, 1fr);
  gap: 14px;
  align-items: stretch;
  justify-content: start;
  height: 100%;
}

.product-card-images {
  grid-column: 2;
  grid-row: 1;
  height: var(--pc-media-h);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
}

.product-card-carousel {
  width: var(--pc-media-w);
  height: 100%;
  max-width: 100%;
  background: linear-gradient(180deg, #f6f1e6 0%, #efe6d3 100%);
  border: 1px solid #e4dac7;
}

.dialog-image {
  width: 100%;
  height: 100%;
}

.dialog-image-stage {
  width: var(--pc-media-w);
  max-width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 10px;
  background: linear-gradient(180deg, #f6f1e6 0%, #efe6d3 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.dialog-image-empty {
  width: var(--pc-media-w);
  max-width: 100%;
  height: var(--pc-media-h);
  border: 1px dashed #c5b28d;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #faf6ee;
}

.product-card-fields {
  grid-column: 1;
  grid-row: 1;
  border: 1px solid #e4dac7;
  border-radius: 12px;
  background: linear-gradient(180deg, #ffffff 0%, #fdfaf4 100%);
  padding: 12px;
  height: var(--pc-media-h);
  overflow: auto;
}

.field-row {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid #efe5d5;
  font-size: 13px;
}

.field-row.field-row-head {
  background: #f8f3e9;
  border: 1px solid #e6dccb;
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 8px;
}

.field-row:last-child {
  border-bottom: none;
}

.field-row .k {
  color: #6b5a33;
  font-weight: 700;
}

.field-row .v {
  color: #1f1f1f;
  word-break: break-word;
}

.product-card-section {
  position: relative;
  margin-top: 12px;
  border: 1px solid #e6dccb;
  border-radius: 8px;
  background: #fffdf8;
  padding: 10px;
}

.product-card-section-title {
  font-size: 13px;
  font-weight: 800;
  color: #5e4a22;
  margin-bottom: 8px;
}

.price-info-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
}

.price-info-row {
  display: grid;
  grid-template-columns: 70px 1fr 1fr;
  gap: 6px;
  align-items: center;
  min-height: 26px;
  padding: 4px 6px;
  border: 1px solid #f0e5d2;
  border-radius: 6px;
  background: #fff;
  font-size: 12px;
}

.price-label {
  font-weight: 800;
  color: #6b5a33;
}

.price-value,
.price-campaign {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.price-campaign {
  color: #b13a2b;
  font-weight: 700;
}

.size-stock-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(62px, 1fr));
  gap: 6px;
}

.size-stock-cell {
  min-height: 42px;
  border: 1px solid #eadfca;
  border-radius: 6px;
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4px;
}

.size-label {
  font-size: 11px;
  font-weight: 800;
  color: #6b5a33;
}

.size-qty {
  font-size: 13px;
  font-weight: 800;
  color: #1f1f1f;
  font-variant-numeric: tabular-nums;
}

.product-card-empty-text {
  color: #7a6d55;
  font-size: 12px;
  padding: 6px 0;
}

.image-fullscreen-dialog {
  background: #f4f0e2;
}

.image-fullscreen-body {
  height: calc(100vh - 72px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.image-fullscreen-carousel {
  width: min(98vw, 1500px);
}

.image-fullscreen-stage {
  width: min(96vw, 1400px);
  height: calc(100vh - 120px);
  border-radius: 10px;
  background: linear-gradient(180deg, #f1e7d3 0%, #e9dcc4 100%);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.image-fullscreen-img {
  width: 100%;
  height: 100%;
}

.q-btn,
.q-icon,
.cursor-pointer {
  cursor: pointer !important;
}

@media (max-width: 1024px) {
  .product-card-content {
    grid-template-columns: 1fr;
  }

  .product-card-images,
  .product-card-fields {
    grid-column: 1;
    grid-row: auto;
  }
}
</style>
