<template>
  <q-page class="q-pa-xs pricing-page">
    <q-inner-loading :showing="pageBusy">
      <q-spinner-gears size="52px" color="primary" />
    </q-inner-loading>
    <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">Urun Fiyatlandirma</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
            :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
            multiple
            use-chips
            emit-value
            map-options
            :options="topUrunAnaGrubuOptions"
            :loading="Boolean(serverFilterLoading.urunAnaGrubu)"
            :disable="pageBusy"
            label="Urun Ana Grubu (max 3)"
            style="min-width: 260px"
            @filter="onTopFilterSearchUrunAnaGrubu"
            @update:model-value="onTopUrunAnaGrubuChange"
          />
          <q-btn
            color="primary"
            icon="filter_alt"
            label="Gruplari Getir"
            :disable="pageBusy || !canFetchByGroup"
            :loading="store.loading"
            @click="reloadData({ page: 1 })"
          />
          <q-btn
            flat
            color="grey-7"
            icon="restart_alt"
            label="Secimleri Sifirla"
            :disable="pageBusy"
            @click="resetGroupSelections"
          />
        </div>

        <div class="row items-center q-gutter-xs top-actions-row top-actions-row--actions">
          <div class="toolbar-group">
            <q-btn
              dense
              flat
              color="grey-8"
              icon="view_sidebar"
              :label="leftDetailsExpanded ? 'Detaylari Gizle' : 'Detaylari Goster'"
              :disable="pageBusy"
              @click="leftDetailsExpanded = !leftDetailsExpanded"
            />
            <q-btn-dropdown dense color="secondary" outline icon="view_module" label="Doviz Gorunumu" :auto-close="false" :disable="pageBusy">
              <q-list dense class="currency-menu-list">
                <q-item clickable @click="selectAllCurrencies">
                  <q-item-section>Tumunu Sec</q-item-section>
                </q-item>
                <q-item clickable @click="clearAllCurrencies">
                  <q-item-section>Tumunu Temizle</q-item-section>
                </q-item>
                <q-separator />
                <q-item v-for="option in currencyOptions" :key="option.value" clickable @click="toggleCurrencyRow(option.value)">
                  <q-item-section avatar>
                    <q-checkbox
                      :model-value="isCurrencySelected(option.value)"
                      dense
                      :disable="pageBusy"
                      @update:model-value="(val) => toggleCurrency(option.value, val)"
                      @click.stop
                    />
                  </q-item-section>
                  <q-item-section>{{ option.label }}</q-item-section>
                </q-item>
              </q-list>
            </q-btn-dropdown>
            <q-toggle
              v-model="showInStockOnly"
              dense
              color="primary"
              label="Sadece stogu olanlar"
              :disable="pageBusy"
            />
          </div>

          <div class="toolbar-group">
            <q-btn
              dense
              flat
              :color="showSelectedOnly ? 'primary' : 'grey-7'"
              :icon="showSelectedOnly ? 'checklist_rtl' : 'list_alt'"
              :label="showSelectedOnly ? `Secililer (${selectedRowCount})` : 'Secili Olanlari Getir'"
              :disable="pageBusy || (!showSelectedOnly && selectedRowCount === 0)"
              @click="toggleShowSelectedOnly"
            />
            <q-btn
              dense
              color="primary"
              outline
              icon="calculate"
              label="Secilileri Hesapla"
              :disable="pageBusy || selectedRowCount === 0 || bulkCalcLoading"
              :loading="bulkCalcLoading"
              @click="calculateSelectedRows"
            />
            <q-btn
              dense
              color="primary"
              icon="save"
              :label="saveButtonLabel"
              :disable="pageBusy || selectedDirtyCount === 0 || saving"
              :loading="saving"
              @click="saveSelectedRows"
            />
          </div>

          <div class="toolbar-group">
            <q-btn-dropdown dense color="primary" outline icon="download" label="Cikti Al" :auto-close="true" :disable="pageBusy">
              <q-list dense style="min-width: 260px;">
                <q-item clickable :disable="filteredRows.length === 0" @click="exportCurrentView">
                  <q-item-section avatar><q-icon name="grid_on" /></q-item-section>
                  <q-item-section>Sayfayi Excel'e Aktar</q-item-section>
                </q-item>
                <q-item clickable :disable="filteredRows.length === 0 || exportAllLoading" @click="exportAllFiltered">
                  <q-item-section avatar><q-icon name="download_for_offline" /></q-item-section>
                  <q-item-section>Tum Filtreyi Excel'e Aktar</q-item-section>
                </q-item>
                <q-separator />
                <q-item clickable :disable="store.loading" @click="openPriceListExportDialog()">
                  <q-item-section avatar><q-icon name="receipt_long" /></q-item-section>
                  <q-item-section>Fiyat Listesi Ciktisi...</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, store.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, store.totalPages || 1) }} - Toplam {{ store.totalCount || 0 }} urun kodu
            </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">Calismaya Baslamak Icin</div>
          <div class="text-body2 q-mt-xs">
            Urun Ilk Grubu veya Urun Ana Grubu secin ve <b>GRUPLARI GETIR</b>'e basin.
          </div>
        </div>
      </div>
      <div
        ref="topScrollRef"
        class="top-x-scroll"
        @scroll.passive="onTopScroll"
      >
        <div
          ref="topScrollInnerRef"
          class="top-x-scroll-inner"
          :style="{ width: `${tableMinWidth}px` }"
        />
      </div>
      <q-table
        ref="mainTableRef"
        class="pane-table pricing-table"
        flat
        dense
        row-key="productCode"
        :rows="filteredRows"
        :columns="visibleColumns"
        :loading="tableLoading"
        :rows-per-page-options="[0]"
        :pagination="tablePagination"
        hide-bottom
        :table-style="tableStyle"
        @update:pagination="onPaginationChange"
      >
        <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)"
            >
              <q-checkbox
                v-if="col.name === 'select'"
                size="sm"
                color="primary"
                :model-value="allSelectedVisible"
                :indeterminate="someSelectedVisible && !allSelectedVisible"
                :disable="pageBusy"
                @update:model-value="toggleSelectAllVisible"
              />
              <div v-else class="header-with-filter">
                <span :title="col.label">{{ col.label }}</span>
                <q-tooltip
                  v-if="col.label"
                  anchor="top middle"
                  self="bottom middle"
                  :offset="[0, 6]"
                >
                  {{ col.label }}
                </q-tooltip>
                <q-btn
                  v-if="isHeaderFilterField(col.field)"
                  dense
                  flat
                  round
                  size="8px"
                  icon="filter_alt"
                  :color="hasFilter(col.field) ? 'primary' : 'grey-7'"
                  :disable="pageBusy"
                  class="header-filter-btn"
                >
                  <q-badge v-if="hasFilter(col.field)" color="primary" floating rounded>
                    {{ getFilterBadgeValue(col.field) }}
                  </q-badge>
                  <q-menu
                    anchor="bottom right"
                    self="top right"
                    :offset="[0, 4]"
                    @before-show="() => onFilterMenuBeforeShow(col.field)"
                  >
                    <div v-if="isMultiSelectFilterField(col.field)" class="excel-filter-menu">
                      <q-input
                        v-model="columnFilterSearch[col.field]"
                        dense
                        outlined
                        clearable
                        use-input
                        :disable="pageBusy"
                        class="excel-filter-select"
                        placeholder="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" @click="selectAllColumnFilterOptions(col.field)" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy" @click="clearColumnFilter(col.field)" />
                      </div>
                      <q-virtual-scroll
                        v-if="getFilterOptionsForField(col.field).length > 0"
                        class="excel-filter-options"
                        :items="getFilterOptionsForField(col.field)"
                        :virtual-scroll-item-size="32"
                        separator
                      >
                        <template #default="{ item: option }">
                          <q-item
                            :key="`${col.field}-${option.value}`"
                            dense
                            clickable
                            :disable="pageBusy"
                            class="excel-filter-option"
                            @click="toggleColumnFilterValue(col.field, option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                size="sm"
                                :model-value="isColumnFilterValueSelected(col.field, option.value)"
                                :disable="pageBusy"
                                @update:model-value="() => toggleColumnFilterValue(col.field, 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>
                    <div v-else-if="isValueSelectFilterField(col.field)" class="excel-filter-menu">
                      <q-input
                        v-model="valueFilterSearch[col.field]"
                        dense
                        outlined
                        clearable
                        use-input
                        :disable="pageBusy"
                        class="excel-filter-select"
                        placeholder="Deger 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" @click="selectAllColumnFilterOptions(col.field)" />
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy" @click="clearColumnFilter(col.field)" />
                      </div>
                      <q-virtual-scroll
                        v-if="getFilterOptionsForField(col.field).length > 0"
                        class="excel-filter-options"
                        :items="getFilterOptionsForField(col.field)"
                        :virtual-scroll-item-size="32"
                        separator
                      >
                        <template #default="{ item: option }">
                          <q-item
                            :key="`${col.field}-${option.value}`"
                            dense
                            clickable
                            :disable="pageBusy"
                            class="excel-filter-option"
                            @click="toggleColumnFilterValue(col.field, option.value)"
                          >
                            <q-item-section avatar>
                              <q-checkbox
                                dense
                                size="sm"
                                :model-value="isColumnFilterValueSelected(col.field, option.value)"
                                :disable="pageBusy"
                                @update:model-value="() => toggleColumnFilterValue(col.field, 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>
                    <div v-else-if="isNumberRangeFilterField(col.field)" class="excel-filter-menu">
                      <div class="range-filter-grid">
                        <q-input
                          v-model="numberRangeFilters[col.field].min"
                          dense
                          outlined
                          clearable
                          label="Min"
                          inputmode="decimal"
                          :disable="pageBusy"
                          class="range-filter-field"
                        />
                        <q-input
                          v-model="numberRangeFilters[col.field].max"
                          dense
                          outlined
                          clearable
                          label="Max"
                          inputmode="decimal"
                          :disable="pageBusy"
                          class="range-filter-field"
                        />
                      </div>
                      <div class="row justify-end q-pt-xs">
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy" @click="clearRangeFilter(col.field)" />
                      </div>
                    </div>
                    <div v-else-if="isDateRangeFilterField(col.field)" class="excel-filter-menu">
                      <div class="range-filter-grid">
                        <q-input
                          v-model="dateRangeFilters[col.field].from"
                          dense
                          outlined
                          clearable
                          type="date"
                          label="Baslangic"
                          :disable="pageBusy"
                          class="range-filter-field"
                        />
                        <q-input
                          v-model="dateRangeFilters[col.field].to"
                          dense
                          outlined
                          clearable
                          type="date"
                          label="Bitis"
                          :disable="pageBusy"
                          class="range-filter-field"
                        />
                      </div>
                      <div class="row justify-end q-pt-xs">
                        <q-btn flat dense size="sm" label="Temizle" :disable="pageBusy" @click="clearRangeFilter(col.field)" />
                      </div>
                    </div>
                  </q-menu>
                </q-btn>
                <q-btn
                  v-else
                  dense
                  flat
                  round
                  size="8px"
                  icon="filter_alt"
                  class="header-filter-btn header-filter-ghost"
                  tabindex="-1"
                />
              </div>
            </q-th>
          </q-tr>
        </template>

        <template #body-cell-select="props">
          <q-td
            :props="props"
            class="text-center selection-col"
            :class="{ 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }"
            :style="getBodyCellStyle(props.col)"
          >
            <q-checkbox
              size="sm"
              color="primary"
              :model-value="isRowSelected(rowSelectionKey(props.row))"
              :disable="pageBusy"
              @update:model-value="(val) => onRowCheckboxChange(props.row, val)"
              @click.stop
            />
          </q-td>
        </template>

        <template #body-cell-calcAction="props">
          <q-td
            :props="props"
            :class="{ 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }"
            :style="getBodyCellStyle(props.col)"
          >
            <q-btn
              dense
              size="sm"
              color="primary"
              label="Hesapla"
              :loading="!!calcLoadingMap[props.row.productCode]"
              :disable="pageBusy || !!calcLoadingMap[props.row.productCode]"
              @click="calculateRow(props.row)"
            />
          </q-td>
        </template>

        <template #body-cell-historyAction="props">
          <q-td
            :props="props"
            :class="{ 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }"
            :style="getBodyCellStyle(props.col)"
          >
            <q-btn
              dense
              flat
              round
              size="sm"
              color="grey-8"
              icon="history"
              :disable="pageBusy || !props.row?.productCode"
              @click="openPriceHistoryDialog(props.row)"
            >
              <q-tooltip anchor="top middle" self="bottom middle" :offset="[0, 6]">Fiyat gecmisi</q-tooltip>
            </q-btn>
          </q-td>
        </template>

        <template #body-cell-productCode="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <span class="product-code-text" :title="String(props.value ?? '')">{{ props.value }}</span>
          </q-td>
        </template>

        <template #body-cell-stockQty="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <span class="stock-qty-text">{{ formatStock(props.value) }}</span>
          </q-td>
        </template>

        <template #body-cell-stockEntryDate="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <span class="date-cell-text">{{ formatDateDisplay(props.value) }}</span>
          </q-td>
        </template>

        <template #body-cell-lastCostingDate="props">
          <q-td
            :props="props"
            :class="[
              { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) },
              { 'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name) },
              { 'cell-danger': needsCosting(props.row) }
            ]"
            :style="getBodyCellStyle(props.col)"
          >
            <span :class="['date-cell-text', { 'text-white': needsCosting(props.row) }]">
              {{ formatDateDisplay(props.value) }}
            </span>
            <q-tooltip v-if="needsCosting(props.row)" anchor="top middle" self="bottom middle" :offset="[0, 6]">
              Stok girisinden sonra maliyetlendirme yapilmamis. Urun Ilk Grubu: {{ props.row.urunIlkGrubu || '-' }}
            </q-tooltip>
          </q-td>
        </template>

        <template #body-cell-lastPricingDate="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <span :class="['date-cell-text', { 'date-warning': needsRepricing(props.row) }]">
              {{ formatDateDisplay(props.value) }}
            </span>
          </q-td>
        </template>

        <template #body-cell-brandGroupSelection="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <span class="cell-text" :title="props.row.brandGroupSelection || ''">
              {{ props.row.brandGroupSelection || '' }}
            </span>
          </q-td>
        </template>

        <template #body-cell="props">
          <q-td
            :props="props"
            :class="{
              'sticky-col': isStickyCol(props.col.name),
              'sticky-boundary': isStickyBoundary(props.col.name),
              'selected-tone-cell': shouldToneSelectedCell(props.row, props.col.name)
            }"
            :style="getBodyCellStyle(props.col)"
          >
            <div v-if="editableColumnSet.has(props.col.name)" class="editable-price-cell">
              <input
                class="native-cell-input text-right price-edit-input"
                :value="formatPrice(props.row[props.col.field])"
                type="text"
                inputmode="decimal"
                :disabled="pageBusy"
                @change="(e) => onEditableCellChange(props.row, props.col.field, e.target.value)"
              />
              <span class="old-price-label" :title="`Eski: ${formatPrice(getOriginalCellValue(props.row, props.col.field))}`">
                {{ formatPrice(getOriginalCellValue(props.row, props.col.field)) }}
              </span>
            </div>
            <span v-else class="cell-text" :title="String(props.value ?? '')">{{ props.value }}</span>
          </q-td>
        </template>
      </q-table>
    </div>

    <q-banner v-if="store.error && !isGuidanceState" class="bg-red text-white q-mt-xs">
      {{ store.error }}
    </q-banner>

    <q-dialog v-model="priceHistoryDialogOpen" persistent>
      <q-card class="price-history-card">
        <q-card-section class="row items-center justify-between">
          <div>
            <div class="text-subtitle1 text-weight-bold">Urun Fiyat Karti</div>
            <div class="text-caption text-grey-7">
              {{ priceHistoryRow?.productCode || '-' }} | {{ priceHistoryRow?.marka || '-' }}
            </div>
          </div>
          <q-btn flat round icon="close" color="grey-8" @click="priceHistoryDialogOpen = false" />
        </q-card-section>

        <q-separator />

        <q-card-section class="q-pt-sm q-pb-none">
          <div class="row items-center q-gutter-sm">
            <q-btn
              color="negative"
              icon="delete"
              label="Secilenleri Sil"
              :disable="selectedHistoryCount === 0 || priceHistoryLoading"
              @click="confirmDeleteSelectedHistory"
            />
            <q-space />
            <q-btn
              outline
              color="primary"
              icon="refresh"
              label="Yenile"
              :loading="priceHistoryLoading"
              :disable="!priceHistoryRow?.productCode"
              @click="reloadPriceHistory()"
            />
          </div>
        </q-card-section>

        <q-card-section class="q-pt-sm">
          <q-inner-loading :showing="priceHistoryLoading">
            <q-spinner-gears size="46px" color="primary" />
          </q-inner-loading>

          <q-tabs v-model="priceHistoryTab" dense inline-label class="text-grey-8" active-color="primary">
            <q-tab name="pg" icon="storefront" label="B2B/B2C" />
            <q-tab name="mssql" icon="dns" label="NEBIM_V3" />
          </q-tabs>
          <q-separator class="q-mt-xs q-mb-sm" />

          <q-tab-panels v-model="priceHistoryTab" animated>
            <q-tab-panel name="pg" class="q-pa-none">
              <div v-if="pgHistoryGroups.length === 0" class="text-caption text-grey-7 q-pa-sm">
                Kayit bulunamadi.
              </div>
              <q-list v-else dense bordered separator>
                <q-expansion-item
                  v-for="g in pgHistoryGroups"
                  :key="g.key"
                  expand-separator
                  :label="`${g.currency} ${(g.levelNo <= 5) ? 'B2B' : 'B2C'} Level ${g.levelNo}`"
                  :caption="`${g.rows.length} kayit | Son: ${formatMoney(g.latest?.price)} @ ${g.latest?.updated_at || '-'}`"
                >
                  <q-item v-for="r in g.rows" :key="r.id">
                    <q-item-section avatar>
                      <q-checkbox
                        :model-value="selectedPgIdSet.has(r.id)"
                        dense
                        @update:model-value="(val) => toggleSelectedPgId(r.id, val)"
                        @click.stop
                      />
                    </q-item-section>
                    <q-item-section>
                      <q-item-label>{{ formatMoney(r.price) }}</q-item-label>
                      <q-item-label caption>{{ r.updated_at }}</q-item-label>
                    </q-item-section>
                    <q-item-section side>
                      <q-badge color="grey-6" outline>{{ r.currency }} {{ r.level_no }}</q-badge>
                    </q-item-section>
                  </q-item>
                </q-expansion-item>
              </q-list>
            </q-tab-panel>

            <q-tab-panel name="mssql" class="q-pa-none">
              <div v-if="mssqlHistoryGroups.length === 0" class="text-caption text-grey-7 q-pa-sm">
                Kayit bulunamadi.
              </div>
              <q-list v-else dense bordered separator>
                <q-expansion-item
                  v-for="g in mssqlHistoryGroups"
                  :key="g.key"
                  expand-separator
                  :label="`${g.currency} ${g.price_group_code}`"
                  :caption="`${g.rows.length} kayit | Son: ${formatMoney(g.latest?.price)} @ ${formatMssqlStamp(g.latest)}`"
                >
                  <q-item v-for="r in g.rows" :key="r.price_list_line_id">
                    <q-item-section avatar>
                      <q-checkbox
                        :model-value="selectedMssqlIdSet.has(r.price_list_line_id)"
                        dense
                        @update:model-value="(val) => toggleSelectedMssqlId(r.price_list_line_id, val)"
                        @click.stop
                      />
                    </q-item-section>
                    <q-item-section>
                      <q-item-label>{{ formatMoney(r.price) }}</q-item-label>
                      <q-item-label caption>{{ formatMssqlStamp(r) }}</q-item-label>
                    </q-item-section>
                    <q-item-section side>
                      <q-badge color="grey-6" outline>{{ r.currency }} {{ r.price_group_code }}</q-badge>
                    </q-item-section>
                  </q-item>
                </q-expansion-item>
              </q-list>
            </q-tab-panel>
          </q-tab-panels>
        </q-card-section>
      </q-card>
    </q-dialog>

    <q-dialog v-model="priceListExportDialogOpen" persistent>
      <q-card style="min-width: 740px; max-width: 95vw;">
        <q-card-section class="row items-center justify-between">
          <div class="text-subtitle1 text-weight-bold">
            Fiyat Listesi Ciktisi
          </div>
          <q-btn flat round icon="close" color="grey-8" @click="priceListExportDialogOpen = false" />
        </q-card-section>
        <q-separator />

        <q-card-section class="q-gutter-sm">
          <div class="row items-center q-gutter-sm">
            <q-btn-toggle
              v-model="priceListExportFormat"
              dense
              unelevated
              toggle-color="primary"
              color="grey-3"
              text-color="grey-9"
              :options="[
                { label: 'PDF', value: 'pdf', icon: 'picture_as_pdf' },
                { label: 'Excel', value: 'excel', icon: 'grid_on' }
              ]"
            />
            <q-toggle v-model="priceListInStockOnly" label="Sadece stogu olan urunler" />
            <q-space />
            <q-btn
              color="primary"
              icon="download"
              :label="priceListExportFormat === 'pdf' ? 'PDF Olustur' : 'Excel Olustur'"
              :loading="priceListExportLoading"
              @click="runPriceListExport"
            />
          </div>

          <div class="row q-col-gutter-sm">
            <div class="col-12 col-md-4">
              <q-select
                v-model="priceListUrunIlkGrubu"
                dense
                outlined
                clearable
                emit-value
                map-options
                :options="topUrunIlkGrubuOptions"
                :loading="Boolean(serverFilterLoading.urunIlkGrubu)"
                label="Urun Ilk Grubu"
                @filter="onTopFilterSearchUrunIlkGrubu"
                @update:model-value="onPriceListUrunIlkGrubuChange"
              />
            </div>
            <div class="col-12 col-md-4">
              <q-select
                v-model="priceListUrunAnaGrubu"
                dense
                outlined
                clearable
                multiple
                use-chips
                emit-value
                map-options
                :options="topUrunAnaGrubuOptions"
                :loading="Boolean(serverFilterLoading.urunAnaGrubu)"
                label="Urun Ana Grubu (max 3)"
                @filter="onTopFilterSearchUrunAnaGrubu"
                @update:model-value="onPriceListUrunAnaGrubuChange"
              />
            </div>
            <div class="col-12 col-md-4">
              <q-select
                v-model="priceListUrunAltGrubu"
                dense
                outlined
                clearable
                multiple
                use-chips
                emit-value
                map-options
                :options="priceListUrunAltGrubuOptions"
                :loading="Boolean(serverFilterLoading.urunAltGrubu)"
                label="Urun Alt Grubu"
                @filter="onPriceListFilterSearchUrunAltGrubu"
              />
            </div>
          </div>

          <div class="row items-center q-gutter-sm">
            <q-toggle v-model="priceListIncludeCost" label="Maliyet fiyati" />
            <q-toggle v-model="priceListIncludeBase" label="Taban fiyatlar (USD/TRY)" />
          </div>

          <div class="row q-col-gutter-sm">
            <div class="col-12 col-md-4">
              <div class="text-caption text-grey-8 q-mb-xs">USD seviyeleri</div>
              <q-option-group
                v-model="priceListUSDLevels"
                type="checkbox"
                dense
                :options="priceLevelOptionsUSD"
              />
            </div>
            <div class="col-12 col-md-4">
              <div class="text-caption text-grey-8 q-mb-xs">EUR seviyeleri</div>
              <q-option-group
                v-model="priceListEURLevels"
                type="checkbox"
                dense
                :options="priceLevelOptionsEUR"
              />
            </div>
            <div class="col-12 col-md-4">
              <div class="text-caption text-grey-8 q-mb-xs">TRY seviyeleri</div>
              <q-option-group
                v-model="priceListTRYLevels"
                type="checkbox"
                dense
                :options="priceLevelOptionsTRY"
              />
            </div>
          </div>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { Notify, useQuasar } from 'quasar'
import { useProductPricingStore } from 'src/stores/ProductPricingStore'
import api, { download } from 'src/services/api'

const $q = useQuasar()
const store = useProductPricingStore()

const isReloading = ref(false)
const reloadScheduled = ref(false)
const pageBusy = computed(() => Boolean(reloadScheduled.value || isReloading.value || store.loading || saving.value || bulkCalcLoading.value || exportAllLoading.value))
const PAGE_LIMIT = 250
const currentPage = ref(1)
let reloadTimer = null

const GUIDANCE_MSG = "Calismak icin once Urun Ilk Grubu veya Urun Ana Grubu Secin ve GRUPLARI GETIR'e Basin."

const usdToTry = 38.25
const eurToTry = 41.6
const multipliers = [1, 1.03, 1.06, 1.09, 1.12, 1.15]
const rowHeight = 31
const headerHeight = 72

// Marka grubu artik Marka Siniflandirma modulunden (mk_brandgrp) gelir ve listede sadece goruntulenir.

const currencyOptions = [
  { label: 'USD', value: 'USD' },
  { label: 'EUR', value: 'EUR' },
  { label: 'TRY', value: 'TRY' }
]

const multiFilterColumns = [
  { field: 'productCode', label: 'Urun Kodu' },
  { field: 'brandGroupSelection', label: 'Marka Grubu' },
  { field: 'marka', label: 'Marka' },
  { field: 'askiliYan', label: 'Askili Yan' },
  { field: 'kategori', label: 'Kategori' },
  { field: 'urunIlkGrubu', label: 'Urun Ilk Grubu' },
  { field: 'urunAnaGrubu', label: 'Urun Ana Grubu' },
  { field: 'urunAltGrubu', label: 'Urun Alt Grubu' },
  { field: 'icerik', label: 'Icerik' },
  { field: 'karisim', label: 'Karisim' }
]
const serverBackedMultiFilterFields = new Set([
  'productCode',
  'marka',
  'askiliYan',
  'kategori',
  'urunIlkGrubu',
  'urunAnaGrubu',
  'urunAltGrubu',
  'icerik',
  'karisim'
])
const numberRangeFilterFields = ['stockQty']
const dateRangeFilterFields = ['stockEntryDate', 'lastPricingDate']
const valueFilterFields = [
  'costPrice',
  'basePriceUsd',
  'basePriceTry',
  'usd1',
  'usd2',
  'usd3',
  'usd4',
  'usd5',
  'usd6',
  'eur1',
  'eur2',
  'eur3',
  'eur4',
  'eur5',
  'eur6',
  'try1',
  'try2',
  'try3',
  'try4',
  'try5',
  'try6'
]
const columnFilters = ref({
  productCode: [],
  brandGroupSelection: [],
  marka: [],
  askiliYan: [],
  kategori: [],
  urunIlkGrubu: [],
  urunAnaGrubu: [],
  urunAltGrubu: [],
  icerik: [],
  karisim: []
})
const columnFilterSearch = ref({
  productCode: '',
  brandGroupSelection: '',
  marka: '',
  askiliYan: '',
  kategori: '',
  urunIlkGrubu: '',
  urunAnaGrubu: '',
  urunAltGrubu: '',
  icerik: '',
  karisim: ''
})

const serverFilterOptionMap = ref({})
const serverFilterLoading = ref({})
const serverFilterLastQuery = ref({})
const serverFilterTimers = {}

const topUrunIlkGrubu = ref(null)
const topUrunAnaGrubu = ref([])

const topUrunIlkGrubuOptions = computed(() => serverFilterOptionMap.value.urunIlkGrubu || [])
const topUrunAnaGrubuOptions = computed(() => serverFilterOptionMap.value.urunAnaGrubu || [])
const priceListUrunAltGrubuOptions = computed(() => serverFilterOptionMap.value.urunAltGrubu || [])

const priceLevelOptionsUSD = [
  { label: 'USD 1', value: 1 },
  { label: 'USD 2', value: 2 },
  { label: 'USD 3', value: 3 },
  { label: 'USD 4', value: 4 },
  { label: 'USD 5', value: 5 },
  { label: 'USD 6', value: 6 }
]
const priceLevelOptionsEUR = [
  { label: 'EUR 1', value: 1 },
  { label: 'EUR 2', value: 2 },
  { label: 'EUR 3', value: 3 },
  { label: 'EUR 4', value: 4 },
  { label: 'EUR 5', value: 5 },
  { label: 'EUR 6', value: 6 }
]
const priceLevelOptionsTRY = [
  { label: 'TRY 1', value: 1 },
  { label: 'TRY 2', value: 2 },
  { label: 'TRY 3', value: 3 },
  { label: 'TRY 4', value: 4 },
  { label: 'TRY 5', value: 5 },
  { label: 'TRY 6', value: 6 }
]
const canFetchByGroup = computed(() => {
  return Boolean(String(topUrunIlkGrubu.value || '').trim()) || (topUrunAnaGrubu.value?.length || 0) > 0
})

async function fetchServerFilterOptions (field, { force = false } = {}) {
  if (!serverBackedMultiFilterFields.has(field)) return
  const q = String(columnFilterSearch.value[field] || '').trim()
  const lastQ = String(serverFilterLastQuery.value[field] || '')
  const hasCached = Array.isArray(serverFilterOptionMap.value[field]) && serverFilterOptionMap.value[field].length > 0
  if (!force && hasCached && q === lastQ) return
  if (serverFilterLoading.value[field]) return

  serverFilterLoading.value = { ...serverFilterLoading.value, [field]: true }
  serverFilterLastQuery.value = { ...serverFilterLastQuery.value, [field]: q }
  try {
    const params = { field, q, limit: 160 }
    // Cascade scope for Urun Ana Grubu options.
    if (field === 'urunAnaGrubu') {
      const ilk = String(topUrunIlkGrubu.value || '').trim()
      if (ilk) params.urun_ilk_grubu = [ilk]
    }
    const res = await api.get('/pricing/products/options', { params })
    const items = Array.isArray(res?.data?.items) ? res.data.items : []
    serverFilterOptionMap.value = {
      ...serverFilterOptionMap.value,
      [field]: items.map((x) => ({
        label: String(x?.label ?? x?.value ?? '').trim(),
        value: String(x?.value ?? x?.label ?? '').trim()
      })).filter((x) => x.value)
    }
  } catch (err) {
    console.warn('[product-pricing][ui] filter options error', {
      field,
      q,
      message: String(err?.message || err || 'options failed')
    })
    serverFilterOptionMap.value = { ...serverFilterOptionMap.value, [field]: [] }
  } finally {
    serverFilterLoading.value = { ...serverFilterLoading.value, [field]: false }
  }
}

function scheduleServerFilterOptionsFetch (field) {
  if (!serverBackedMultiFilterFields.has(field)) return
  if (serverFilterTimers[field]) clearTimeout(serverFilterTimers[field])
  serverFilterTimers[field] = setTimeout(() => {
    serverFilterTimers[field] = null
    void fetchServerFilterOptions(field)
  }, 220)
}

function onFilterMenuBeforeShow (field) {
  if (!serverBackedMultiFilterFields.has(field)) return
  void fetchServerFilterOptions(field)
}

function onTopFilterSearchUrunIlkGrubu (val, update) {
  update(() => {
    columnFilterSearch.value = { ...columnFilterSearch.value, urunIlkGrubu: String(val || '') }
    scheduleServerFilterOptionsFetch('urunIlkGrubu')
  })
}

function onTopFilterSearchUrunAnaGrubu (val, update) {
  update(() => {
    columnFilterSearch.value = { ...columnFilterSearch.value, urunAnaGrubu: String(val || '') }
    scheduleServerFilterOptionsFetch('urunAnaGrubu')
  })
}

function applyTopGroupFiltersToColumnFilters () {
  // Enforce max 3 selection for Urun Ana Grubu.
  const nextAna = Array.isArray(topUrunAnaGrubu.value) ? topUrunAnaGrubu.value.slice(0, 3) : []
  if (nextAna.length !== (topUrunAnaGrubu.value || []).length) topUrunAnaGrubu.value = nextAna
  const ilk = String(topUrunIlkGrubu.value || '').trim()

  columnFilters.value = {
    ...columnFilters.value,
    urunIlkGrubu: ilk ? [ilk] : [],
    urunAnaGrubu: nextAna
  }
}

function onTopUrunIlkGrubuChange () {
  // Cascade: when Ilk Grubu changes, clear Ana Grubu selection and refetch options scoped by Ilk Grubu.
  topUrunAnaGrubu.value = []
  applyTopGroupFiltersToColumnFilters()
  void fetchServerFilterOptions('urunAnaGrubu', { force: true })
}

function onTopUrunAnaGrubuChange () {
  applyTopGroupFiltersToColumnFilters()
}

function onPriceListUrunIlkGrubuChange () {
  // cascade for export dialog
  priceListUrunAnaGrubu.value = []
  const ilk = String(priceListUrunIlkGrubu.value || '').trim()
  if (ilk) {
    // scope ana grubu options
    topUrunIlkGrubu.value = ilk
    void fetchServerFilterOptions('urunAnaGrubu', { force: true })
  }
}

function onPriceListUrunAnaGrubuChange () {
  // enforce max 3
  const nextAna = Array.isArray(priceListUrunAnaGrubu.value) ? priceListUrunAnaGrubu.value.slice(0, 3) : []
  if (nextAna.length !== (priceListUrunAnaGrubu.value || []).length) priceListUrunAnaGrubu.value = nextAna
}

function onPriceListFilterSearchUrunAltGrubu (val, update) {
  update(() => {
    columnFilterSearch.value = { ...columnFilterSearch.value, urunAltGrubu: String(val || '') }
    scheduleServerFilterOptionsFetch('urunAltGrubu')
  })
}

function openPriceListExportDialog (format) {
  // format optional (default: pdf); dialog includes its own format selector.
  if (format === 'excel' || format === 'pdf') {
    priceListExportFormat.value = format
  } else {
    priceListExportFormat.value = 'pdf'
  }
  priceListExportLoading.value = false
  // default selections: mirror top group selections if present
  priceListUrunIlkGrubu.value = topUrunIlkGrubu.value
  priceListUrunAnaGrubu.value = Array.isArray(topUrunAnaGrubu.value) ? topUrunAnaGrubu.value.slice(0, 3) : []
  priceListUrunAltGrubu.value = []
  // preload alt group options
  void fetchServerFilterOptions('urunAltGrubu', { force: true })
  priceListExportDialogOpen.value = true
}

async function runPriceListExport () {
  priceListExportLoading.value = true
  try {
    const payload = {
      in_stock_only: !!priceListInStockOnly.value,
      include_meta: true,
      include_cost: !!priceListIncludeCost.value,
      include_base: !!priceListIncludeBase.value,
      usd_levels: Array.isArray(priceListUSDLevels.value) ? priceListUSDLevels.value : [],
      eur_levels: Array.isArray(priceListEURLevels.value) ? priceListEURLevels.value : [],
      try_levels: Array.isArray(priceListTRYLevels.value) ? priceListTRYLevels.value : [],
      urun_ilk_grubu: String(priceListUrunIlkGrubu.value || '').trim() ? [String(priceListUrunIlkGrubu.value || '').trim()] : [],
      urun_ana_grubu: Array.isArray(priceListUrunAnaGrubu.value) ? priceListUrunAnaGrubu.value : [],
      urun_alt_grubu: Array.isArray(priceListUrunAltGrubu.value) ? priceListUrunAltGrubu.value : []
    }

    const traceId = `ui-${Date.now()}-${Math.random().toString(16).slice(2)}`
    const url = priceListExportFormat.value === 'excel'
      ? '/pricing/products/price-list/export-excel'
      : '/pricing/products/price-list/export-pdf'

    const res = await api.request({
      method: 'POST',
      url,
      data: payload,
      responseType: 'blob',
      timeout: 0,
      headers: { 'X-Trace-ID': traceId }
    })
    const blob = res?.data instanceof Blob ? res.data : new Blob([res?.data || ''])
    const objUrl = URL.createObjectURL(blob)

    if (priceListExportFormat.value === 'pdf') {
      window.open(objUrl, '_blank')
      setTimeout(() => URL.revokeObjectURL(objUrl), 120000)
    } else {
      const a = document.createElement('a')
      a.href = objUrl
      a.download = `baggi_guncel_fiyat_listesi_${new Date().toISOString().slice(0, 10)}.xlsx`
      document.body.appendChild(a)
      a.click()
      a.remove()
      URL.revokeObjectURL(objUrl)
    }

    priceListExportDialogOpen.value = false
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.parsedMessage || err?.message || 'Fiyat listesi olusturulamadi' })
  } finally {
    priceListExportLoading.value = false
  }
}

function resetGroupSelections () {
  topUrunIlkGrubu.value = null
  topUrunAnaGrubu.value = []
  applyTopGroupFiltersToColumnFilters()
  // Keep other local filters cleared too, so page is "clean render".
  store.rows = []
  store.error = GUIDANCE_MSG
  store.totalCount = 0
  store.totalPages = 1
  store.page = 1
  store.hasMore = false
}

for (const field of Array.from(serverBackedMultiFilterFields)) {
  watch(
    () => columnFilterSearch.value[field],
    () => { scheduleServerFilterOptionsFetch(field) }
  )
}
const numberRangeFilters = ref({
  stockQty: { min: '', max: '' }
})
const dateRangeFilters = ref({
  stockEntryDate: { from: '', to: '' },
  lastPricingDate: { from: '', to: '' }
})
const valueFilters = ref(Object.fromEntries(valueFilterFields.map((field) => [field, []])))
const valueFilterSearch = ref(Object.fromEntries(valueFilterFields.map((field) => [field, ''])))
const multiSelectFilterFieldSet = new Set(multiFilterColumns.map((x) => x.field))
const numberRangeFilterFieldSet = new Set(numberRangeFilterFields)
const dateRangeFilterFieldSet = new Set(dateRangeFilterFields)
const valueSelectFilterFieldSet = new Set(valueFilterFields)
const headerFilterFieldSet = new Set([
  ...multiFilterColumns.map((x) => x.field),
  ...numberRangeFilterFields,
  ...dateRangeFilterFields,
  ...valueFilterFields
])

const mainTableRef = ref(null)
const topScrollRef = ref(null)
const topScrollInnerRef = ref(null)
const tablePagination = ref({
  page: 1, // server-side paging var; q-table local paging kapali
  rowsPerPage: 0,
  sortBy: 'stockQty',
  descending: true
})
const selectedMap = ref({})
const selectedCurrencies = ref(['USD', 'EUR', 'TRY'])
const exportAllLoading = ref(false)
const showSelectedOnly = ref(false)
const leftDetailsExpanded = ref(true)
const showInStockOnly = ref(false)
const calcLoadingMap = ref({})
const bulkCalcLoading = ref(false)
const saving = ref(false)

const priceHistoryDialogOpen = ref(false)
const priceHistoryRow = ref(null)
const priceHistoryLoading = ref(false)
const priceHistoryTab = ref('pg')
const priceHistory = ref({ postgres: [], mssql: [] })
const selectedPgIds = ref([])
const selectedMssqlIds = ref([])

const priceListExportDialogOpen = ref(false)
const priceListExportFormat = ref('pdf') // 'pdf' | 'excel'
const priceListExportLoading = ref(false)
const priceListInStockOnly = ref(true)
const priceListUrunIlkGrubu = ref(null)
const priceListUrunAnaGrubu = ref([])
const priceListUrunAltGrubu = ref([])
const priceListIncludeCost = ref(true)
const priceListIncludeBase = ref(true)
const priceListUSDLevels = ref([1, 2, 3, 4, 5, 6])
const priceListEURLevels = ref([1, 2, 3, 4, 5, 6])
const priceListTRYLevels = ref([1, 2, 3, 4, 5, 6])

const editableColumns = [
  'costPrice',
  'basePriceUsd',
  'basePriceTry',
  'usd1',
  'usd2',
  'usd3',
  'usd4',
  'usd5',
  'usd6',
  'eur1',
  'eur2',
  'eur3',
  'eur4',
  'eur5',
  'eur6',
  'try1',
  'try2',
  'try3',
  'try4',
  'try5',
  'try6'
]
const editableColumnSet = new Set(editableColumns)

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('select', '', 'select', 40, { align: 'center', classes: 'text-center selection-col' }),
  col('brandGroupSelection', 'MARKA GRUBU SECIMI', 'brandGroupSelection', 76),
  col('marka', 'MARKA', 'marka', 54, { sortable: true, classes: 'ps-col' }),
  col('productCode', 'URUN KODU', 'productCode', 108, { sortable: true, classes: 'ps-col product-code-col' }),
  col('calcAction', 'HESAPLA', 'calcAction', 72, { align: 'center', classes: 'ps-col' }),
  col('historyAction', '', 'historyAction', 40, { align: 'center', classes: 'ps-col text-center' }),
  col('stockQty', 'STOK ADET', 'stockQty', 72, { align: 'right', sortable: true, classes: 'ps-col stock-col' }),
  col('stockEntryDate', 'STOK GIRIS TARIHI', 'stockEntryDate', 92, { align: 'center', sortable: true, classes: 'ps-col date-col' }),
  col('lastCostingDate', 'SON MALIYETLENDIRME', 'lastCostingDate', 110, { align: 'center', sortable: true, classes: 'ps-col date-col' }),
  col('lastPricingDate', 'SON FIYATLANDIRMA TARIHI', 'lastPricingDate', 108, { align: 'center', sortable: true, classes: 'ps-col date-col' }),
  col('askiliYan', 'ASKILI YAN', 'askiliYan', 54, { sortable: true, classes: 'ps-col' }),
  col('kategori', 'KATEGORI', 'kategori', 54, { sortable: true, classes: 'ps-col' }),
  col('urunIlkGrubu', 'URUN ILK GRUBU', 'urunIlkGrubu', 66, { sortable: true, classes: 'ps-col' }),
  col('urunAnaGrubu', 'URUN ANA GRUBU', 'urunAnaGrubu', 66, { sortable: true, classes: 'ps-col' }),
  col('urunAltGrubu', 'URUN ALT GRUBU', 'urunAltGrubu', 66, { sortable: true, classes: 'ps-col' }),
  col('icerik', 'ICERIK', 'icerik', 62, { sortable: true, classes: 'ps-col' }),
  col('karisim', 'KARISIM', 'karisim', 62, { sortable: true, classes: 'ps-col' }),
  col('costPrice', 'MALIYET FIYATI', 'costPrice', 88, { align: 'right', sortable: true, classes: 'usd-col' }),
  col('basePriceUsd', 'TABAN USD', 'basePriceUsd', 88, { align: 'right', classes: 'usd-col' }),
  col('basePriceTry', 'TABAN TRY', 'basePriceTry', 96, { align: 'right', classes: 'try-col' }),
  col('usd1', 'USD 1', 'usd1', 84, { align: 'right', classes: 'usd-col' }),
  col('usd2', 'USD 2', 'usd2', 84, { align: 'right', classes: 'usd-col' }),
  col('usd3', 'USD 3', 'usd3', 84, { align: 'right', classes: 'usd-col' }),
  col('usd4', 'USD 4', 'usd4', 84, { align: 'right', classes: 'usd-col' }),
  col('usd5', 'USD 5', 'usd5', 84, { align: 'right', classes: 'usd-col' }),
  col('usd6', 'USD 6', 'usd6', 84, { align: 'right', classes: 'usd-col' }),
  col('eur1', 'EUR 1', 'eur1', 84, { align: 'right', classes: 'eur-col' }),
  col('eur2', 'EUR 2', 'eur2', 84, { align: 'right', classes: 'eur-col' }),
  col('eur3', 'EUR 3', 'eur3', 84, { align: 'right', classes: 'eur-col' }),
  col('eur4', 'EUR 4', 'eur4', 84, { align: 'right', classes: 'eur-col' }),
  col('eur5', 'EUR 5', 'eur5', 84, { align: 'right', classes: 'eur-col' }),
  col('eur6', 'EUR 6', 'eur6', 84, { align: 'right', classes: 'eur-col' }),
  col('try1', 'TRY 1', 'try1', 96, { align: 'right', classes: 'try-col' }),
  col('try2', 'TRY 2', 'try2', 96, { align: 'right', classes: 'try-col' }),
  col('try3', 'TRY 3', 'try3', 96, { align: 'right', classes: 'try-col' }),
  col('try4', 'TRY 4', 'try4', 96, { align: 'right', classes: 'try-col' }),
  col('try5', 'TRY 5', 'try5', 96, { align: 'right', classes: 'try-col' }),
  col('try6', 'TRY 6', 'try6', 96, { align: 'right', classes: 'try-col' })
]

const hideableLeftDetailColumnNames = new Set([
  'stockEntryDate',
  'lastCostingDate',
  'lastPricingDate',
  'askiliYan',
  'kategori',
  'urunIlkGrubu',
  'urunAnaGrubu',
  'urunAltGrubu',
  'icerik',
  'karisim'
])
const stickyColumnNamesBase = [
  'select',
  'brandGroupSelection',
  'marka',
  'productCode',
  'calcAction',
  'historyAction',
  'stockQty',
  'stockEntryDate',
  'lastPricingDate',
  'askiliYan',
  'kategori',
  'urunIlkGrubu',
  'urunAnaGrubu',
  'urunAltGrubu',
  'icerik',
  'karisim',
  'costPrice',
  'basePriceUsd',
  'basePriceTry'
]
const stickyBoundaryColumnName = 'basePriceTry'

const visibleColumns = computed(() => {
  const selected = new Set(selectedCurrencies.value)
  return allColumns.filter((c) => {
    if (c.name.startsWith('usd')) return selected.has('USD')
    if (c.name.startsWith('eur')) return selected.has('EUR')
    if (c.name.startsWith('try')) return selected.has('TRY')
    if (!leftDetailsExpanded.value && hideableLeftDetailColumnNames.has(c.name)) return false
    return true
  })
})

const stickyColumnNames = computed(() => {
  const visibleNameSet = new Set(visibleColumns.value.map((col) => col.name))
  return stickyColumnNamesBase.filter((name) => visibleNameSet.has(name))
})
const stickyColumnNameSet = computed(() => new Set(stickyColumnNames.value))

const exportableColumns = computed(() => visibleColumns.value.filter((col) => col.name !== 'select' && col.name !== 'calcAction' && col.name !== 'historyAction'))

const pgHistoryGroups = computed(() => {
  const list = Array.isArray(priceHistory.value?.postgres) ? priceHistory.value.postgres : []
  const map = new Map()
  for (const r of list) {
    const currency = String(r?.currency || '').toUpperCase().trim()
    const levelNo = Number(r?.level_no || 0)
    if (!currency || !(levelNo >= 1 && levelNo <= 6)) continue
    const key = `${currency}|${levelNo}`
    if (!map.has(key)) map.set(key, { key, currency, levelNo, rows: [] })
    map.get(key).rows.push(r)
  }
  const out = Array.from(map.values())
  for (const g of out) g.latest = g.rows?.[0] || null
  out.sort((a, b) => (a.currency + a.levelNo).localeCompare(b.currency + b.levelNo))
  return out
})

const mssqlHistoryGroups = computed(() => {
  const list = Array.isArray(priceHistory.value?.mssql) ? priceHistory.value.mssql : []
  const map = new Map()
  for (const r of list) {
    const currency = String(r?.currency || '').toUpperCase().trim()
    const pgc = String(r?.price_group_code || '').trim()
    if (!currency || !pgc) continue
    const key = `${currency}|${pgc}`
    if (!map.has(key)) map.set(key, { key, currency, price_group_code: pgc, rows: [] })
    map.get(key).rows.push(r)
  }
  const out = Array.from(map.values())
  for (const g of out) g.latest = g.rows?.[0] || null
  out.sort((a, b) => (a.currency + a.price_group_code).localeCompare(b.currency + b.price_group_code))
  return out
})

const selectedPgIdSet = computed(() => new Set(selectedPgIds.value || []))
const selectedMssqlIdSet = computed(() => new Set(selectedMssqlIds.value || []))
const selectedHistoryCount = computed(() => (selectedPgIds.value?.length || 0) + (selectedMssqlIds.value?.length || 0))

const stickyLeftMap = computed(() => {
  const map = {}
  let left = 0
  for (const colName of stickyColumnNames.value) {
    const c = allColumns.find((x) => x.name === colName)
    if (!c) continue
    map[colName] = left
    left += extractWidth(c.style)
  }
  return map
})
const stickyScrollComp = computed(() => {
  const boundaryCol = allColumns.find((x) => x.name === stickyBoundaryColumnName)
  return ((stickyLeftMap.value[stickyBoundaryColumnName] || 0) + extractWidth(boundaryCol?.style)) * 1.2
})
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 rows = computed(() => store.rows || [])
const tableLoading = computed(() => Boolean(store.loading) && rows.value.length === 0)

const isGuidanceState = computed(() => String(store.error || '').trim() === GUIDANCE_MSG)
const showGuidanceOverlay = computed(() => isGuidanceState.value && !store.loading && rows.value.length === 0)
const multiFilterOptionMap = computed(() => {
  const map = {}
  multiFilterColumns.forEach(({ field }) => {
    const uniq = new Set()
    rows.value.forEach((row) => {
      const val = String(row?.[field] ?? '').trim()
      if (val) uniq.add(val)
    })
    map[field] = Array.from(uniq)
      .sort((a, b) => a.localeCompare(b, 'tr'))
      .map((v) => ({ label: v, value: v }))
  })
  return map
})
const filteredFilterOptionMap = computed(() => {
  const map = {}
  multiFilterColumns.forEach(({ field }) => {
    const search = String(columnFilterSearch.value[field] || '').trim().toLocaleLowerCase('tr')
    const options = multiFilterOptionMap.value[field] || []
    map[field] = search
      ? options.filter((option) => option.label.toLocaleLowerCase('tr').includes(search))
      : options
  })
  return map
})
const valueFilterOptionMap = computed(() => {
  const map = {}
  valueFilterFields.forEach((field) => {
    const uniq = new Set()
    rows.value.forEach((row) => {
      uniq.add(toValueFilterKey(row?.[field]))
    })
    map[field] = Array.from(uniq)
      .sort((a, b) => Number(a) - Number(b))
      .map((v) => ({ label: formatPrice(v), value: v }))
  })
  return map
})
const filteredValueFilterOptionMap = computed(() => {
  const map = {}
  valueFilterFields.forEach((field) => {
    const search = String(valueFilterSearch.value[field] || '').trim().toLocaleLowerCase('tr')
    const options = valueFilterOptionMap.value[field] || []
    map[field] = search
      ? options.filter((option) => option.label.toLocaleLowerCase('tr').includes(search))
      : options
  })
  return map
})

function rowSelectionKey (row) {
  const code = String(row?.productCode ?? '').trim()
  if (code) return code
  return String(row?.id ?? '')
}

const filteredRows = computed(() => {
  return rows.value.filter((row) => {
    if (showSelectedOnly.value && !selectedMap.value[rowSelectionKey(row)]) return false
    if (showInStockOnly.value && Number(row?.stockQty ?? 0) <= 0) return false
    for (const { field } of multiFilterColumns) {
      // Server-backed filters already reload full dataset (all pages) from backend.
      // Keep only non-server multi filters (e.g. brandGroupSelection) as local page filter.
      if (serverBackedMultiFilterFields.has(field)) continue
      const selected = columnFilters.value[field] || []
      if (selected.length <= 0) continue
      const rowVal = String(row?.[field] ?? '').trim()
      if (!selected.includes(rowVal)) return false
    }
    for (const field of valueFilterFields) {
      const selected = valueFilters.value[field] || []
      if (selected.length > 0 && !selected.includes(toValueFilterKey(row?.[field]))) return false
    }
    const stockQtyMin = parseNullableNumber(numberRangeFilters.value.stockQty?.min)
    const stockQtyMax = parseNullableNumber(numberRangeFilters.value.stockQty?.max)
    const stockQty = Number(row?.stockQty ?? 0)
    if (stockQtyMin !== null && stockQty < stockQtyMin) return false
    if (stockQtyMax !== null && stockQty > stockQtyMax) return false
    if (!matchesDateRange(String(row?.stockEntryDate || '').trim(), dateRangeFilters.value.stockEntryDate)) return false
    if (!matchesDateRange(String(row?.lastPricingDate || '').trim(), dateRangeFilters.value.lastPricingDate)) return false
    return true
  })
})

const visibleRowIds = computed(() => filteredRows.value.map((row) => rowSelectionKey(row)))
const selectedRowCount = computed(() => Object.values(selectedMap.value).filter(Boolean).length)
const selectedVisibleCount = computed(() => visibleRowIds.value.filter((id) => !!selectedMap.value[id]).length)
const allSelectedVisible = computed(() => visibleRowIds.value.length > 0 && selectedVisibleCount.value === visibleRowIds.value.length)
const someSelectedVisible = computed(() => selectedVisibleCount.value > 0)

function isHeaderFilterField (field) {
  return headerFilterFieldSet.has(field)
}

function isMultiSelectFilterField (field) {
  return multiSelectFilterFieldSet.has(field)
}

function isNumberRangeFilterField (field) {
  return numberRangeFilterFieldSet.has(field)
}

function isDateRangeFilterField (field) {
  return dateRangeFilterFieldSet.has(field)
}

function isValueSelectFilterField (field) {
  return valueSelectFilterFieldSet.has(field)
}

function hasFilter (field) {
  if (isMultiSelectFilterField(field)) return (columnFilters.value[field] || []).length > 0
  if (isValueSelectFilterField(field)) return (valueFilters.value[field] || []).length > 0
  if (isNumberRangeFilterField(field)) {
    const filter = numberRangeFilters.value[field]
    return !!String(filter?.min || '').trim() || !!String(filter?.max || '').trim()
  }
  if (isDateRangeFilterField(field)) {
    const filter = dateRangeFilters.value[field]
    return !!String(filter?.from || '').trim() || !!String(filter?.to || '').trim()
  }
  return false
}

function getFilterBadgeValue (field) {
  if (isMultiSelectFilterField(field)) return (columnFilters.value[field] || []).length
  if (isValueSelectFilterField(field)) return (valueFilters.value[field] || []).length
  if (isNumberRangeFilterField(field)) {
    const filter = numberRangeFilters.value[field]
    return [filter?.min, filter?.max].filter((x) => String(x || '').trim()).length
  }
  if (isDateRangeFilterField(field)) {
    const filter = dateRangeFilters.value[field]
    return [filter?.from, filter?.to].filter((x) => String(x || '').trim()).length
  }
  return 0
}

function clearColumnFilter (field) {
  if (isMultiSelectFilterField(field)) {
    columnFilters.value = {
      ...columnFilters.value,
      [field]: []
    }
    return
  }
  if (isValueSelectFilterField(field)) {
    valueFilters.value = {
      ...valueFilters.value,
      [field]: []
    }
  }
}

function clearRangeFilter (field) {
  if (isNumberRangeFilterField(field)) {
    numberRangeFilters.value = {
      ...numberRangeFilters.value,
      [field]: { min: '', max: '' }
    }
    return
  }
  if (isDateRangeFilterField(field)) {
    dateRangeFilters.value = {
      ...dateRangeFilters.value,
      [field]: { from: '', to: '' }
    }
  }
}

function getFilterOptionsForField (field) {
  if (isValueSelectFilterField(field)) return filteredValueFilterOptionMap.value[field] || []
  if (serverBackedMultiFilterFields.has(field)) {
    return serverFilterOptionMap.value[field] || []
  }
  return filteredFilterOptionMap.value[field] || []
}

function isColumnFilterValueSelected (field, value) {
  if (isValueSelectFilterField(field)) return (valueFilters.value[field] || []).includes(value)
  return (columnFilters.value[field] || []).includes(value)
}

function toggleColumnFilterValue (field, value) {
  const target = isValueSelectFilterField(field) ? valueFilters.value : columnFilters.value
  const current = new Set(target[field] || [])
  if (current.has(value)) current.delete(value)
  else current.add(value)
  if (isValueSelectFilterField(field)) {
    valueFilters.value = {
      ...valueFilters.value,
      [field]: Array.from(current)
    }
    return
  }
  if (field === 'productCode') {
    columnFilters.value = {
      ...columnFilters.value,
      [field]: current.has(value) ? [value] : []
    }
    return
  }
  columnFilters.value = {
    ...columnFilters.value,
    [field]: Array.from(current)
  }
}

function selectAllColumnFilterOptions (field) {
  const options = getFilterOptionsForField(field)
  if (isValueSelectFilterField(field)) {
    valueFilters.value = {
      ...valueFilters.value,
      [field]: options.map((option) => option.value)
    }
    return
  }
  if (field === 'productCode') {
    columnFilters.value = {
      ...columnFilters.value,
      [field]: options.length > 0 ? [options[0].value] : []
    }
    return
  }
  columnFilters.value = {
    ...columnFilters.value,
    [field]: options.map((option) => option.value)
  }
}

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

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

function isStickyBoundary (colName) {
  return colName === stickyBoundaryColumnName
}

function getHeaderCellStyle (col) {
  if (!isStickyCol(col.name)) return undefined
  return { left: `${stickyLeftMap.value[col.name] || 0}px`, zIndex: 22 }
}

function getBodyCellStyle (col) {
  if (!isStickyCol(col.name)) return undefined
  return { left: `${stickyLeftMap.value[col.name] || 0}px`, zIndex: 12 }
}

function round2 (value) {
  return Number(Number(value || 0).toFixed(2))
}

function toValueFilterKey (value) {
  return round2(parseNumber(value)).toFixed(2)
}

function parseNumber (val) {
  if (typeof val === 'number') return Number.isFinite(val) ? val : 0
  const text = String(val ?? '').trim().replace(/\s/g, '')
  if (!text) return 0

  const lastComma = text.lastIndexOf(',')
  const lastDot = text.lastIndexOf('.')
  let normalized = text

  if (lastComma >= 0 && lastDot >= 0) {
    if (lastComma > lastDot) {
      normalized = text.replace(/\./g, '').replace(',', '.')
    } else {
      normalized = text.replace(/,/g, '')
    }
  } else if (lastComma >= 0) {
    normalized = text.replace(/\./g, '').replace(',', '.')
  } else {
    normalized = text.replace(/,/g, '')
  }

  const n = Number(normalized)
  return Number.isFinite(n) ? n : 0
}

function parseNullableNumber (val) {
  const text = String(val ?? '').trim()
  if (!text) return null
  const n = parseNumber(text)
  return Number.isFinite(n) ? n : null
}

function matchesDateRange (value, filter) {
  const from = String(filter?.from || '').trim()
  const to = String(filter?.to || '').trim()
  if (!from && !to) return true
  if (!value) return false
  if (from && value < from) return false
  if (to && value > to) return false
  return true
}

function formatPrice (val) {
  const n = parseNumber(val)
  return n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}

function formatStock (val) {
  const n = Number(val || 0)
  if (!Number.isFinite(n)) return '0'
  const hasFraction = Math.abs(n % 1) > 0.0001
  return n.toLocaleString('tr-TR', {
    minimumFractionDigits: hasFraction ? 2 : 0,
    maximumFractionDigits: hasFraction ? 2 : 0
  })
}

function formatDateDisplay (val) {
  const text = String(val || '').trim()
  if (!text) return '-'
  const [year, month, day] = text.split('-')
  if (!year || !month || !day) return text
  return `${day}.${month}.${year}`
}

function formatMoney (v) {
  const n = Number(v ?? 0)
  if (!Number.isFinite(n)) return '-'
  return n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 6 })
}

function formatMssqlStamp (row) {
  if (!row) return '-'
  const vd = String(row?.valid_date || '').trim()
  const vt = String(row?.valid_time || '').trim()
  const lud = String(row?.last_updated_date || '').trim()
  const parts = []
  if (vd) parts.push(vd)
  if (vt) parts.push(vt)
  if (lud) parts.push(`upd:${lud}`)
  return parts.length ? parts.join(' ') : '-'
}

function getOriginalCellValue (row, field) {
  return row?.[`__orig_${field}`] ?? row?.[field] ?? 0
}

function exportDecimalValue (value) {
  const n = parseNumber(value)
  if (!Number.isFinite(n)) return ''
  return n.toFixed(2).replace('.', ',')
}

function isExportDecimalField (field) {
  return editableColumnSet.has(field) || field === 'costPrice' || field === 'basePriceUsd' || field === 'basePriceTry'
}

function exportCellValue (row, field) {
  if (field === 'stockQty') return formatStock(row?.[field])
  if (field === 'stockEntryDate' || field === 'lastCostingDate' || field === 'lastPricingDate') return formatDateDisplay(row?.[field])
  if (isExportDecimalField(field)) return exportDecimalValue(row?.[field])
  return String(row?.[field] ?? '').trim()
}

function csvSafe (value) {
  let text = String(value ?? '').replaceAll('\r', ' ').replaceAll('\n', ' ').trim()
  if (text.includes(';') || text.includes('"')) {
    text = `"${text.replaceAll('"', '""')}"`
  }
  return text
}

function exportCurrentView () {
  const cols = exportableColumns.value
  const list = filteredRows.value
  if (cols.length === 0 || list.length === 0) return

  const lines = [cols.map((col) => csvSafe(col.label)).join(';')]
  for (const row of list) {
    lines.push(cols.map((col) => csvSafe(exportCellValue(row, col.field))).join(';'))
  }

  const bom = '\uFEFF'
  const blob = new Blob([bom + lines.join('\n')], { type: 'text/csv;charset=utf-8;' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `product_pricing_${new Date().toISOString().slice(0, 10)}.csv`
  document.body.appendChild(a)
  a.click()
  a.remove()
  URL.revokeObjectURL(url)
}

async function exportAllFiltered () {
  exportAllLoading.value = true
  try {
    const filters = buildServerFilters()
    const params = {
      product_code: (filters.product_code || []).join(','),
      brand_group_selection: (filters.brand_group_selection || []).join(','),
      marka: (filters.marka || []).join(','),
      askili_yan: (filters.askili_yan || []).join(','),
      kategori: (filters.kategori || []).join(','),
      urun_ilk_grubu: (filters.urun_ilk_grubu || []).join(','),
      urun_ana_grubu: (filters.urun_ana_grubu || []).join(','),
      urun_alt_grubu: (filters.urun_alt_grubu || []).join(','),
      icerik: (filters.icerik || []).join(','),
      karisim: (filters.karisim || []).join(','),
      sort_by: tablePagination.value?.sortBy || '',
      desc: tablePagination.value?.descending ? 1 : 0,
      currencies: (selectedCurrencies.value || []).join(',')
    }

    for (const field of valueFilterFields) {
      const values = valueFilters.value[field] || []
      if (values.length > 0) {
        params[`vf_${field}`] = values.join(',')
      }
    }

    const stockQtyMin = String(numberRangeFilters.value.stockQty?.min || '').trim()
    const stockQtyMax = String(numberRangeFilters.value.stockQty?.max || '').trim()
    if (stockQtyMin) params.stock_qty_min = stockQtyMin
    if (stockQtyMax) params.stock_qty_max = stockQtyMax

    const stockEntryFrom = String(dateRangeFilters.value.stockEntryDate?.from || '').trim()
    const stockEntryTo = String(dateRangeFilters.value.stockEntryDate?.to || '').trim()
    const lastPricingFrom = String(dateRangeFilters.value.lastPricingDate?.from || '').trim()
    const lastPricingTo = String(dateRangeFilters.value.lastPricingDate?.to || '').trim()
    if (stockEntryFrom) params.stock_entry_from = stockEntryFrom
    if (stockEntryTo) params.stock_entry_to = stockEntryTo
    if (lastPricingFrom) params.last_pricing_from = lastPricingFrom
    if (lastPricingTo) params.last_pricing_to = lastPricingTo

    const blob = await download('/pricing/products/export-all', params)
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `product_pricing_all_${new Date().toISOString().slice(0, 10)}.csv`
    document.body.appendChild(a)
    a.click()
    a.remove()
    URL.revokeObjectURL(url)
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.message || 'Tum filtre export alinamadi' })
  } finally {
    exportAllLoading.value = false
  }
}

function needsRepricing (row) {
  const stockEntryDate = String(row?.stockEntryDate || '').trim()
  const lastPricingDate = String(row?.lastPricingDate || '').trim()
  if (!stockEntryDate) return false
  if (!lastPricingDate) return true
  return lastPricingDate < stockEntryDate
}

function needsCosting (row) {
  const stockEntryDate = String(row?.stockEntryDate || '').trim()
  const lastCostingDate = String(row?.lastCostingDate || '').trim()
  if (!stockEntryDate) return false
  if (!lastCostingDate) return true
  return lastCostingDate < stockEntryDate
}

function recalcByBasePrice (row) {
  row.basePriceTry = round2(row.basePriceUsd * usdToTry)
  let prevUsd = row.basePriceUsd
  let prevTry = row.basePriceTry
  let prevEur = round2(row.basePriceUsd * usdToTry / eurToTry)
  multipliers.forEach((multiplier, index) => {
    const nextUsd = round2(prevUsd * multiplier)
    const nextTry = round2(prevTry * multiplier)
    const nextEur = round2(prevEur * multiplier)
    row[`usd${index + 1}`] = nextUsd
    row[`eur${index + 1}`] = nextEur
    row[`try${index + 1}`] = nextTry
    prevUsd = nextUsd
    prevTry = nextTry
    prevEur = nextEur
  })
}

function onEditableCellChange (row, field, val) {
  const parsed = parseNumber(val)
  store.updateCell(row, field, parsed)
  if (field === 'basePriceUsd') recalcByBasePrice(row)
}

function setCalcLoading (productCode, value) {
  calcLoadingMap.value = {
    ...calcLoadingMap.value,
    [productCode]: !!value
  }
}

function applyPreviewRowToUiRow (row, preview) {
  row.basePriceUsd = round2(preview?.base_price_usd)
  row.basePriceTry = round2(preview?.base_price_try)
  row.usd1 = round2(preview?.usd1)
  row.usd2 = round2(preview?.usd2)
  row.usd3 = round2(preview?.usd3)
  row.usd4 = round2(preview?.usd4)
  row.usd5 = round2(preview?.usd5)
  row.usd6 = round2(preview?.usd6)
  row.eur1 = round2(preview?.eur1)
  row.eur2 = round2(preview?.eur2)
  row.eur3 = round2(preview?.eur3)
  row.eur4 = round2(preview?.eur4)
  row.eur5 = round2(preview?.eur5)
  row.eur6 = round2(preview?.eur6)
  row.try1 = round2(preview?.try1)
  row.try2 = round2(preview?.try2)
  row.try3 = round2(preview?.try3)
  row.try4 = round2(preview?.try4)
  row.try5 = round2(preview?.try5)
  row.try6 = round2(preview?.try6)
}

async function calculateRow (row) {
  if (!row?.productCode) return
  const productCode = String(row.productCode).trim()
  if (!productCode) return

  setCalcLoading(productCode, true)
  console.info('[product-pricing][ui] calc-row:start', { product_code: productCode })
  try {
    const res = await api.post('/pricing/products/calculate-snapshots', {
      preview_only: true,
      product_codes: [productCode]
    }, {
      timeout: 180000
    })
    const list = Array.isArray(res?.data?.rows) ? res.data.rows : []
    const preview = list.find((item) => String(item?.product_code || '').trim() === productCode)
    if (!preview) {
      Notify.create({ type: 'warning', message: 'Bu urun icin hesap sonucu donmedi.' })
      return
    }
    applyPreviewRowToUiRow(row, preview)
    toggleRowSelection(rowSelectionKey(row), true)
    console.info('[product-pricing][ui] calc-row:done', { product_code: productCode })
  } catch (err) {
    console.error('[product-pricing][ui] calc-row:error', {
      product_code: productCode,
      status: err?.response?.status ?? null,
      message: err?.response?.data || err?.message || 'calc-row failed'
    })
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Hesaplama onizlemesi alinamadi' })
  } finally {
    setCalcLoading(productCode, false)
  }
}

async function openPriceHistoryDialog (row) {
  if (!row?.productCode) return
  priceHistoryRow.value = row
  priceHistoryTab.value = 'pg'
  priceHistory.value = { postgres: [], mssql: [] }
  selectedPgIds.value = []
  selectedMssqlIds.value = []
  priceHistoryDialogOpen.value = true
  await reloadPriceHistory()
}

async function reloadPriceHistory () {
  const code = String(priceHistoryRow.value?.productCode || '').trim()
  if (!code) return
  priceHistoryLoading.value = true
  try {
    const traceId = `ui-${Date.now()}-${Math.random().toString(16).slice(2)}`
    const res = await api.request({
      method: 'GET',
      url: `/pricing/products/${encodeURIComponent(code)}/price-history`,
      timeout: 180000,
      headers: { 'X-Trace-ID': traceId }
    })
    priceHistory.value = {
      postgres: Array.isArray(res?.data?.postgres) ? res.data.postgres : [],
      mssql: Array.isArray(res?.data?.mssql) ? res.data.mssql : []
    }
    // keep selection but drop ids that no longer exist
    const pgSet = new Set(priceHistory.value.postgres.map((r) => String(r?.id || '').trim()).filter(Boolean))
    const msSet = new Set(priceHistory.value.mssql.map((r) => String(r?.price_list_line_id || '').trim()).filter(Boolean))
    selectedPgIds.value = (selectedPgIds.value || []).filter((id) => pgSet.has(id))
    selectedMssqlIds.value = (selectedMssqlIds.value || []).filter((id) => msSet.has(id))
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Fiyat gecmisi yuklenemedi' })
  } finally {
    priceHistoryLoading.value = false
  }
}

function toggleSelectedPgId (id, val) {
  const sid = String(id || '').trim()
  if (!sid) return
  const set = new Set(selectedPgIds.value || [])
  if (val) set.add(sid)
  else set.delete(sid)
  selectedPgIds.value = Array.from(set)
}

function toggleSelectedMssqlId (id, val) {
  const sid = String(id || '').trim()
  if (!sid) return
  const set = new Set(selectedMssqlIds.value || [])
  if (val) set.add(sid)
  else set.delete(sid)
  selectedMssqlIds.value = Array.from(set)
}

async function confirmDeleteSelectedHistory () {
  const code = String(priceHistoryRow.value?.productCode || '').trim()
  if (!code) return

  const pgCount = selectedPgIds.value?.length || 0
  const msCount = selectedMssqlIds.value?.length || 0
  if (pgCount + msCount === 0) return

  await $q.dialog({
    title: 'Secilenleri Sil',
    message: `Secili kayitlari silmek istiyor musunuz? (B2B/B2C: ${pgCount}, NEBIM_V3: ${msCount})`,
    cancel: true,
    persistent: true,
    ok: { label: 'Sil', color: 'negative' },
    cancel: { label: 'Vazgec', color: 'grey-7', flat: true }
  }).onOk(async () => {
    const traceId = `ui-${Date.now()}-${Math.random().toString(16).slice(2)}`
    const payload = {
      pg_ids: (selectedPgIds.value || []).map((x) => String(x || '').trim()).filter(Boolean),
      mssql_ids: (selectedMssqlIds.value || []).map((x) => String(x || '').trim()).filter(Boolean)
    }
    await api.request({
      method: 'POST',
      url: `/pricing/products/${encodeURIComponent(code)}/price-history/delete-selected`,
      data: payload,
      timeout: 180000,
      headers: { 'X-Trace-ID': traceId }
    })
    Notify.create({ type: 'positive', message: 'Secilen kayitlar silindi.' })
    selectedPgIds.value = []
    selectedMssqlIds.value = []
    await reloadPriceHistory()
    await reloadData({ page: currentPage.value })
  })
}

let tableMiddleScrollEl = null
let horizontalResizeObserver = null
let syncingTopScroll = false

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

function syncTopScrollWidth () {
  const top = topScrollRef.value
  const inner = topScrollInnerRef.value
  const middle = getTableMiddleScrollEl()
  if (!top || !inner || !middle) return
  const scrollWidth = Math.max(middle.scrollWidth, tableMinWidth.value, top.clientWidth)
  inner.style.width = `${scrollWidth}px`
  if (top.scrollLeft !== middle.scrollLeft) {
    top.scrollLeft = middle.scrollLeft
  }
}

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

function onTableMiddleScroll () {
  const top = topScrollRef.value
  const middle = getTableMiddleScrollEl()
  if (!top || !middle || syncingTopScroll) return
  syncingTopScroll = true
  top.scrollLeft = middle.scrollLeft
  requestAnimationFrame(() => {
    syncingTopScroll = false
  })
}

async function bindHorizontalScrollSync () {
  await nextTick()
  const middle = getTableMiddleScrollEl()
  if (tableMiddleScrollEl && tableMiddleScrollEl !== middle) {
    tableMiddleScrollEl.removeEventListener('scroll', onTableMiddleScroll)
  }
  tableMiddleScrollEl = middle
  if (tableMiddleScrollEl) {
    tableMiddleScrollEl.removeEventListener('scroll', onTableMiddleScroll)
    tableMiddleScrollEl.addEventListener('scroll', onTableMiddleScroll, { passive: true })
  }
  if (horizontalResizeObserver) {
    horizontalResizeObserver.disconnect()
    horizontalResizeObserver = null
  }
  if (typeof ResizeObserver !== 'undefined') {
    horizontalResizeObserver = new ResizeObserver(() => {
      syncTopScrollWidth()
    })
    if (topScrollRef.value) horizontalResizeObserver.observe(topScrollRef.value)
    if (tableMiddleScrollEl) horizontalResizeObserver.observe(tableMiddleScrollEl)
  }
  syncTopScrollWidth()
}

function onBrandGroupSelectionChange (row, val) {
  // no-op (read-only)
}

function isRowSelected (rowKey) {
  const k = String(rowKey ?? '').trim()
  if (!k) return false
  return !!selectedMap.value[k]
}

const selectedToneColumnNameSet = new Set([
  // "Karisim"e kadar olan sol kolonlar (fiyat kolonlarini boyamayalim)
  'brandGroupSelection',
  'marka',
  'productCode',
  'stockQty',
  'stockEntryDate',
  'lastCostingDate',
  'lastPricingDate',
  'askiliYan',
  'kategori',
  'urunIlkGrubu',
  'urunAnaGrubu',
  'urunAltGrubu',
  'icerik',
  'karisim'
])

function shouldToneSelectedCell (row, colName) {
  if (!selectedToneColumnNameSet.has(String(colName || '').trim())) return false
  if (!isRowSelected(rowSelectionKey(row))) return false
  // don't override critical warning coloring
  if (String(colName || '').trim() === 'lastCostingDate' && needsCosting(row)) return false
  return true
}

function onRowCheckboxChange (row, val) {
  if (!row) return
  toggleRowSelection(rowSelectionKey(row), val)
}

function toggleRowSelection (rowKey, val) {
  const k = String(rowKey ?? '').trim()
  if (!k) return
  selectedMap.value = { ...selectedMap.value, [k]: !!val }
}

function isRowDirty (row) {
  if (!row) return false
  const fields = [
    'basePriceUsd',
    'basePriceTry',
    'usd1', 'usd2', 'usd3', 'usd4', 'usd5', 'usd6',
    'eur1', 'eur2', 'eur3', 'eur4', 'eur5', 'eur6',
    'try1', 'try2', 'try3', 'try4', 'try5', 'try6'
  ]
  for (const f of fields) {
    const cur = Number(row?.[f] ?? 0)
    const orig = Number(row?.[`__orig_${f}`] ?? 0)
    if (Math.abs(cur - orig) > 1e-9) return true
  }
  return false
}

const selectedRows = computed(() => {
  const map = selectedMap.value || {}
  return rows.value.filter((r) => !!map[rowSelectionKey(r)])
})

const selectedDirtyRows = computed(() => selectedRows.value.filter(isRowDirty))
const selectedDirtyCount = computed(() => selectedDirtyRows.value.length)
const saveButtonLabel = computed(() => {
  if (selectedDirtyCount.value > 0) return `Kaydet (${selectedDirtyCount.value})`
  return 'Kaydet'
})

async function calculateSelectedRows () {
  const list = selectedRows.value
  if (!Array.isArray(list) || list.length === 0) return
  const productCodes = list
    .map((r) => String(r?.productCode || '').trim())
    .filter(Boolean)
  if (productCodes.length === 0) return

  bulkCalcLoading.value = true
  console.info('[product-pricing][ui] bulk-calc:start', { selected: productCodes.length })
  try {
    const res = await api.post('/pricing/products/calculate-snapshots', {
      preview_only: true,
      product_codes: productCodes
    }, {
      timeout: 180000
    })
    const previewRows = Array.isArray(res?.data?.rows) ? res.data.rows : []
    const byCode = new Map(previewRows.map((p) => [String(p?.product_code || '').trim(), p]))
    let applied = 0
    for (const row of rows.value) {
      const code = String(row?.productCode || '').trim()
      if (!code) continue
      if (!selectedMap.value?.[rowSelectionKey(row)]) continue
      const p = byCode.get(code)
      if (!p) continue
      applyPreviewRowToUiRow(row, p)
      applied++
    }
    Notify.create({ type: 'positive', message: `Hesaplandi: ${applied} / ${productCodes.length}` })
    console.info('[product-pricing][ui] bulk-calc:done', { applied, selected: productCodes.length })
  } catch (err) {
    console.error('[product-pricing][ui] bulk-calc:error', {
      status: err?.response?.status ?? null,
      message: err?.response?.data || err?.message || 'bulk-calc failed'
    })
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Toplu hesaplama basarisiz' })
  } finally {
    bulkCalcLoading.value = false
  }
}

async function saveSelectedRows () {
  const list = selectedDirtyRows.value
  if (!Array.isArray(list) || list.length === 0) return

  saving.value = true
  try {
    const traceId = `ui-${Date.now()}-${Math.random().toString(16).slice(2)}`
    console.info('[product-pricing][ui] save:start', { trace_id: traceId, dirty_count: list.length })
    const payload = {
      items: list.map((r) => ({
        product_code: String(r?.productCode || '').trim(),
        base_price_usd: Number(r?.basePriceUsd ?? 0),
        base_price_try: Number(r?.basePriceTry ?? 0),
        usd1: Number(r?.usd1 ?? 0),
        usd2: Number(r?.usd2 ?? 0),
        usd3: Number(r?.usd3 ?? 0),
        usd4: Number(r?.usd4 ?? 0),
        usd5: Number(r?.usd5 ?? 0),
        usd6: Number(r?.usd6 ?? 0),
        eur1: Number(r?.eur1 ?? 0),
        eur2: Number(r?.eur2 ?? 0),
        eur3: Number(r?.eur3 ?? 0),
        eur4: Number(r?.eur4 ?? 0),
        eur5: Number(r?.eur5 ?? 0),
        eur6: Number(r?.eur6 ?? 0),
        try1: Number(r?.try1 ?? 0),
        try2: Number(r?.try2 ?? 0),
        try3: Number(r?.try3 ?? 0),
        try4: Number(r?.try4 ?? 0),
        try5: Number(r?.try5 ?? 0),
        try6: Number(r?.try6 ?? 0)
      }))
    }

    await api.request({
      method: 'POST',
      url: '/pricing/products/save',
      data: payload,
      timeout: 0,
      headers: { 'X-Trace-ID': traceId }
    })

    Notify.create({ type: 'positive', message: `Kaydedildi: ${list.length}` })
    console.info('[product-pricing][ui] save:done', { trace_id: traceId, dirty_count: list.length })

    // After persisting, clear selection state and reload from backend.
    // This avoids "Kaydet(1) but checkbox not ticked" confusion and ensures UI reflects DB.
    selectedMap.value = {}
    showSelectedOnly.value = false
    await reloadData({ page: currentPage.value, useCache: false })
  } catch (err) {
    console.error('[product-pricing][ui] save:error', {
      status: err?.response?.status ?? null,
      trace_id: err?.response?.headers?.['x-trace-id'] || null,
      message: err?.response?.data || err?.message || 'save failed'
    })
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Kaydedilemedi' })
  } finally {
    saving.value = false
  }
}

function toggleSelectAllVisible (val) {
  const next = { ...selectedMap.value }
  visibleRowIds.value.forEach((id) => { next[id] = !!val })
  selectedMap.value = next
}

function resetAll () {
  columnFilters.value = {
    productCode: [],
    brandGroupSelection: [],
    marka: [],
    askiliYan: [],
    kategori: [],
    urunIlkGrubu: [],
    urunAnaGrubu: [],
    urunAltGrubu: [],
    icerik: [],
    karisim: []
  }
  columnFilterSearch.value = {
    productCode: '',
    brandGroupSelection: '',
    marka: '',
    askiliYan: '',
    kategori: '',
    urunIlkGrubu: '',
    urunAnaGrubu: '',
    urunAltGrubu: '',
    icerik: '',
    karisim: ''
  }
  valueFilters.value = Object.fromEntries(valueFilterFields.map((field) => [field, []]))
  valueFilterSearch.value = Object.fromEntries(valueFilterFields.map((field) => [field, '']))
  numberRangeFilters.value = {
    stockQty: { min: '', max: '' }
  }
  dateRangeFilters.value = {
    stockEntryDate: { from: '', to: '' },
    lastPricingDate: { from: '', to: '' }
  }
  showSelectedOnly.value = false
  selectedMap.value = {}
}

function toggleShowSelectedOnly () {
  if (!showSelectedOnly.value && selectedRowCount.value === 0) return
  showSelectedOnly.value = !showSelectedOnly.value
}

function isCurrencySelected (code) {
  return selectedCurrencies.value.includes(code)
}

function toggleCurrency (code, checked) {
  const set = new Set(selectedCurrencies.value)
  if (checked) set.add(code)
  else set.delete(code)
  selectedCurrencies.value = currencyOptions.map((x) => x.value).filter((x) => set.has(x))
}

function toggleCurrencyRow (code) {
  toggleCurrency(code, !isCurrencySelected(code))
}

function selectAllCurrencies () {
  selectedCurrencies.value = currencyOptions.map((x) => x.value)
}

function clearAllCurrencies () {
  selectedCurrencies.value = []
}

function onPaginationChange (next) {
  const prevSortBy = tablePagination.value.sortBy
  const prevDesc = tablePagination.value.descending
  tablePagination.value = {
    ...tablePagination.value,
    ...(next || {}),
    page: 1,
    rowsPerPage: 0
  }
  const nextSortBy = tablePagination.value.sortBy
  const nextDesc = tablePagination.value.descending
  if (nextSortBy !== prevSortBy || nextDesc !== prevDesc) {
    currentPage.value = 1
    void reloadData({ page: 1 })
  }
}

function buildServerFilters () {
  return {
    product_code: columnFilters.value.productCode || [],
    brand_group_selection: columnFilters.value.brandGroupSelection || [],
    marka: columnFilters.value.marka || [],
    askili_yan: columnFilters.value.askiliYan || [],
    kategori: columnFilters.value.kategori || [],
    urun_ilk_grubu: columnFilters.value.urunIlkGrubu || [],
    urun_ana_grubu: columnFilters.value.urunAnaGrubu || [],
    urun_alt_grubu: columnFilters.value.urunAltGrubu || [],
    icerik: columnFilters.value.icerik || [],
    karisim: columnFilters.value.karisim || []
  }
}

function scheduleReload () {
  reloadScheduled.value = true
  if (reloadTimer) clearTimeout(reloadTimer)
  reloadTimer = setTimeout(() => {
    reloadTimer = null
    void reloadData({ page: 1 })
  }, 180)
}

async function fetchChunk ({ page = 1, useCache = true } = {}) { 
  const filters = buildServerFilters()
  const hasAnyFilter = Object.values(filters).some((v) => Array.isArray(v) && v.length > 0)
  const productCodeCount = Array.isArray(filters.product_code) ? filters.product_code.length : 0
  const hasPrimaryFilter = (filters.urun_ilk_grubu?.length || 0) > 0 || (filters.urun_ana_grubu?.length || 0) > 0
  const hasNarrowProductFilter = productCodeCount > 0
  if (!hasAnyFilter) {
    // This endpoint is expensive without filters; require the user to scope down first.
    store.rows = []
    store.error = 'Liste cok buyuk. Lutfen en az bir filtre secin (or: Urun Ilk Grubu / Urun Ana Grubu / Urun Kodu).'
    store.totalCount = 0
    store.totalPages = 1
    store.page = 1
    store.hasMore = false
    return 0
  }
  if (!hasPrimaryFilter && !hasNarrowProductFilter) {
    store.rows = []
    store.error = GUIDANCE_MSG
    store.totalCount = 0
    store.totalPages = 1
    store.page = 1
    store.hasMore = false
    return 0
  }
  const effectiveLimit = hasNarrowProductFilter
    ? Math.max(productCodeCount, 1)
    : PAGE_LIMIT
  const result = await store.fetchRows({ 
    limit: effectiveLimit, 
    page, 
    append: false, 
    silent: false, 
    useCache,
    filters,
    sortBy: tablePagination.value.sortBy,
    descending: tablePagination.value.descending
  }) 
  currentPage.value = Number(result?.page) || page 
  return Number(result?.fetched) || 0 
} 

async function reloadData ({ page = 1, useCache = true } = {}) {
  if (isReloading.value) {
    reloadScheduled.value = false
    return
  }
  const startedAt = Date.now()
  isReloading.value = true
  reloadScheduled.value = false
  console.info('[product-pricing][ui] reload:start', {
    at: new Date(startedAt).toISOString()
  })
  try {
    await fetchChunk({ page, useCache })
  } catch (err) {
    console.error('[product-pricing][ui] reload:error', {
      duration_ms: Date.now() - startedAt,
      message: String(err?.message || err || 'reload failed')
    })
  } finally {
    console.info('[product-pricing][ui] reload:done', {
      duration_ms: Date.now() - startedAt,
      row_count: Array.isArray(store.rows) ? store.rows.length : 0,
      has_error: Boolean(store.error)
    })
    await bindHorizontalScrollSync()
    // Let the table render before we re-enable actions (prevents double-submits while the UI is still updating).
    await nextTick()
    const remainingBusyMs = Math.max(0, 350 - (Date.now() - startedAt))
    await new Promise((resolve) => setTimeout(resolve, remainingBusyMs))
    console.info('[product-pricing][ui] render:done', {
      duration_ms: Date.now() - startedAt,
      row_count: Array.isArray(store.rows) ? store.rows.length : 0
    })
    reloadScheduled.value = false
    isReloading.value = false
  }
}

// Full "fetch all pages" is intentionally avoided; keep server-side paging for performance.

function onPageChange (page) {
  const p = Number(page) > 0 ? Number(page) : 1
  if (store.loading) return
  if (p === currentPage.value && p === (store.page || 1)) return
  currentPage.value = p
  void reloadData({ page: p })
}

onMounted(async () => {
  // Prefetch a couple of common filters so the first open is not empty.
  void fetchServerFilterOptions('urunIlkGrubu')
  void fetchServerFilterOptions('urunAnaGrubu')
  // Do not auto-fetch listing on mount; user must scope by group first.
  store.rows = []
  store.error = GUIDANCE_MSG
  store.totalCount = 0
  store.totalPages = 1
  store.page = 1
  store.hasMore = false
  await bindHorizontalScrollSync()
})

watch(
  () => [tableMinWidth.value, rows.value.length, selectedCurrencies.value.join(',')],
  () => {
    void bindHorizontalScrollSync()
  }
)

onBeforeUnmount(() => {
  if (reloadTimer) {
    clearTimeout(reloadTimer)
    reloadTimer = null
  }
  if (tableMiddleScrollEl) {
    tableMiddleScrollEl.removeEventListener('scroll', onTableMiddleScroll)
    tableMiddleScrollEl = null
  }
  if (horizontalResizeObserver) {
    horizontalResizeObserver.disconnect()
    horizontalResizeObserver = null
  }
})

// NOTE: Listing fetch is intentionally manual via "Gruplari Getir" for performance.
</script>

<style scoped>
.pricing-page {
  --pricing-row-height: 31px;
  --pricing-header-height: 72px;
  --pricing-table-height: calc(100vh - 210px);

  position: relative;
  height: calc(100vh - 120px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.currency-menu-list {
  min-width: 170px;
}

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

.page-busy-label {
  color: #1f2937;
  font-size: 13px;
  font-weight: 700;
}

.top-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  width: 100%;
}

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

.top-bar {
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
}

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

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

/* paging group is inside actions row now */

.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--paging :deep(.q-pagination) {
  margin-right: 4px;
}

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

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

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

.toolbar-group :deep(.q-btn__content .q-icon) {
  font-size: 18px;
}

.toolbar-group :deep(.q-btn .q-icon) {
  margin-right: 2px;
}

.toolbar-group :deep(.q-btn__content span) {
  line-height: 1.1;
}

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

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

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

@media (max-width: 1240px) {
  .top-actions-row--filters,
  .top-actions-row--actions {
    justify-content: flex-start;
  }
}

.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;
}

.empty-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 5;
  pointer-events: none;
}

.empty-overlay-inner {
  width: min(720px, 100%);
  border: 1px dashed rgba(0, 0, 0, 0.18);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.92);
  padding: 16px 18px;
  text-align: center;
}

.price-history-card {
  width: 980px;
  max-width: 95vw;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}

.price-history-card :deep(.q-card__section) {
  flex: 0 0 auto;
}

.price-history-card :deep(.q-tab-panels) {
  max-height: 62vh;
  overflow: auto;
}

.top-x-scroll {
  flex: 0 0 14px;
  height: 14px;
  overflow-x: auto;
  overflow-y: hidden;
  background: #fff;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

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

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

.pricing-table :deep(.q-table__middle) {
  height: calc(var(--pricing-table-height) - 14px);
  min-height: calc(var(--pricing-table-height) - 14px);
  max-height: calc(var(--pricing-table-height) - 14px);
  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(th),
.pricing-table :deep(td) {
  box-sizing: border-box;
  padding: 0 1px;
  overflow: hidden;
  vertical-align: middle;
}

.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: var(--pricing-row-height);
  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;
  padding: 0 1px !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: nowrap;
  word-break: normal;
  text-overflow: ellipsis;
  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;
}

.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;
  text-overflow: ellipsis;
  white-space: normal;
  font-weight: 800;
  line-height: 1.15;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

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

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

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

.range-filter-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}

.range-filter-field {
  min-width: 0;
}

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

.excel-filter-select :deep(.q-field__native),
.excel-filter-select :deep(.q-field__input) {
  font-weight: 700;
}

.excel-filter-actions {
  gap: 4px;
}

.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;
}

.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 .cell-text),
.pricing-table :deep(td.ps-col .product-code-text),
.pricing-table :deep(td.ps-col .stock-qty-text) {
  font-size: 11px;
  line-height: 1.1;
  white-space: normal;
  word-break: break-word;
}

.pricing-table :deep(td.selected-tone-cell) {
  /* "Secondary" tonlu secim vurgusu (yalnizca karisima kadar olan sol kolonlar) */
  background: color-mix(in srgb, var(--q-secondary) 12%, #ffffff);
}

.stock-qty-text {
  display: block;
  width: 100%;
  text-align: center;
  font-weight: 700;
  padding: 0 4px;
}

.date-cell-text {
  display: block;
  width: 100%;
  text-align: center;
  font-weight: 700;
  padding: 0 4px;
}

.date-warning {
  color: #c62828;
}

.cell-danger {
  background: #c62828 !important;
}

.pricing-table :deep(th.selection-col),
.pricing-table :deep(td.selection-col) {
  background: #fff;
  color: var(--q-primary);
  padding-left: 0 !important;
  padding-right: 0 !important;
}

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

.pricing-table :deep(.selection-col .q-checkbox__inner) {
  color: var(--q-primary);
  font-size: 16px;
}

.pricing-table :deep(th.selection-col .q-checkbox),
.pricing-table :deep(td.selection-col .q-checkbox) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.pricing-table :deep(.selection-col .q-checkbox__bg) {
  border-color: var(--q-primary);
}

.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;
}

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

.product-code-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  font-weight: 700;
  letter-spacing: 0;
}

.editable-price-cell {
  display: flex !important;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  width: 100%;
  align-items: stretch !important;
}

.old-price-label {
  display: block;
  width: 90%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 12px;
  line-height: 1;
  font-weight: 700;
  color: #7c3aed;
  text-align: right;
  margin: 0 auto;
  padding-right: 1px;
  margin-top: 0;
}

.native-cell-input,
.native-cell-select {
  width: 90%;
  height: 22px;
  box-sizing: border-box;
  padding: 1px 1px;
  border: 1px solid #cfd8dc;
  border-radius: 4px;
  background: #fff;
  font-size: 12px;
  margin: 0 auto;
  font-variant-numeric: tabular-nums;
}

.price-edit-input {
  font-size: 12px;
  font-weight: 700;
  margin-bottom: 0;
}

.native-cell-input:focus,
.native-cell-select:focus {
  outline: none;
  border-color: #1976d2;
}
</style>
