<template>
  <q-page class="product-performance-page q-pa-sm">
    <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">{{ loadingMessage }}</div>
        <div v-if="loadingElapsedLabel" class="page-busy-time">{{ loadingElapsedLabel }}</div>
      </div>
    </teleport>
    <div class="performance-top-bar row items-center justify-between q-col-gutter-xs q-mb-xs">
      <div class="col-12 col-md">
        <div class="performance-page-title">Ürün Performans ve Karlılık Analizi</div>
        <div class="performance-page-subtitle text-grey-7">
          Ürün, renk, yaka, piyasa ve müşteri segmenti bazında satış, stok dönüşü ve karlılık KPI'ları.
        </div>
      </div>
      <div class="performance-top-actions col-12 col-md-auto row items-center q-gutter-xs">
        <q-option-group
          v-model="selectedPeriods"
          :options="periodOptions"
          type="checkbox"
          inline
          dense
          :disable="pageBusy"
          class="period-selector bg-white q-px-xs q-py-none"
        />
        <q-btn
          dense
          outline
          color="primary"
          icon="view_column"
          :label="detailColumnsHidden ? 'Detay Kolonları Göster' : 'Detay Kolonları Gizle'"
          :disable="pageBusy"
          @click="detailColumnsHidden = !detailColumnsHidden"
        />
        <q-btn-dropdown
          v-model="detailLevelMenuOpen"
          split
          dense
          outline
          color="primary"
          :icon="allProductGroupsExpanded ? 'unfold_less' : 'unfold_more'"
          :label="allProductGroupsExpanded ? 'Detayları Kapat' : 'Tüm Detayları Aç'"
          :disable="pageBusy"
          @click="toggleAllProductGroups"
        >
          <div class="detail-level-menu">
            <q-option-group
              v-model="activeSelectedExpandLevelKeys"
              :options="expandLevelOptions"
              type="checkbox"
              dense
            />
            <q-separator class="q-my-sm" />
            <div class="row items-center justify-end q-gutter-xs">
              <q-btn dense flat color="grey-7" label="Kapat" :disable="pageBusy" @click.stop="collapseAllProductGroups" />
              <q-btn dense color="primary" label="Seçileni Aç" :disable="pageBusy" @click.stop="expandSelectedProductGroups" />
            </div>
          </div>
        </q-btn-dropdown>
        <q-btn dense outline color="positive" icon="grid_on" label="Excel" :loading="excelExportLoading" :disable="pageBusy || excelExportLoading" @click="exportProductPerformanceExcel" />
        <q-btn dense outline color="secondary" icon="refresh" label="Yenile" :loading="loading" :disable="pageBusy" @click="reload" />
      </div>
    </div>

    <div class="performance-summary-row row q-col-gutter-xs q-mb-xs">
      <div v-for="card in summaryCards" :key="card.key" class="col-6 col-md-2">
        <q-card flat bordered class="metric-card">
          <q-card-section>
            <div class="text-caption text-grey-7">{{ card.label }}</div>
            <div class="metric-value">{{ card.value }}</div>
          </q-card-section>
        </q-card>
      </div>
    </div>

    <div class="performance-tab-grid q-mb-xs">
      <button
        v-for="tab in performanceTabs"
        :key="tab.name"
        type="button"
        :class="['performance-tab-button', { active: activeTab === tab.name }]"
        :disabled="pageBusy"
        @click="switchPerformanceTab(tab.name)"
      >
        <q-icon :name="tab.icon" size="16px" />
        <span>{{ tab.label }}</span>
      </button>
    </div>

    <div ref="topScrollbarRef" class="performance-top-scrollbar q-mb-xs">
      <div ref="topScrollbarInnerRef" class="performance-top-scrollbar-inner"></div>
    </div>

    <q-table
      v-if="activeTab === 'general'"
      flat
      bordered
      row-key="row_key"
      :class="[
        'performance-table',
        'sticky-dim-table',
        detailColumnsHidden ? 'sticky-dim-4' : 'sticky-dim-7',
        'bg-white'
      ]"
      :rows="filteredGeneralRows"
      :columns="visibleGeneralColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.general"
      virtual-scroll
      :virtual-scroll-item-size="productThumbVirtualItemSize"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('general', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('general', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('general', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('general', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('general', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('general', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('general', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('general', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('general', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('general', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('general', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-image="props">
        <q-td :props="props" class="product-image-cell">
          <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
            <img
              v-if="getCachedProductImageUrl(props.row)"
              :src="getCachedProductImageUrl(props.row)"
              class="product-thumb"
              @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
              loading="lazy"
              decoding="async"
              alt=""
            />
            <div v-else class="product-thumb-placeholder">
              <q-icon name="image" size="22px" />
            </div>
            <q-tooltip>Foto</q-tooltip>
          </button>
        </q-td>
      </template>
      <template #body-cell-performance_bucket="props">
        <q-td :props="props">
          <q-badge :color="bucketColor(props.row.performance_bucket)">
            {{ bucketLabel(props.row.performance_bucket) }}
          </q-badge>
        </q-td>
      </template>
    </q-table>

    <q-table
      v-else-if="activeTab === 'order_product_customers'"
      flat
      bordered
      row-key="row_key"
      class="performance-table sticky-dim-table sticky-dim-8 bg-white"
      :rows="displayOrderProductCustomerTableRows"
      :columns="orderProductCustomerColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.order_product_customers"
      :sort-method="sortProductGroupedTableRows"
      virtual-scroll
      :virtual-scroll-item-size="productThumbVirtualItemSize"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('order_product_customers', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('order_product_customers', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('order_product_customers', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('order_product_customers', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('order_product_customers', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('order_product_customers', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('order_product_customers', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('order_product_customers', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('order_product_customers', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('order_product_customers', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('order_product_customers', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-image="props">
        <q-td :props="props" class="product-image-cell">
          <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
            <img
              v-if="getCachedProductImageUrl(props.row)"
              :src="getCachedProductImageUrl(props.row)"
              class="product-thumb"
              @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
              loading="lazy"
              decoding="async"
              alt=""
            />
            <div v-else class="product-thumb-placeholder">
              <q-icon name="image" size="22px" />
            </div>
            <q-tooltip>Foto</q-tooltip>
          </button>
        </q-td>
      </template>
      <template #body-cell-performance_bucket="props">
        <q-td :props="props">
          <q-badge :color="bucketColor(props.row.performance_bucket)">
            {{ bucketLabel(props.row.performance_bucket) }}
          </q-badge>
        </q-td>
      </template>
      <template #body-cell-net_stock_after_order="props">
        <q-td :props="props" class="text-right" :class="Number(props.row.net_stock_after_order || 0) < 0 ? 'text-negative text-weight-bold' : ''">
          {{ formatNumber(props.row.net_stock_after_order, 0) }}
        </q-td>
      </template>
      <template #body-cell-expected_profit_cost_usd="props">
        <q-td :props="props" class="text-right" :class="Number(props.row.expected_profit_cost_usd || 0) < 0 ? 'text-negative text-weight-bold' : 'text-positive'">
          {{ formatMoney(props.row.expected_profit_cost_usd, 'USD') }}
        </q-td>
      </template>
      <template #body="props">
        <q-tr
          v-if="props.row.__group"
          :props="props"
          :class="groupRowClasses(props.row)"
        >
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <template v-if="col.name === 'image' && groupShowsImage(props.row)">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <div
              v-else-if="col.name === groupLabelColumnName(props.row)"
              class="group-cell-label"
              :style="{ paddingLeft: groupIndentPx(props.row) }"
            >
              <q-btn
                flat
                dense
                round
                size="sm"
                :icon="groupCanExpand(props.row) ? (isGroupExpanded(props.row.key) ? 'expand_more' : 'chevron_right') : 'fiber_manual_record'"
                :disable="!groupCanExpand(props.row)"
                @click.stop="toggleGroup(props.row.key)"
              />
              <span class="group-title">{{ groupDisplayLabel(props.row) }}</span>
            </div>
            <span v-else>{{ formatGroupCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
        <q-tr v-else :props="props" class="cursor-pointer" @click="openPerformanceDialog(props.row)">
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <template v-if="col.name === 'image'">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <q-badge v-else-if="col.name === 'performance_bucket'" :color="bucketColor(props.row.performance_bucket)">
              {{ bucketLabel(props.row.performance_bucket) }}
            </q-badge>
            <span v-else>{{ formatTableCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
      </template>
    </q-table>

    <q-table
      v-else-if="activeTab === 'order_market_details'"
      flat
      bordered
      row-key="row_key"
      class="performance-table sticky-dim-table sticky-dim-10 bg-white"
      :rows="displayOrderMarketDetailTableRows"
      :columns="orderMarketDetailColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.order_market_details"
      :sort-method="sortProductGroupedTableRows"
      virtual-scroll
      :virtual-scroll-item-size="64"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('order_market_details', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('order_market_details', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('order_market_details', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('order_market_details', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('order_market_details', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('order_market_details', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('order_market_details', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('order_market_details', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('order_market_details', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('order_market_details', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('order_market_details', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-performance_bucket="props">
        <q-td :props="props">
          <q-badge :color="bucketColor(props.row.performance_bucket)">
            {{ bucketLabel(props.row.performance_bucket) }}
          </q-badge>
        </q-td>
      </template>
      <template #body-cell-net_stock_after_order="props">
        <q-td :props="props" class="text-right" :class="Number(props.row.net_stock_after_order || 0) < 0 ? 'text-negative text-weight-bold' : ''">
          {{ formatNumber(props.row.net_stock_after_order, 0) }}
        </q-td>
      </template>
      <template #body-cell-expected_profit_cost_usd="props">
        <q-td :props="props" class="text-right" :class="Number(props.row.expected_profit_cost_usd || 0) < 0 ? 'text-negative text-weight-bold' : 'text-positive'">
          {{ formatMoney(props.row.expected_profit_cost_usd, 'USD') }}
        </q-td>
      </template>
      <template #body="props">
        <q-tr
          v-if="props.row.__group"
          :props="props"
          :class="groupRowClasses(props.row)"
        >
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <div
              v-if="col.name === groupLabelColumnName(props.row)"
              class="group-cell-label"
              :style="{ paddingLeft: groupIndentPx(props.row) }"
            >
              <q-btn
                flat
                dense
                round
                size="sm"
                :icon="groupCanExpand(props.row) ? (isGroupExpanded(props.row.key) ? 'expand_more' : 'chevron_right') : 'fiber_manual_record'"
                :disable="!groupCanExpand(props.row)"
                @click.stop="toggleGroup(props.row.key)"
              />
              <span class="group-title">{{ groupDisplayLabel(props.row) }}</span>
            </div>
            <span v-else>{{ formatGroupCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
        <q-tr v-else :props="props" class="cursor-pointer" @click="openPerformanceDialog(props.row)">
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <q-badge v-if="col.name === 'performance_bucket'" :color="bucketColor(props.row.performance_bucket)">
              {{ bucketLabel(props.row.performance_bucket) }}
            </q-badge>
            <span v-else>{{ formatTableCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
      </template>
    </q-table>

    <q-table
      v-else-if="isProductKpiTab"
      flat
      bordered
      row-key="row_key"
      :class="[
        'performance-table',
        'product-breakdown-table',
        'bg-white',
        { 'compact-detail-columns': detailColumnsHidden },
        activeTab === 'product_detail' ? 'product-detail-table' : 'product-summary-table'
      ]"
      :rows="displayProductKpiTableRows"
      :columns="activeProductColumns"
      :loading="activeTableLoading"
      v-model:pagination="activeProductPagination"
      :sort-method="sortProductGroupedTableRows"
      virtual-scroll
      :virtual-scroll-item-size="productThumbVirtualItemSize"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter(activeProductTableKey, props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter(activeProductTableKey, props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue(activeProductTableKey, props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey(activeProductTableKey, props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions(activeProductTableKey, props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter(activeProductTableKey, props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions(activeProductTableKey, props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions(activeProductTableKey, props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue(activeProductTableKey, props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected(activeProductTableKey, props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue(activeProductTableKey, props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>

      <template #body="props">
        <q-tr
          v-if="props.row.__group"
          :props="props"
          :class="groupRowClasses(props.row)"
        >
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <template v-if="col.name === 'image' && groupShowsImage(props.row)">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <div
              v-else-if="col.name === groupLabelColumnName(props.row)"
              class="group-cell-label"
              :style="{ paddingLeft: groupIndentPx(props.row) }"
            >
              <q-btn
                flat
                dense
                round
                size="sm"
                :icon="groupCanExpand(props.row) ? (isGroupExpanded(props.row.key) ? 'expand_more' : 'chevron_right') : 'fiber_manual_record'"
                :disable="!groupCanExpand(props.row)"
                @click.stop="toggleGroup(props.row.key)"
              />
              <span class="group-title">{{ groupDisplayLabel(props.row) }}</span>
            </div>
            <span v-else>{{ formatGroupCell(props.row, col.name) }}</span>
          </q-td>
        </q-tr>
        <q-tr v-else :props="props" class="cursor-pointer" @click="openPerformanceDialog(props.row)">
          <q-td v-for="col in props.cols" :key="col.name" :props="props">
            <template v-if="col.name === 'image'">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <q-badge v-else-if="col.name === 'performance_bucket'" :color="bucketColor(props.row.performance_bucket)">
              {{ bucketLabel(props.row.performance_bucket) }}
            </q-badge>
            <span v-else>{{ formatProductCell(props.row, col.name) }}</span>
          </q-td>
        </q-tr>
      </template>
    </q-table>

    <q-table
      v-else-if="activeTab === 'idle'"
      flat
      bordered
      row-key="row_key"
      class="performance-table sticky-dim-table sticky-dim-11 bg-white"
      :rows="displayIdleTableRows"
      :columns="idleColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.idle"
      :sort-method="sortProductGroupedTableRows"
      virtual-scroll
      :virtual-scroll-item-size="productThumbVirtualItemSize"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('idle', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('idle', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('idle', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('idle', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('idle', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('idle', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('idle', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('idle', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('idle', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('idle', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('idle', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-image="props">
        <q-td :props="props" class="product-image-cell">
          <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
            <img
              v-if="getCachedProductImageUrl(props.row)"
              :src="getCachedProductImageUrl(props.row)"
              class="product-thumb"
              @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
              loading="lazy"
              decoding="async"
              alt=""
            />
            <div v-else class="product-thumb-placeholder">
              <q-icon name="image" size="22px" />
            </div>
            <q-tooltip>Foto</q-tooltip>
          </button>
        </q-td>
      </template>

      <template #body-cell-idle_cost_usd="props">
        <q-td :props="props" class="text-right text-weight-bold">
          {{ formatMoney(props.row.idle_cost_usd, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-performance_bucket="props">
        <q-td :props="props">
          <q-badge :color="bucketColor(props.row.performance_bucket)">
            {{ bucketLabel(props.row.performance_bucket) }}
          </q-badge>
        </q-td>
      </template>
      <template #body="props">
        <q-tr
          v-if="props.row.__group"
          :props="props"
          :class="groupRowClasses(props.row)"
        >
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <template v-if="col.name === 'image' && groupShowsImage(props.row)">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <div
              v-else-if="col.name === groupLabelColumnName(props.row)"
              class="group-cell-label"
              :style="{ paddingLeft: groupIndentPx(props.row) }"
            >
              <q-btn
                flat
                dense
                round
                size="sm"
                :icon="groupCanExpand(props.row) ? (isGroupExpanded(props.row.key) ? 'expand_more' : 'chevron_right') : 'fiber_manual_record'"
                :disable="!groupCanExpand(props.row)"
                @click.stop="toggleGroup(props.row.key)"
              />
              <span class="group-title">{{ groupDisplayLabel(props.row) }}</span>
            </div>
            <span v-else>{{ formatGroupCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
        <q-tr v-else :props="props" class="cursor-pointer" @click="openPerformanceDialog(props.row)">
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <template v-if="col.name === 'image'">
              <button type="button" class="product-thumb-button" @click.stop="openProductImageDialog(props.row)">
                <img
                  v-if="getCachedProductImageUrl(props.row)"
                  :src="getCachedProductImageUrl(props.row)"
                  class="product-thumb"
                  @error="event => markProductImageFailed(props.row, event?.target?.currentSrc)"
                  loading="lazy"
                  decoding="async"
                  alt=""
                />
                <div v-else class="product-thumb-placeholder">
                  <q-icon name="image" size="22px" />
                </div>
                <q-tooltip>Foto</q-tooltip>
              </button>
            </template>
            <q-badge v-else-if="col.name === 'performance_bucket'" :color="bucketColor(props.row.performance_bucket)">
              {{ bucketLabel(props.row.performance_bucket) }}
            </q-badge>
            <span v-else>{{ formatTableCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
      </template>
    </q-table>

    <q-table
      v-else-if="activeTab === 'markets'"
      flat
      bordered
      row-key="market_key"
      class="performance-table sticky-dim-table sticky-dim-9 bg-white"
      :rows="filteredMarketRows"
      :columns="marketColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.markets"
      virtual-scroll
      :virtual-scroll-item-size="64"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('markets', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('markets', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('markets', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('markets', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('markets', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('markets', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('markets', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('markets', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('markets', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('markets', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('markets', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-stock_cost_value_usd="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.stock_cost_value_usd, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-risk_stock_cost_value_usd="props">
        <q-td :props="props" class="text-right text-negative text-weight-bold">
          {{ formatMoney(props.row.risk_stock_cost_value_usd, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-avg_gross_margin_90d="props">
        <q-td :props="props" class="text-right">
          {{ formatPercent(props.row.avg_gross_margin_90d) }}
        </q-td>
      </template>
    </q-table>

    <q-table
      v-else-if="salesBreakdownTabKeys.includes(activeTab)"
      flat
      bordered
      row-key="row_key"
      :class="[
        'performance-table',
        'sticky-dim-table',
        salesBreakdownStickyClass,
        activeTab === 'sales_color_yaka_market_customer' ? 'sales-color-yaka-table' : '',
        'bg-white'
      ]"
      :rows="displayActiveSalesBreakdownTableRows"
      :columns="visibleSalesBreakdownColumns"
      :loading="activeTableLoading"
      v-model:pagination="activeSalesBreakdownPagination"
      :sort-method="sortProductGroupedTableRows"
      virtual-scroll
      :virtual-scroll-item-size="productThumbVirtualItemSize"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter(activeTab, props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter(activeTab, props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue(activeTab, props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey(activeTab, props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions(activeTab, props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter(activeTab, props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions(activeTab, props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions(activeTab, props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue(activeTab, props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected(activeTab, props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue(activeTab, props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-sales_usd_90d="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.sales_usd_90d, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-sales_usd_365d="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.sales_usd_365d, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-avg_price_usd_90d="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.avg_price_usd_90d, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-performance_bucket="props">
        <q-td :props="props">
          <q-badge :color="bucketColor(props.row.performance_bucket)">
            {{ bucketLabel(props.row.performance_bucket) }}
          </q-badge>
        </q-td>
      </template>
      <template #body="props">
        <q-tr
          v-if="props.row.__group"
          :props="props"
          :class="groupRowClasses(props.row)"
        >
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <div
              v-if="col.name === groupLabelColumnName(props.row)"
              class="group-cell-label"
              :style="{ paddingLeft: groupIndentPx(props.row) }"
            >
              <q-btn
                flat
                dense
                round
                size="sm"
                :icon="groupCanExpand(props.row) ? (isGroupExpanded(props.row.key) ? 'expand_more' : 'chevron_right') : 'fiber_manual_record'"
                :disable="!groupCanExpand(props.row)"
                @click.stop="toggleGroup(props.row.key)"
              />
              <span class="group-title">{{ groupDisplayLabel(props.row) }}</span>
            </div>
            <span v-else>{{ formatGroupCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
        <q-tr v-else :props="props" class="cursor-pointer" @click="openPerformanceDialog(props.row)">
          <q-td v-for="col in props.cols" :key="col.name" :props="props" :class="groupCellClass(col.name)">
            <q-badge v-if="col.name === 'performance_bucket'" :color="bucketColor(props.row.performance_bucket)">
              {{ bucketLabel(props.row.performance_bucket) }}
            </q-badge>
            <span v-else>{{ formatTableCell(props.row, col) }}</span>
          </q-td>
        </q-tr>
      </template>
    </q-table>

    <div v-else-if="activeTab === 'customers'">
      <q-card flat bordered class="q-mb-sm">
        <q-card-section class="row q-col-gutter-sm items-center">
          <div class="col-12 col-md-4">
            <q-select
              v-model="customerBreakdown"
              dense
              outlined
              emit-value
              map-options
              label="Kırılım"
              :options="customerBreakdownOptions"
              @update:model-value="reload"
            />
          </div>
        </q-card-section>
      </q-card>
      <q-table
        flat
        bordered
        row-key="customer_key"
        class="performance-table sticky-dim-table sticky-dim-5 bg-white"
        :rows="filteredCustomerRows"
        :columns="customerColumns"
        :loading="activeTableLoading"
        v-model:pagination="tablePagination.customers"
        virtual-scroll
        :virtual-scroll-item-size="64"
        :virtual-scroll-slice-size="tableVirtualSliceSize"
      >
        <template #header-cell="props">
          <q-th :props="props" class="filterable-header-cell">
            <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('customers', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('customers', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('customers', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('customers', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('customers', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('customers', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('customers', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('customers', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('customers', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('customers', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('customers', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
        </template>
        <template #body-cell-sales_usd_90d="props">
          <q-td :props="props" class="text-right">
            {{ formatMoney(props.row.sales_usd_90d, 'USD') }}
          </q-td>
        </template>
        <template #body-cell-avg_price_usd_90d="props">
          <q-td :props="props" class="text-right">
            {{ formatMoney(props.row.avg_price_usd_90d, 'USD') }}
          </q-td>
        </template>
        <template #body-cell-sales_usd_365d="props">
          <q-td :props="props" class="text-right">
            {{ formatMoney(props.row.sales_usd_365d, 'USD') }}
          </q-td>
        </template>
      </q-table>
    </div>

    <q-table
      v-else
      flat
      bordered
      row-key="country_key"
      class="performance-table sticky-dim-table sticky-dim-3 bg-white"
      :rows="filteredCountryRows"
      :columns="countryColumns"
      :loading="activeTableLoading"
      v-model:pagination="tablePagination.countries"
      virtual-scroll
      :virtual-scroll-item-size="64"
      :virtual-scroll-slice-size="tableVirtualSliceSize"
    >
      <template #header-cell="props">
        <q-th :props="props" class="filterable-header-cell">
          <div class="header-with-filter">
            <span class="header-label" :title="props.col.label">{{ props.col.label }}</span>
            <q-btn
              v-if="isColumnFilterable(props.col.name)"
              dense
              flat
              round
              size="8px"
              icon="filter_alt"
              :color="hasColumnFilter('countries', props.col.name) ? 'primary' : 'grey-7'"
              class="header-filter-btn"
              @click.stop
            >
              <q-badge v-if="hasColumnFilter('countries', props.col.name)" color="primary" floating rounded>
                {{ getColumnFilterBadgeValue('countries', props.col.name) }}
              </q-badge>
              <q-menu anchor="bottom right" self="top right" :offset="[0, 4]">
                <div class="excel-filter-menu">
                  <q-input
                    v-model="columnFilterSearch[filterKey('countries', props.col.name)]"
                    dense
                    outlined
                    clearable
                    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="Tümünü Seç" @click="selectAllColumnFilterOptions('countries', props.col.name)" />
                    <q-btn flat dense size="sm" label="Temizle" @click="clearColumnFilter('countries', props.col.name)" />
                  </div>
                  <q-virtual-scroll
                    v-if="columnFilterOptions('countries', props.col.name).length > 0"
                    class="excel-filter-options"
                    :items="columnFilterOptions('countries', props.col.name)"
                    :virtual-scroll-item-size="32"
                    separator
                  >
                    <template #default="{ item: option }">
                      <q-item
                        :key="`${props.col.name}-${option.value}`"
                        dense
                        clickable
                        class="excel-filter-option"
                        @click="toggleColumnFilterValue('countries', props.col.name, option.value)"
                      >
                        <q-item-section avatar>
                          <q-checkbox
                            dense
                            size="sm"
                            :model-value="isColumnFilterValueSelected('countries', props.col.name, option.value)"
                            @update:model-value="() => toggleColumnFilterValue('countries', props.col.name, option.value)"
                            @click.stop
                          />
                        </q-item-section>
                        <q-item-section>
                          <q-item-label>{{ option.label }}</q-item-label>
                        </q-item-section>
                      </q-item>
                    </template>
                  </q-virtual-scroll>
                  <div v-else class="excel-filter-empty">Sonuç yok</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>
      </template>
      <template #body-cell-sales_usd_90d="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.sales_usd_90d, 'USD') }}
        </q-td>
      </template>
      <template #body-cell-avg_price_usd_90d="props">
        <q-td :props="props" class="text-right">
          {{ formatMoney(props.row.avg_price_usd_90d, 'USD') }}
        </q-td>
      </template>
    </q-table>

    <q-dialog v-model="imageDialog" maximized>
      <q-card class="product-image-dialog">
        <q-card-section class="row items-center q-pb-sm">
          <div>
            <div class="text-h6">{{ imageDialogTitle }}</div>
            <div class="text-caption text-grey-7">{{ imageDialogSubtitle }}</div>
          </div>
          <q-space />
          <q-btn v-close-popup flat round dense icon="close" />
        </q-card-section>
        <q-separator />
        <q-card-section class="product-image-dialog-body">
          <div class="performance-image-layout">
            <aside class="image-product-fields">
              <div class="image-section-title">Ürün Bilgileri</div>
              <div v-for="item in imageDialogInfoRows" :key="item.key" class="image-field-row">
                <span class="k">{{ item.label }}</span>
                <span class="v">{{ item.value }}</span>
              </div>

              <div class="image-product-section">
                <div class="image-section-title">Beden Stokları</div>
                <q-inner-loading :showing="detailLoading">
                  <q-spinner size="24px" color="primary" />
                </q-inner-loading>
                <div v-if="detailStockSizes.length" class="image-size-stock-grid">
                  <div v-for="item in detailStockSizes" :key="item.size_code" class="image-size-stock-cell">
                    <span class="image-size-label">{{ item.size_code || '-' }}</span>
                    <span class="image-size-qty">{{ formatNumber(item.stock_qty, 0) }}</span>
                  </div>
                </div>
                <div v-else-if="!detailLoading" class="image-empty-text">Beden stoku bulunamadi</div>
              </div>
            </aside>

            <section class="image-media-panel">
              <q-carousel
                v-if="imageDialogUrls.length"
                v-model="imageSlide"
                animated
                arrows
                navigation
                infinite
                swipeable
                class="product-image-carousel"
              >
                <q-carousel-slide
                  v-for="(url, idx) in imageDialogUrls"
                  :key="`${url}-${idx}`"
                  :name="idx"
                  class="column no-wrap flex-center"
                >
                  <q-img :src="url" fit="contain" class="product-image-large" @error="markDialogImageFailed(url)" />
                </q-carousel-slide>
              </q-carousel>
              <div v-else class="product-image-empty">
                <q-icon name="image_not_supported" size="48px" />
                <div class="text-subtitle2 q-mt-sm">Foto bulunamadi</div>
              </div>
            </section>

            <section class="image-sales-panel">
              <div class="row q-col-gutter-xs q-mb-sm">
                <div v-for="card in imageDialogPerformanceCards" :key="card.key" class="col-6 col-md-3 col-lg-4">
                  <q-card flat bordered class="metric-card image-metric-card">
                    <q-card-section>
                      <div class="text-caption text-grey-7">{{ card.label }}</div>
                      <div class="metric-value">{{ card.value }}</div>
                    </q-card-section>
                  </q-card>
                </div>
              </div>
              <q-table
                flat
                bordered
                dense
                class="dialog-detail-table"
                title="Satış / Müşteri Özeti"
                row-key="detail_key"
                :rows="detailSalesRows"
                :columns="detailSalesColumns"
                :loading="detailLoading"
                :pagination="{ rowsPerPage: 0, sortBy: 'sales_usd', descending: true }"
              />
            </section>
          </div>
        </q-card-section>
      </q-card>
    </q-dialog>

    <q-dialog v-model="performanceDialog" maximized>
      <q-card class="product-performance-dialog">
        <q-card-section class="row items-center q-pb-sm">
          <div>
            <div class="text-h6">{{ performanceDialogTitle }}</div>
            <div class="text-caption text-grey-7">{{ performanceDialogSubtitle }}</div>
          </div>
          <q-space />
          <q-btn v-close-popup flat round dense icon="close" />
        </q-card-section>
        <q-separator />
        <q-card-section class="q-pa-sm">
          <div class="row q-col-gutter-sm q-mb-sm">
            <div v-for="card in performanceCards" :key="card.key" class="col-6 col-md-2">
              <q-card flat bordered class="metric-card">
                <q-card-section>
                  <div class="text-caption text-grey-7">{{ card.label }}</div>
                  <div class="metric-value">{{ card.value }}</div>
                </q-card-section>
              </q-card>
            </div>
          </div>
          <div class="row q-col-gutter-sm">
            <div class="col-12 col-md-4">
              <q-table
                flat
                bordered
                dense
                class="dialog-stock-table"
                title="Beden Stok"
                row-key="size_code"
                :rows="detailStockSizes"
                :columns="detailStockColumns"
                :loading="detailLoading"
                :pagination="{ rowsPerPage: 0 }"
                virtual-scroll
                :virtual-scroll-item-size="48"
              />
            </div>
            <div class="col-12 col-md-8">
              <q-table
                flat
                bordered
                dense
                class="dialog-detail-table"
                title="Satış / Müşteri Özeti"
                row-key="detail_key"
                :rows="detailSalesRows"
                :columns="detailSalesColumns"
                :loading="detailLoading"
                :pagination="{ rowsPerPage: 0, sortBy: 'sales_usd', descending: true }"
                virtual-scroll
                :virtual-scroll-item-size="48"
              />
            </div>
          </div>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { Notify } from 'quasar'
import api, { extractApiErrorDetail } from 'src/services/api'
import { useProductPerformanceStore } from 'src/stores/productPerformanceStore'

const performanceStore = useProductPerformanceStore()

function formatNumber (value, fraction = 2) {
  const n = Number(value || 0)
  const requested = Number(fraction)
  const digits = Number.isFinite(requested) ? Math.max(0, Math.min(4, requested)) : 2
  return n.toLocaleString('tr-TR', {
    minimumFractionDigits: digits,
    maximumFractionDigits: digits
  })
}

function formatMoney (value, currency = 'USD') {
  return `${formatNumber(value, 2)} ${currency}`
}

function formatPercent (value) {
  return `${formatNumber(Number(value || 0) * 100, 2)}%`
}

const loading = ref(false)
const excelExportLoading = ref(false)
const rows = ref([])
const detailMainGroupOptionRows = ref([])
const generalRows = ref([])
const orderAnalysisRows = ref([])
const orderMarketRows = ref([])
const orderCustomerRows = ref([])
const orderProductCustomerRows = ref([])
const orderMarketDetailRows = ref([])
const orderAnalysisLoaded = ref(false)
const orderProductCustomersLoaded = ref(false)
const orderMarketDetailsLoaded = ref(false)
const marketRowsLoaded = ref(false)
const countryRowsLoaded = ref(false)
const customerRowsLoaded = ref(false)
const orderAnalysisMode = ref('product')
const marketRows = ref([])
const countryRows = ref([])
const customerRows = ref([])
const salesBreakdownRows = reactive({
  sales_color_yaka_market_customer: [],
  sales_product_country_segment_market_customer: [],
  sales_market_customer_product: [],
  sales_country_segment_market_customer_product: []
})
const summary = ref({})
const activeTab = ref('products')
const customerBreakdown = ref('market_customer')
const selectedPeriods = ref(['90', '180', '360', 'total'])
const selectedDetailMainGroup = ref('')
const imageUrlByKey = ref({})
const imageListByKey = ref({})
const imageFailedKeys = ref(new Set())
const imageLoadingKeys = ref(new Set())
const imageDialog = ref(false)
const imageDialogUrls = ref([])
const imageDialogTitle = ref('')
const imageDialogSubtitle = ref('')
const imageSlide = ref(0)
const imageDialogRow = ref(null)
const performanceDialog = ref(false)
const performanceDialogRow = ref(null)
const performanceDialogTitle = ref('')
const performanceDialogSubtitle = ref('')
const detailLoading = ref(false)
const detailSalesRows = ref([])
const detailStockSizes = ref([])
const backendGroupedRows = ref({})
const backendGroupedFilterOptions = ref({})
const backendGroupedLoading = ref(false)
const imagePrimeLoading = ref(false)
const filterBusy = ref(false)
const bulkExpandLoading = ref(false)
const loadingStartedAt = ref(0)
const loadingNow = ref(0)
const loadingStage = ref('')
const generalRowsLoaded = ref(false)
const detailLevelMenuOpen = ref(false)
const detailColumnsHidden = ref(false)
const topScrollbarRef = ref(null)
const topScrollbarInnerRef = ref(null)
let backendGroupedTimer = null
let imagePrimeActiveRequests = 0
let activeTableMiddleEl = null
let topScrollbarResizeObserver = null
let topScrollbarSyncing = false
let loadingTimer = null

const columnFilters = reactive({})
const columnFilterSearch = reactive({})
const expandedGroups = ref({})
const lastManualExpandedGroupKey = ref('')
const selectedExpandLevelKeysByTab = reactive({
  products: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
  product_detail: ['urun_alt_grubu'],
  sales_color_yaka_market_customer: ['urun_ana_grubu', 'color_yaka'],
  idle: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
  sales_product_country_segment_market_customer: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
  sales_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
  sales_country_segment_market_customer_product: ['country', 'customer_segment', 'market_key'],
  order_product_customers: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
  order_market_details: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan']
})
const productKpiFetchLimit = 50000
const reportFetchLimit = 50000
const maxBulkExpandKeys = 1000
const maxBackendExpandedKeys = 1000
const tableVirtualSliceSize = 160
const productThumbVirtualItemSize = 236
const maxRenderedGroupedRows = 2500
const maxAutoExpandLevelByTab = {
  products: 8,
  product_detail: 8,
  idle: 8,
  sales_color_yaka_market_customer: 8,
  sales_product_country_segment_market_customer: 8,
  sales_market_customer_product: 8,
  sales_country_segment_market_customer_product: 8,
  order_product_customers: 8,
  order_market_details: 8
}
const backendGroupedFilterFields = new Set([
  'kategori',
  'askili_yan',
  'urun_ilk_grubu',
  'urun_ana_grubu',
  'urun_alt_grubu',
  'product_code',
  'color_yaka',
  'market_key',
  'country',
  'customer_segment',
  'customer_code',
  'customer_name',
  'performance_bucket'
])
const productPerformanceExcelExportFilterFields = new Set([
  'kategori',
  'askili_yan',
  'urun_ilk_grubu',
  'urun_ana_grubu',
  'urun_alt_grubu',
  'product_code',
  'color_yaka',
  'color_code',
  'yaka_kodu',
  'market_key',
  'performance_bucket'
])
const performanceTabs = [
  { name: 'products', icon: 'dashboard', label: 'Genel Özet KPI' },
  { name: 'sales_color_yaka_market_customer', icon: 'palette', label: 'Ürün Ana Grubu > Renk/Yaka > Piyasa > Müşteri' },
  { name: 'idle', icon: 'warning', label: 'Atıl Stok / Maliyet' },
  { name: 'sales_product_country_segment_market_customer', icon: 'account_tree', label: 'Ürün > Ülke > Segment > Piyasa > Müşteri' },
  { name: 'sales_country_segment_market_customer_product', icon: 'public', label: 'Ülke > Segment > Piyasa > Müşteri > Ürün Satış KPI' },
  { name: 'order_product_customers', icon: 'assignment_ind', label: 'Ürün > Piyasa > Müşteri Sipariş' },
  { name: 'order_market_details', icon: 'receipt_long', label: 'Piyasa > Müşteri > Ürün Sipariş' }
]
const pageBusy = computed(() => loading.value)
const activeTableLoading = computed(() => loading.value || (backendGroupedSupportedTab(activeTab.value) && backendGroupedLoading.value))
const loadingElapsedLabel = computed(() => {
  if (!loadingStartedAt.value || !loadingNow.value) return ''
  const seconds = Math.max(0, Math.floor((loadingNow.value - loadingStartedAt.value) / 1000))
  const minutes = Math.floor(seconds / 60)
  const rest = seconds % 60
  return minutes > 0 ? `${minutes}:${String(rest).padStart(2, '0')}` : `${seconds} sn`
})
const loadingMessage = computed(() => loadingStage.value || 'Rapor verileri yükleniyor...')

const tablePagination = reactive({
  general: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  products: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  product_detail: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  order_product_customers: { page: 1, rowsPerPage: 0, sortBy: 'order_usd', descending: true },
  order_market_details: { page: 1, rowsPerPage: 0, sortBy: 'order_date', descending: true },
  idle: { page: 1, rowsPerPage: 0, sortBy: 'idle_cost_usd', descending: true },
  markets: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_90d', descending: true },
  countries: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_90d', descending: true },
  customers: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_365d', descending: true },
  sales_color_yaka_market_customer: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  sales_product_country_segment_market_customer: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  sales_market_customer_product: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
  sales_country_segment_market_customer_product: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }
})

const activeProductPagination = computed({
  get: () => tablePagination[activeTab.value === 'product_detail' ? 'product_detail' : 'products'],
  set: value => {
    tablePagination[activeTab.value === 'product_detail' ? 'product_detail' : 'products'] = normalizeTablePagination(value)
  }
})

const activeSalesBreakdownPagination = computed({
  get: () => tablePagination[activeTab.value] || tablePagination.sales_color_yaka_market_customer,
  set: value => {
    tablePagination[activeTab.value] = normalizeTablePagination(value)
  }
})

const activeBackendGroupedSortSignature = computed(() => {
  if (!backendGroupedSupportedTab(activeTab.value)) return ''
  const state = tablePagination[activeTab.value] || {}
  return `${activeTab.value}|${state.sortBy || ''}|${state.descending !== false ? 1 : 0}`
})

function normalizeTablePagination (value) {
  return {
    page: Number(value?.page || 1),
    rowsPerPage: 0,
    sortBy: value?.sortBy || '',
    descending: value?.descending === true
  }
}

const bucketOptions = [
  { label: 'Yıldız Ürün', value: 'YILDIZ_URUN' },
  { label: 'Stoksuz Talep', value: 'STOKSUZ_TALEP' },
  { label: 'Stok Riski', value: 'STOK_RISKI' },
  { label: 'Fiyat Baskısı', value: 'FIYAT_BASKISI' },
  { label: 'Fiyat Fırsatı', value: 'FIYAT_FIRSATI' },
  { label: 'Maliyet Yok', value: 'MALIYET_YOK' },
  { label: 'Takip', value: 'TAKIP' }
]

const periodOptions = [
  { label: '90G', value: '90' },
  { label: '180G', value: '180' },
  { label: '360G', value: '360' },
  { label: 'Genel', value: 'total' }
]

const customerBreakdownOptions = [
  { label: 'Piyasa > Müşteri', value: 'market_customer' },
  { label: 'Ülke > Müşteri', value: 'country_customer' },
  { label: 'Piyasa > Ülke > Müşteri', value: 'market_country_customer' }
]

const salesBreakdownModes = {
  sales_color_yaka_market_customer: 'color_yaka_market_customer',
  sales_product_country_segment_market_customer: 'product_country_segment_market_customer',
  sales_country_segment_market_customer_product: 'country_segment_market_customer_product'
}

const salesBreakdownTabKeys = Object.keys(salesBreakdownModes)

const detailStockColumns = [
  { name: 'size_code', label: 'Beden', field: 'size_code', align: 'left', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true }
]

const detailSalesColumns = [
  { name: 'sales_date', label: 'Tarih', field: 'sales_date', align: 'left', sortable: true },
  { name: 'ref_number', label: 'Ref', field: 'ref_number', align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'country', label: 'Ülke', field: 'country', align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'sales_qty', label: 'Adet', field: row => formatNumber(row.sales_qty, 2), align: 'right', sortable: true },
  { name: 'sales_usd', label: 'USD', field: row => formatMoney(row.sales_usd, 'USD'), align: 'right', sortable: true },
  { name: 'avg_price_usd', label: 'Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd, 'USD'), align: 'right', sortable: true }
]

const columns = [
  { name: 'image', label: 'Foto', field: 'image', align: 'center' },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left' },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left' },
  { name: 'kategori', label: 'Kategori', field: row => productCategoryValue(row), align: 'left' },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left' },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left' },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left' },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left' },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left' },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'performance_score_90d', label: '90G Skor', field: row => formatNumber(row.performance_score_90d, 2), align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'performance_score_180d', label: '180G Skor', field: row => formatNumber(row.performance_score_180d, 2), align: 'right', sortable: true },
  { name: 'sales_qty_180d', label: '180G Satış', field: row => formatNumber(row.sales_qty_180d, 0), align: 'right', sortable: true },
  { name: 'performance_score_365d', label: '360G Skor', field: row => formatNumber(row.performance_score_365d, 2), align: 'right', sortable: true },
  { name: 'sales_qty_365d', label: '360G Satış', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
  { name: 'performance_score_total', label: 'Genel Skor', field: row => formatNumber(row.performance_score_total, 2), align: 'right', sortable: true },
  { name: 'sales_qty_total', label: 'Genel Satış', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
  { name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
  { name: 'sales_usd_180d', label: '180G USD', field: 'sales_usd_180d', align: 'right', sortable: true },
  { name: 'sales_usd_365d', label: '360G USD', field: 'sales_usd_365d', align: 'right', sortable: true },
  { name: 'sales_usd_total', label: 'Genel USD', field: 'sales_usd_total', align: 'right', sortable: true },
  { name: 'stock_days_90d', label: 'Stok Gün', field: row => formatNumber(row.stock_days_90d, 1), align: 'right', sortable: true },
  { name: 'stock_days_180d', label: '180G Stok Gün', field: row => formatNumber(row.stock_days_180d, 1), align: 'right', sortable: true },
  { name: 'stock_days_365d', label: '360G Stok Gün', field: row => formatNumber(row.stock_days_365d, 1), align: 'right', sortable: true },
  { name: 'stock_days_total', label: 'Genel Stok Gün', field: row => formatNumber(row.stock_days_total, 1), align: 'right', sortable: true },
  { name: 'stock_turnover_90d', label: '90G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
  { name: 'stock_turnover_180d', label: '180G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
  { name: 'stock_turnover_365d', label: '360G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
  { name: 'stock_turnover_total', label: 'Genel Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
  { name: 'avg_price_usd_90d', label: '90G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right' },
  { name: 'avg_price_usd_180d', label: '180G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right' },
  { name: 'avg_price_usd_365d', label: '360G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right' },
  { name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right' },
  { name: 'base_price_usd', label: 'USD Taban Maliyeti', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd', label: 'USD Çıplak Maliyeti', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_base_90d', label: '90G P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_90d, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_cost_90d', label: '90G P.Başı Çıplak K/Z', field: row => formatMoney(row.unit_profit_cost_90d, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_base_180d', label: '180G P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_180d, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_cost_180d', label: '180G P.Başı Çıplak K/Z', field: row => formatMoney(row.unit_profit_cost_180d, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_base_total', label: 'Genel P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_total, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_cost_total', label: 'Genel P.Başı Çıplak K/Z', field: row => formatMoney(row.unit_profit_cost_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_usd_90d', label: '90G Toplam K/Z', field: row => formatMoney(row.gross_profit_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_usd_180d', label: '180G Toplam K/Z', field: row => formatMoney(row.gross_profit_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_usd_total', label: 'Genel Toplam K/Z', field: row => formatMoney(row.gross_profit_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_total', label: 'Genel Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_total', label: 'Genel Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_90d', label: '90G Taban Marj', field: row => formatPercent(row.gross_margin_base_90d), align: 'right', sortable: true },
  { name: 'gross_margin_cost_90d', label: '90G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_90d), align: 'right', sortable: true },
  { name: 'gross_margin_base_180d', label: '180G Taban Marj', field: row => formatPercent(row.gross_margin_base_180d), align: 'right', sortable: true },
  { name: 'gross_margin_cost_180d', label: '180G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_180d), align: 'right', sortable: true },
  { name: 'gross_margin_base_total', label: 'Genel Taban Marj', field: row => formatPercent(row.gross_margin_base_total), align: 'right', sortable: true },
  { name: 'gross_margin_cost_total', label: 'Genel Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_total), align: 'right', sortable: true },
  { name: 'market_count_90d', label: '90G Piyasa', field: row => formatNumber(row.market_count_90d, 0), align: 'right', sortable: true },
  { name: 'customer_count_90d', label: '90G Müşteri', field: row => formatNumber(row.customer_count_90d, 0), align: 'right', sortable: true },
  { name: 'market_count_total', label: 'Genel Piyasa', field: row => formatNumber(row.market_count_total, 0), align: 'right', sortable: true },
  { name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
  { name: 'sales_index_90d', label: 'Piyasa End.', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
  { name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left' },
  { name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
]

const periodColumnRules = [
  { period: '90', match: name => name.includes('_90d') },
  { period: '180', match: name => name.includes('_180d') },
  { period: '360', match: name => name.includes('_365d') },
  { period: 'total', match: name => name.includes('_total') }
]

const periodOrder = {
  90: 1,
  180: 2,
  360: 3,
  total: 4
}

const hiddenTableColumns = new Set([
  'item_description',
  'performance_score',
  'gross_profit_usd_total',
  'invoice_count_90d',
  'invoice_count_180d',
  'invoice_count_365d',
  'invoice_count_total',
  'sales_index_90d',
  'sales_index_180d',
  'sales_index_365d',
  'sales_index_total',
  'stock_days_90d',
  'stock_days_180d',
  'stock_days_365d',
  'stock_days_total'
])
const dimensionColumnNames = [
  'product_code',
  'color_yaka',
  'color_code',
  'yaka_kodu',
  'item_description',
  'kategori',
  'askili_yan',
  'urun_ilk_grubu',
  'urun_ana_grubu',
  'urun_alt_grubu',
  'market_key',
  'country',
  'customer_segment',
  'customer_code',
  'customer_name'
]

const productHierarchyColumnOrder = [
  'urun_ilk_grubu',
  'askili_yan',
  'kategori',
  'urun_ana_grubu',
  'urun_alt_grubu'
]

const metricOrderFragments = [
  'customer_score',
  'performance_score',
  'product_count',
  'market_count',
  'customer_count',
  'invoice_count',
  'order_count',
  'line_count',
  'sales_qty',
  'order_qty',
  'sales_usd',
  'order_usd',
  'avg_price',
  'avg_order_price',
  'sales_index',
  'avg_daily_sales',
  'stock_days',
  'stock_turnover',
  'base_price',
  'cost_price',
  'base_cost',
  'cost_amount',
  'unit_profit_base',
  'unit_profit_cost',
  'gross_profit_usd',
  'gross_profit_base',
  'gross_profit_cost',
  'expected_profit_base',
  'expected_profit_cost',
  'gross_margin_base',
  'gross_margin_cost',
  'expected_margin_base',
  'expected_margin_cost'
]

const selectedPeriodSet = computed(() => new Set(selectedPeriods.value))

function columnPeriod (name) {
  return periodColumnRules.find(rule => rule.match(name))?.period || ''
}

function visiblePeriodColumn (col) {
  const period = columnPeriod(col.name)
  return !period || selectedPeriodSet.value.has(period)
}

function metricBaseName (name) {
  return String(name || '')
    .replace(/_90d$|_180d$|_365d$|_total$/g, '')
}

function metricOrder (name) {
  const base = metricBaseName(name)
  const idx = metricOrderFragments.findIndex(fragment => base.includes(fragment))
  return idx >= 0 ? idx : 999
}

function isTrailingInfoColumn (name) {
  return ['performance_bucket', 'recommendation', 'last_sale_date', 'first_sale_date', 'is_overdue'].includes(name)
}

function nonPeriodColumnOrder (name) {
  if (name === 'performance_score') return 2
  if (name === 'stock_qty' || name === 'product_count') return 1
  if (dimensionColumnNames.includes(name) || name === 'image') return 0
  return 3
}

function orderedMetricColumns (sourceColumns) {
  const sorted = sourceColumns.filter(col => !hiddenTableColumns.has(col.name)).sort((a, b) => {
    const aPeriod = columnPeriod(a.name)
    const bPeriod = columnPeriod(b.name)
    const aTrailing = isTrailingInfoColumn(a.name)
    const bTrailing = isTrailingInfoColumn(b.name)
    if (aTrailing !== bTrailing) return aTrailing ? 1 : -1
    if (!aPeriod && !bPeriod) {
      return nonPeriodColumnOrder(a.name) - nonPeriodColumnOrder(b.name)
    }
    if (!aPeriod) return -1
    if (!bPeriod) return 1
    const periodDiff = periodOrder[aPeriod] - periodOrder[bPeriod]
    if (periodDiff) return periodDiff
    const metricDiff = metricOrder(a.name) - metricOrder(b.name)
    return metricDiff || 0
  })
  return normalizeProductHierarchyColumnOrder(sorted)
}

function normalizeProductHierarchyColumnOrder (sourceColumns) {
  const positions = sourceColumns
    .map((col, index) => productHierarchyColumnOrder.includes(col.name) ? index : -1)
    .filter(index => index >= 0)
  if (positions.length < 2) return sourceColumns
  const ordered = positions
    .map(index => sourceColumns[index])
    .sort((a, b) => productHierarchyColumnOrder.indexOf(a.name) - productHierarchyColumnOrder.indexOf(b.name))
  const next = [...sourceColumns]
  positions.forEach((position, index) => {
    next[position] = ordered[index]
  })
  return next
}

function reorderColumnsInPlace (targetColumns) {
  targetColumns.splice(0, targetColumns.length, ...orderedMetricColumns(targetColumns))
  return targetColumns
}

const productColumns = computed(() => orderedMetricColumns(columns.filter(visiblePeriodColumn)))
const productDetailColumns = computed(() => orderedMetricColumns(columns
  .filter(col => !['urun_ilk_grubu', 'askili_yan', 'kategori'].includes(col.name))
  .filter(visiblePeriodColumn)))
const detailColumnNames = new Set([
  'item_description',
  'kategori',
  'askili_yan',
  'urun_ilk_grubu',
  'urun_ana_grubu',
  'urun_alt_grubu',
  'period_start',
  'period_end',
  'country',
  'customer_segment',
  'customer_code',
  'customer_name',
  'first_sale_date',
  'last_sale_date',
  'last_ref_number'
])

function applyDetailColumnVisibility (sourceColumns) {
  if (!detailColumnsHidden.value) return sourceColumns
  return sourceColumns.filter(col => !detailColumnNames.has(col.name))
}

const visibleProductColumns = computed(() => applyDetailColumnVisibility(productColumns.value))
const visibleProductDetailColumns = computed(() => applyDetailColumnVisibility(productDetailColumns.value))
const activeProductColumns = computed(() => activeTab.value === 'product_detail' ? visibleProductDetailColumns.value : visibleProductColumns.value)

const generalColumns = [
  { name: 'image', label: 'Foto', field: 'image', align: 'center' },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left', sortable: true },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left', sortable: true },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left', sortable: true },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left', sortable: true },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'period_start', label: 'Başlangıç', field: 'period_start', align: 'left', sortable: true },
  { name: 'period_end', label: 'Bitiş', field: 'period_end', align: 'left', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'sales_qty_total', label: 'Toplam Adet', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
  { name: 'sales_usd_total', label: 'Toplam Ciro USD', field: row => formatMoney(row.sales_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'avg_daily_sales_total', label: 'Günlük Ort.', field: row => formatNumber(row.avg_daily_sales_total, 3), align: 'right', sortable: true },
  { name: 'stock_days_total', label: 'Genel Stok Gün', field: row => formatNumber(row.stock_days_total, 1), align: 'right', sortable: true },
  { name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'base_price_usd', label: 'USD Taban Maliyeti', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd', label: 'USD Çıplak Maliyeti', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_base_total', label: 'P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_total, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_cost_total', label: 'P.Başı Çıplak K/Z', field: row => formatMoney(row.unit_profit_cost_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_usd_total', label: 'Toplam K/Z', field: row => formatMoney(row.gross_profit_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_total', label: 'Taban Toplam K/Z', field: row => formatMoney(row.gross_profit_base_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_total', label: 'Çıplak Toplam K/Z', field: row => formatMoney(row.gross_profit_cost_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_total', label: 'Genel Taban Marj', field: row => formatPercent(row.gross_margin_base_total), align: 'right', sortable: true },
  { name: 'gross_margin_cost_total', label: 'Genel Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_total), align: 'right', sortable: true },
  { name: 'market_count_total', label: 'Toplam Piyasa', field: row => formatNumber(row.market_count_total, 0), align: 'right', sortable: true },
  { name: 'customer_count_total', label: 'Toplam Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
  { name: 'invoice_count_total', label: 'Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
  { name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
  { name: 'performance_score_total', label: 'Genel Skor', field: row => formatNumber(row.performance_score_total ?? row.performance_score, 2), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'first_sale_date', label: 'İlk Satış', field: 'first_sale_date', align: 'left', sortable: true },
  { name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
]

const visibleGeneralColumns = computed(() => applyDetailColumnVisibility(generalColumns))

const orderAnalysisColumns = [
  { name: 'image', label: 'Foto', field: 'image', align: 'center' },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left', sortable: true },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left', sortable: true },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left', sortable: true },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left', sortable: true },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'order_qty', label: 'Açık Sipariş', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
  { name: 'order_usd', label: 'Sipariş USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
  { name: 'avg_order_price_usd', label: 'Ort. Sipariş USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
  { name: 'base_price_usd', label: 'USD Taban Maliyeti', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd', label: 'USD Çıplak Maliyeti', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_base_usd', label: 'P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_usd, 'USD'), align: 'right', sortable: true },
  { name: 'unit_profit_cost_usd', label: 'P.Başı Çıplak K/Z', field: row => formatMoney(row.unit_profit_cost_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_base_usd', label: 'Taban Toplam K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_cost_usd', label: 'Çıplak Toplam K/Z', field: 'expected_profit_cost_usd', align: 'right', sortable: true },
  { name: 'expected_margin_cost', label: 'Beklenen Marj', field: row => formatPercent(row.expected_margin_cost), align: 'right', sortable: true },
  { name: 'market_count', label: 'Piyasa', field: row => formatNumber(row.market_count, 0), align: 'right', sortable: true },
  { name: 'customer_count', label: 'Müşteri', field: row => formatNumber(row.customer_count, 0), align: 'right', sortable: true },
  { name: 'order_count', label: 'Sipariş', field: row => formatNumber(row.order_count, 0), align: 'right', sortable: true },
  { name: 'line_count', label: 'Satır', field: row => formatNumber(row.line_count, 0), align: 'right', sortable: true },
  { name: 'earliest_due_date', label: 'İlk Termin', field: 'earliest_due_date', align: 'left', sortable: true },
  { name: 'overdue_qty', label: 'Geciken Adet', field: row => formatNumber(row.overdue_qty, 0), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Yorum', field: 'recommendation', align: 'left' }
]

const orderGroupColumns = [
  { name: 'group_key', label: 'Kırılım', field: row => orderGroupLabel(row), align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
  { name: 'order_qty', label: 'Açık Sipariş', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
  { name: 'order_usd', label: 'Sipariş USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
  { name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'base_cost_usd', label: 'Taban Maliyet USD', field: row => formatMoney(row.base_cost_usd, 'USD'), align: 'right', sortable: true },
  { name: 'cost_amount_usd', label: 'Çıplak Maliyet USD', field: row => formatMoney(row.cost_amount_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_base_usd', label: 'Brüt K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_cost_usd', label: 'Yakl. Net K/Z', field: 'expected_profit_cost_usd', align: 'right', sortable: true },
  { name: 'expected_margin_base', label: 'Brüt Marj', field: row => formatPercent(row.expected_margin_base), align: 'right', sortable: true },
  { name: 'expected_margin_cost', label: 'Yakl. Net Marj', field: row => formatPercent(row.expected_margin_cost), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
  { name: 'market_count', label: 'Piyasa', field: row => formatNumber(row.market_count, 0), align: 'right', sortable: true },
  { name: 'customer_count', label: 'Müşteri', field: row => formatNumber(row.customer_count, 0), align: 'right', sortable: true },
  { name: 'order_count', label: 'Sipariş', field: row => formatNumber(row.order_count, 0), align: 'right', sortable: true },
  { name: 'line_count', label: 'Satır', field: row => formatNumber(row.line_count, 0), align: 'right', sortable: true },
  { name: 'overdue_qty', label: 'Geciken Adet', field: row => formatNumber(row.overdue_qty, 0), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Yorum', field: 'recommendation', align: 'left' }
]

const orderProductCustomerColumns = [
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'image', label: 'Foto', field: 'image', align: 'center' },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left', sortable: true },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left', sortable: true },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left', sortable: true },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left', sortable: true },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left', sortable: true },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
  { name: 'order_qty', label: 'Açık Sipariş', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
  { name: 'order_usd', label: 'Sipariş USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
  { name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
  { name: 'base_price_usd', label: 'Taban USD', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd', label: 'Çıplak USD', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_base_usd', label: 'Brüt K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_cost_usd', label: 'Yakl. Net K/Z', field: 'expected_profit_cost_usd', align: 'right', sortable: true },
  { name: 'expected_margin_base', label: 'Brüt Marj', field: row => formatPercent(row.expected_margin_base), align: 'right', sortable: true },
  { name: 'expected_margin_cost', label: 'Yakl. Net Marj', field: row => formatPercent(row.expected_margin_cost), align: 'right', sortable: true },
  { name: 'order_count', label: 'Sipariş', field: row => formatNumber(row.order_count, 0), align: 'right', sortable: true },
  { name: 'line_count', label: 'Satır', field: row => formatNumber(row.line_count, 0), align: 'right', sortable: true },
  { name: 'overdue_qty', label: 'Geciken Adet', field: row => formatNumber(row.overdue_qty, 0), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Yorum', field: 'recommendation', align: 'left' }
]

const orderMarketDetailColumns = [
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left', sortable: true },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left', sortable: true },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left', sortable: true },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left', sortable: true },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left', sortable: true },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
  { name: 'order_number', label: 'Sipariş No', field: 'order_number', align: 'left', sortable: true },
  { name: 'order_date', label: 'Sipariş Tarihi', field: 'order_date', align: 'left', sortable: true },
  { name: 'due_date', label: 'Termin', field: 'due_date', align: 'left', sortable: true },
  { name: 'order_qty', label: 'Adet', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
  { name: 'order_usd', label: 'USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
  { name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
  { name: 'expected_profit_base_usd', label: 'Brüt K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
  { name: 'expected_profit_cost_usd', label: 'Yakl. Net K/Z', field: 'expected_profit_cost_usd', align: 'right', sortable: true },
  { name: 'expected_margin_base', label: 'Brüt Marj', field: row => formatPercent(row.expected_margin_base), align: 'right', sortable: true },
  { name: 'expected_margin_cost', label: 'Yakl. Net Marj', field: row => formatPercent(row.expected_margin_cost), align: 'right', sortable: true },
  { name: 'is_overdue', label: 'Gecikti', field: row => row.is_overdue ? 'Evet' : 'Hayır', align: 'left', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Yorum', field: 'recommendation', align: 'left' }
]

const idleColumns = [
  { name: 'image', label: 'Foto', field: 'image', align: 'center' },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left' },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left' },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left' },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left' },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left' },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left' },
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left' },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left' },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left' },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'cost_price_usd', label: 'Maliyet USD', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
  { name: 'idle_cost_usd', label: 'Stok Maliyeti USD', field: 'idle_cost_usd', align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'stock_days_90d', label: 'Stok Gün', field: row => formatNumber(row.stock_days_90d, 1), align: 'right', sortable: true },
  { name: 'stock_turnover_90d', label: '90G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left' },
  { name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
]

const marketColumns = [
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left' },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left' },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left' },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left' },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left' },
  { name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
  { name: 'star_count', label: 'Yıldız', field: row => formatNumber(row.star_count, 0), align: 'right', sortable: true },
  { name: 'stock_risk_count', label: 'Risk', field: row => formatNumber(row.stock_risk_count, 0), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'stock_cost_value_usd', label: 'Stok Maliyeti', field: 'stock_cost_value_usd', align: 'right', sortable: true },
  { name: 'risk_stock_cost_value_usd', label: 'Risk Maliyeti', field: 'risk_stock_cost_value_usd', align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_90d', label: '90G USD', field: row => formatMoney(row.sales_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'avg_gross_margin_90d', label: 'Ort. Marj', field: 'avg_gross_margin_90d', align: 'right', sortable: true },
  { name: 'avg_stock_days_90d', label: 'Ort. Stok Gün', field: row => formatNumber(row.avg_stock_days_90d, 1), align: 'right', sortable: true }
]

const countryColumns = [
  { name: 'country', label: 'Ülke', field: 'country', align: 'left', sortable: true },
  { name: 'customer_segment', label: 'Müşteri Segmenti', field: 'customer_segment', align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left' },
  { name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Adet', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_90d', label: '90G USD', field: row => formatMoney(row.sales_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'avg_price_usd_90d', label: 'Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'customer_count_90d', label: 'Müşteri', field: row => formatNumber(row.customer_count_90d, 0), align: 'right', sortable: true },
  { name: 'invoice_count_90d', label: 'Fatura', field: row => formatNumber(row.invoice_count_90d, 0), align: 'right', sortable: true },
  { name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_365d', label: '360G USD', field: row => formatMoney(row.sales_usd_365d, 'USD'), align: 'right', sortable: true }
]

const customerColumns = [
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'country', label: 'Ülke', field: 'country', align: 'left', sortable: true },
  { name: 'customer_segment', label: 'Segment', field: 'customer_segment', align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Adet', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
  { name: 'avg_price_usd_90d', label: 'Ort. Satış Fiyatı USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
  { name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 2), align: 'right', sortable: true },
  { name: 'base_price_usd_90d', label: '90G Taban', field: row => formatMoney(row.base_price_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd_90d', label: '90G Çıplak', field: row => formatMoney(row.cost_price_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_90d', label: '90G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_90d', label: '90G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_90d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_90d', label: '90G Taban Marj', field: row => formatPercent(row.gross_margin_base_90d), align: 'right', sortable: true },
  { name: 'gross_margin_cost_90d', label: '90G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_90d), align: 'right', sortable: true },
  { name: 'invoice_count_90d', label: 'Fatura', field: row => formatNumber(row.invoice_count_90d, 0), align: 'right', sortable: true },
  { name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_365d', label: '360G USD', field: 'sales_usd_365d', align: 'right', sortable: true },
  { name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true }
]

const salesBreakdownColumns = [
  { name: 'color_code', label: 'Renk', field: 'color_code', align: 'left', sortable: true },
  { name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
  { name: 'askili_yan', label: 'Askılı/Yan', field: 'askili_yan', align: 'left', sortable: true },
  { name: 'urun_ilk_grubu', label: 'Ürün İlk Grubu', field: row => productFirstGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_ana_grubu', label: 'Ürün Ana Grubu', field: row => productMainGroupValue(row), align: 'left', sortable: true },
  { name: 'urun_alt_grubu', label: 'Ürün Alt Grubu', field: 'urun_alt_grubu', align: 'left', sortable: true },
  { name: 'product_code', label: 'Ürün', field: 'product_code', align: 'left', sortable: true },
  { name: 'item_description', label: 'Açıklama', field: 'item_description', align: 'left', sortable: true },
  { name: 'country', label: 'Ülke', field: 'country', align: 'left', sortable: true },
  { name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
  { name: 'customer_segment', label: 'Segment', field: 'customer_segment', align: 'left', sortable: true },
  { name: 'customer_code', label: 'Müşteri Kodu', field: 'customer_code', align: 'left', sortable: true },
  { name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
  { name: 'kategori', label: 'Kategori', field: 'kategori', align: 'left', sortable: true },
  { name: 'product_count', label: 'Ürün Sayısı', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
  { name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
  { name: 'customer_count_90d', label: '90G Müşteri', field: row => formatNumber(row.customer_count_90d, 0), align: 'right', sortable: true },
  { name: 'invoice_count_90d', label: '90G Fatura', field: row => formatNumber(row.invoice_count_90d, 0), align: 'right', sortable: true },
  { name: 'sales_qty_90d', label: '90G Adet', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
  { name: 'avg_price_usd_90d', label: '90G Ort. Satış Fiyatı USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
  { name: 'stock_turnover_90d', label: '90G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
  { name: 'customer_count_180d', label: '180G Müşteri', field: row => formatNumber(row.customer_count_180d, 0), align: 'right', sortable: true },
  { name: 'invoice_count_180d', label: '180G Fatura', field: row => formatNumber(row.invoice_count_180d, 0), align: 'right', sortable: true },
  { name: 'sales_qty_180d', label: '180G Adet', field: row => formatNumber(row.sales_qty_180d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_180d', label: '180G USD', field: row => formatMoney(row.sales_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'avg_price_usd_180d', label: '180G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'stock_turnover_180d', label: '180G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
  { name: 'base_price_usd_180d', label: '180G Taban', field: row => formatMoney(row.base_price_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd_180d', label: '180G Çıplak', field: row => formatMoney(row.cost_price_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_180d', label: '180G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_180d', label: '180G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_180d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_180d', label: '180G Taban Marj', field: row => formatPercent(row.gross_margin_base_180d), align: 'right', sortable: true },
  { name: 'gross_margin_cost_180d', label: '180G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_180d), align: 'right', sortable: true },
  { name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 2), align: 'right', sortable: true },
  { name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
  { name: 'sales_usd_365d', label: '360G USD', field: row => formatMoney(row.sales_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'avg_price_usd_365d', label: '360G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'stock_turnover_365d', label: '360G Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
  { name: 'base_price_usd_365d', label: '360G Taban', field: row => formatMoney(row.base_price_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd_365d', label: '360G Çıplak', field: row => formatMoney(row.cost_price_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_365d', label: '360G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_365d', label: '360G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_365d, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_365d', label: '360G Taban Marj', field: row => formatPercent(row.gross_margin_base_365d), align: 'right', sortable: true },
  { name: 'gross_margin_cost_365d', label: '360G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_365d), align: 'right', sortable: true },
  { name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 2), align: 'right', sortable: true },
  { name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
  { name: 'invoice_count_total', label: 'Genel Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
  { name: 'sales_qty_total', label: 'Genel Adet', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
  { name: 'sales_usd_total', label: 'Genel USD', field: row => formatMoney(row.sales_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'stock_turnover_total', label: 'Genel Yıllık Stok Devir', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
  { name: 'base_price_usd_total', label: 'Genel Taban', field: row => formatMoney(row.base_price_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'cost_price_usd_total', label: 'Genel Çıplak', field: row => formatMoney(row.cost_price_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_base_usd_total', label: 'Genel Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_profit_cost_usd_total', label: 'Genel Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_total, 'USD'), align: 'right', sortable: true },
  { name: 'gross_margin_base_total', label: 'Genel Taban Marj', field: row => formatPercent(row.gross_margin_base_total), align: 'right', sortable: true },
  { name: 'gross_margin_cost_total', label: 'Genel Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_total), align: 'right', sortable: true },
  { name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 2), align: 'right', sortable: true },
  { name: 'sales_index_90d', label: 'Endeks', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
  { name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
  { name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
  { name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
]

const productPeriodScoreColumns = [
  { name: 'performance_score_90d', label: '90G Ürün Skor', field: row => formatNumber(row.performance_score_90d, 2), align: 'right', sortable: true },
  { name: 'performance_score_180d', label: '180G Ürün Skor', field: row => formatNumber(row.performance_score_180d, 2), align: 'right', sortable: true },
  { name: 'performance_score_365d', label: '360G Ürün Skor', field: row => formatNumber(row.performance_score_365d, 2), align: 'right', sortable: true },
  { name: 'performance_score_total', label: 'Genel Ürün Skor', field: row => formatNumber(row.performance_score_total, 2), align: 'right', sortable: true }
]

const customerPeriodScoreColumns = [
  { name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 2), align: 'right', sortable: true },
  { name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 2), align: 'right', sortable: true },
  { name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 2), align: 'right', sortable: true },
  { name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 2), align: 'right', sortable: true }
]

const metricLabelOverrides = {
  stock_qty: 'Toplam Stok',
  sales_qty: 'Toplam Adet',
  sales_qty_90d: '90G Toplam Adet',
  sales_qty_180d: '180G Toplam Adet',
  sales_qty_365d: '360G Toplam Adet',
  sales_qty_total: 'Genel Toplam Adet',
  sales_usd: 'Toplam Ciro USD',
  sales_usd_90d: '90G Toplam Ciro USD',
  sales_usd_180d: '180G Toplam Ciro USD',
  sales_usd_365d: '360G Toplam Ciro USD',
  sales_usd_total: 'Genel Toplam Ciro USD',
  avg_price_usd: 'Ort. Satış Fiyatı USD',
  avg_price_usd_90d: '90G Ort. Satış Fiyatı USD',
  avg_price_usd_180d: '180G Ort. Satış Fiyatı USD',
  avg_price_usd_365d: '360G Ort. Satış Fiyatı USD',
  avg_price_usd_total: 'Genel Ort. Satış Fiyatı USD',
  base_price_usd: 'Taban Maliyet USD',
  cost_price_usd: 'Çıplak Maliyet USD',
  base_price_usd_90d: '90G Taban Maliyet USD',
  cost_price_usd_90d: '90G Çıplak Maliyet USD',
  base_price_usd_180d: '180G Taban Maliyet USD',
  cost_price_usd_180d: '180G Çıplak Maliyet USD',
  base_price_usd_365d: '360G Taban Maliyet USD',
  cost_price_usd_365d: '360G Çıplak Maliyet USD',
  base_price_usd_total: 'Genel Taban Maliyet USD',
  cost_price_usd_total: 'Genel Çıplak Maliyet USD',
  unit_profit_base_90d: '90G Birim Taban K/Z USD',
  unit_profit_cost_90d: '90G Birim Çıplak K/Z USD',
  unit_profit_base_180d: '180G Birim Taban K/Z USD',
  unit_profit_cost_180d: '180G Birim Çıplak K/Z USD',
  unit_profit_base_total: 'Genel Birim Taban K/Z USD',
  unit_profit_cost_total: 'Genel Birim Çıplak K/Z USD',
  gross_profit_usd_90d: '90G Toplam K/Z USD',
  gross_profit_usd_180d: '180G Toplam K/Z USD',
  gross_profit_usd_total: 'Genel Toplam K/Z USD',
  gross_profit_base_usd_90d: '90G Taban Toplam K/Z USD',
  gross_profit_cost_usd_90d: '90G Çıplak Toplam K/Z USD',
  gross_profit_base_usd_180d: '180G Taban Toplam K/Z USD',
  gross_profit_cost_usd_180d: '180G Çıplak Toplam K/Z USD',
  gross_profit_base_usd_365d: '360G Taban Toplam K/Z USD',
  gross_profit_cost_usd_365d: '360G Çıplak Toplam K/Z USD',
  gross_profit_base_usd_total: 'Genel Taban Toplam K/Z USD',
  gross_profit_cost_usd_total: 'Genel Çıplak Toplam K/Z USD',
  gross_margin_base_90d: '90G Taban Marj',
  gross_margin_cost_90d: '90G Çıplak Marj',
  gross_margin_base_180d: '180G Taban Marj',
  gross_margin_cost_180d: '180G Çıplak Marj',
  gross_margin_base_365d: '360G Taban Marj',
  gross_margin_cost_365d: '360G Çıplak Marj',
  gross_margin_base_total: 'Genel Taban Marj',
  gross_margin_cost_total: 'Genel Çıplak Marj',
  performance_score_90d: '90G Ürün Skor',
  performance_score_180d: '180G Ürün Skor',
  performance_score_365d: '360G Ürün Skor',
  performance_score_total: 'Genel Ürün Skor',
  customer_score_90d: '90G Müşteri Skor',
  customer_score_180d: '180G Müşteri Skor',
  customer_score_365d: '360G Müşteri Skor',
  customer_score_total: 'Genel Müşteri Skor',
  market_count_90d: '90G Tekil Piyasa',
  customer_count_90d: '90G Tekil Müşteri',
  market_count_total: 'Genel Tekil Piyasa',
  customer_count_total: 'Genel Tekil Müşteri',
  stock_turnover_90d: '90G Yıllık Stok Devir',
  stock_turnover_180d: '180G Yıllık Stok Devir',
  stock_turnover_365d: '360G Yıllık Stok Devir',
  stock_turnover_total: 'Genel Yıllık Stok Devir',
  sales_index_90d: '90G Endeks',
  sales_index_total: 'Genel Endeks',
  stock_days_90d: '90G Stok Gün',
  stock_days_180d: '180G Stok Gün',
  stock_days_365d: '360G Stok Gün',
  stock_days_total: 'Genel Stok Gün',
  avg_daily_sales_total: 'Genel Günlük Ort. Adet'
}

function applyMetricLabelOverrides (...columnGroups) {
  for (const group of columnGroups) {
    for (const col of group || []) {
      if (metricLabelOverrides[col?.name]) col.label = metricLabelOverrides[col.name]
    }
  }
}

applyMetricLabelOverrides(
  detailSalesColumns,
  columns,
  generalColumns,
  orderAnalysisColumns,
  orderGroupColumns,
  orderProductCustomerColumns,
  orderMarketDetailColumns,
  idleColumns,
  marketColumns,
  countryColumns,
  customerColumns,
  salesBreakdownColumns,
  productPeriodScoreColumns,
  customerPeriodScoreColumns
)

function ensureColumns (targetColumns, columnsToEnsure) {
  const existing = new Set(targetColumns.map(col => col.name))
  for (const col of columnsToEnsure) {
    if (!existing.has(col.name)) targetColumns.push(col)
  }
}

function removeColumnsInPlace (targetColumns, names) {
  const removeSet = new Set(names)
  const next = targetColumns.filter(col => !removeSet.has(col.name))
  targetColumns.splice(0, targetColumns.length, ...next)
  return targetColumns
}

function ensureColorYakaColumn (targetColumns) {
  const colorIndex = targetColumns.findIndex(col => col.name === 'color_code')
  const yakaIndex = targetColumns.findIndex(col => col.name === 'yaka_kodu')
  const insertAt = colorIndex >= 0 ? colorIndex : yakaIndex
  if (insertAt >= 0 && !targetColumns.some(col => col.name === 'color_yaka')) {
    targetColumns.splice(insertAt, 0, {
      name: 'color_yaka',
      label: 'Renk/Yaka',
      field: row => colorYakaValue(row),
      align: 'left',
      sortable: true,
      classes: 'color-yaka-cell',
      headerClasses: 'color-yaka-cell'
    })
  }
  removeColumnsInPlace(targetColumns, ['color_code', 'yaka_kodu'])
  return targetColumns
}

;[
  columns,
  generalColumns,
  orderAnalysisColumns,
  orderGroupColumns,
  orderProductCustomerColumns,
  orderMarketDetailColumns,
  idleColumns,
  salesBreakdownColumns
].forEach(ensureColorYakaColumn)

;[
  columns,
  generalColumns,
  orderAnalysisColumns,
  orderGroupColumns,
  orderProductCustomerColumns,
  orderMarketDetailColumns,
  idleColumns,
  marketColumns,
  countryColumns,
  salesBreakdownColumns
].forEach(target => ensureColumns(target, productPeriodScoreColumns))

;[
  customerColumns,
  salesBreakdownColumns
].forEach(target => ensureColumns(target, customerPeriodScoreColumns))

;[
  orderAnalysisColumns,
  orderGroupColumns,
  orderProductCustomerColumns,
  orderMarketDetailColumns,
  idleColumns,
  marketColumns,
  countryColumns,
  customerColumns,
  salesBreakdownColumns
].forEach(target => removeColumnsInPlace(target, ['image']))

;[
  columns,
  generalColumns,
  orderAnalysisColumns,
  orderGroupColumns,
  orderProductCustomerColumns,
  orderMarketDetailColumns,
  idleColumns,
  marketColumns,
  countryColumns,
  customerColumns,
  salesBreakdownColumns
].forEach(target => {
  reorderColumnsInPlace(target)
  attachColumnSorts(target)
})

attachColumnSorts(detailStockColumns)
attachColumnSorts(detailSalesColumns)

const visibleSalesBreakdownColumns = computed(() => {
  const base = orderedMetricColumns(salesBreakdownColumns.filter(visiblePeriodColumn))
  if (activeTab.value !== 'sales_color_yaka_market_customer') return base

  const priority = ['performance_bucket', 'recommendation']
  const pulled = priority
    .map(name => base.find(col => col.name === name))
    .filter(Boolean)
  const rest = base.filter(col => !priority.includes(col.name))
  const insertAfter = Math.max(
    rest.findIndex(col => col.name === 'item_description'),
    rest.findIndex(col => col.name === 'product_code')
  )
  if (insertAfter < 0) return [...pulled, ...rest]
  return [
    ...rest.slice(0, insertAfter + 1),
    ...pulled,
    ...rest.slice(insertAfter + 1)
  ]
})
const salesBreakdownStickyClass = computed(() => activeTab.value === 'sales_color_yaka_market_customer' ? 'sticky-dim-9' : 'sticky-dim-9')

const summaryCards = computed(() => [
  { key: 'date', label: 'KPI Tarihi', value: summary.value.kpi_date || '-' },
  { key: 'rows', label: 'Satır', value: formatNumber(summary.value.total_rows, 0) },
  { key: 'star', label: 'Yıldız', value: formatNumber(summary.value.star_count, 0) },
  { key: 'risk', label: 'Stok Riski', value: formatNumber(summary.value.stock_risk, 0) },
  { key: 'markets', label: '90G Piyasa', value: formatNumber(summary.value.market_count_90d, 0) },
  { key: 'customers', label: '90G Müşteri', value: formatNumber(summary.value.customer_count_90d, 0) },
  { key: 'stock_cost', label: 'Stok Maliyeti', value: formatMoney(summary.value.stock_cost_usd, 'USD') },
  { key: 'risk_cost', label: 'Risk Maliyeti', value: formatMoney(summary.value.risk_cost_usd, 'USD') }
])

const idleRows = computed(() => {
  const variants = new Map()
  for (const row of rows.value) {
    const key = `${row.product_code || ''}|${row.color_code || ''}|${row.yaka_kodu || ''}`
    const current = variants.get(key)
    if (!current) {
      variants.set(key, {
        ...row,
        market_key: row.market_key && row.market_key !== 'STOK' ? row.market_key : '',
        sales_qty_90d: Number(row.sales_qty_90d || 0),
        sales_usd_90d: Number(row.sales_usd_90d || 0),
        gross_profit_usd_90d: Number(row.gross_profit_usd_90d || 0),
        customer_count_90d: Number(row.customer_count_90d || 0),
        market_count_90d: row.market_key && row.market_key !== 'STOK' ? 1 : 0,
        has_stock_risk: row.performance_bucket === 'STOK_RISKI'
      })
      continue
    }
    current.market_key = distinctTextSummary(current.market_key, row.market_key)
    current.sales_qty_90d += Number(row.sales_qty_90d || 0)
    current.sales_usd_90d += Number(row.sales_usd_90d || 0)
    current.gross_profit_usd_90d += Number(row.gross_profit_usd_90d || 0)
    current.customer_count_90d += Number(row.customer_count_90d || 0)
    current.market_count_90d += row.market_key && row.market_key !== 'STOK' ? 1 : 0
    current.has_stock_risk = current.has_stock_risk || row.performance_bucket === 'STOK_RISKI'
  }

  return Array.from(variants.values())
    .map(row => {
      const stockQty = Number(row.stock_qty || 0)
      const salesQty90 = Number(row.sales_qty_90d || 0)
      const stockDays90 = salesQty90 > 0 ? stockQty / (salesQty90 / 90) : 0
      return {
        ...row,
        stock_days_90d: stockDays90,
        idle_cost_usd: stockQty * Number(row.cost_price_usd || 0),
        performance_bucket: row.has_stock_risk ? 'STOK_RISKI' : row.performance_bucket
      }
    })
    .filter(row => Number(row.stock_qty || 0) > 0 && (
      row.performance_bucket === 'STOK_RISKI' ||
      Number(row.sales_qty_90d || 0) === 0 ||
      Number(row.stock_days_90d || 0) > 180
    ))
    .sort((a, b) => Number(b.idle_cost_usd || 0) - Number(a.idle_cost_usd || 0))
})

const visibleOrderAnalysisRows = computed(() => {
  if (orderAnalysisMode.value === 'market') return orderMarketRows.value
  if (orderAnalysisMode.value === 'customer') return orderCustomerRows.value
  return orderAnalysisRows.value
})

const visibleOrderAnalysisColumns = computed(() => {
  if (orderAnalysisMode.value === 'market' || orderAnalysisMode.value === 'customer') return orderGroupColumns
  return orderAnalysisColumns
})

function performanceCardsForRow (row) {
  const source = row || {}
  return [
    { key: 'score90', label: '90G Skor', value: formatNumber(source.performance_score_90d ?? source.performance_score, 2) },
    { key: 'score180', label: '180G Skor', value: formatNumber(source.performance_score_180d, 2) },
    { key: 'score360', label: '360G Skor', value: formatNumber(source.performance_score_365d, 2) },
    { key: 'scoreTotal', label: 'Genel Skor', value: formatNumber(source.performance_score_total ?? source.performance_score, 2) },
    { key: 'stock', label: 'Stok', value: formatNumber(source.stock_qty, 0) },
    { key: 'sales90', label: '90G Satış', value: formatNumber(source.sales_qty_90d, 0) },
    { key: 'sales180', label: '180G Satış', value: formatNumber(source.sales_qty_180d, 0) },
    { key: 'markets', label: '90G Piyasa', value: formatNumber(source.market_count_90d, 0) },
    { key: 'customers', label: '90G Müşteri', value: formatNumber(source.customer_count_90d, 0) },
    { key: 'avg', label: 'Ort. Satış Fiyatı', value: formatMoney(source.avg_price_usd_90d, 'USD') },
    { key: 'base', label: 'Taban Maliyet', value: formatMoney(source.base_price_usd, 'USD') },
    { key: 'cost', label: 'Çıplak Maliyet', value: formatMoney(source.cost_price_usd, 'USD') },
    { key: 'profitBase', label: 'P.Başı Taban K/Z', value: formatMoney(source.unit_profit_base_90d, 'USD') },
    { key: 'profitCost', label: 'P.Başı Çıplak K/Z', value: formatMoney(source.unit_profit_cost_90d, 'USD') },
    { key: 'marginBase', label: '90G Taban Marj', value: formatPercent(source.gross_margin_base_90d) },
    { key: 'marginCost', label: '90G Çıplak Marj', value: formatPercent(source.gross_margin_cost_90d) },
    { key: 'bucket', label: 'Durum', value: bucketLabel(source.performance_bucket) }
  ]
}

const performanceCards = computed(() => performanceCardsForRow(performanceDialogRow.value))
const imageDialogPerformanceCards = computed(() => performanceCardsForRow(imageDialogRow.value))

const imageDialogInfoRows = computed(() => {
  const row = imageDialogRow.value || {}
  const productCode = row.image_product_code || row.product_code
  const colorCode = row.image_color_code || row.color_code
  const yakaCode = row.image_yaka_kodu || row.yaka_kodu
  return [
    { key: 'product', label: 'Ürün', value: productCode || '-' },
    { key: 'color', label: 'Renk', value: colorCode || '-' },
    { key: 'yaka', label: 'Yaka', value: yakaCode || '-' },
    { key: 'category', label: 'Kategori', value: productCategoryValue(row) || '-' },
    { key: 'hanger', label: 'Askılı/Yan', value: cleanOptionalGroupValue(row.askili_yan) },
    { key: 'first', label: 'İlk Grup', value: productFirstGroupValue(row) || '-' },
    { key: 'main', label: 'Ana Grup', value: productMainGroupValue(row) || '-' },
    { key: 'sub', label: 'Alt Grup', value: row.urun_alt_grubu || '-' },
    { key: 'market', label: 'Piyasa', value: row.market_key || '-' },
    { key: 'stock', label: 'Toplam Stok', value: formatNumber(row.stock_qty, 0) },
    { key: 'base', label: 'Taban Maliyet', value: formatMoney(row.base_price_usd, 'USD') },
    { key: 'cost', label: 'Çıplak Maliyet', value: formatMoney(row.cost_price_usd, 'USD') }
  ]
})

const groupLevels = [
  { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
  { key: 'askili_yan', label: 'Askılı/Yan' },
  { key: 'kategori', label: 'Kategori' },
  { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
  { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
  { key: 'product_code', label: 'Ürün' },
  { key: 'color_yaka', label: 'Renk/Yaka' },
  { key: 'market_key', label: 'Piyasa' }
]

const tabGroupLevels = {
  products: groupLevels,
  product_detail: [
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' },
    { key: 'market_key', label: 'Piyasa' }
  ],
  sales_color_yaka_market_customer: [
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'color_yaka', label: 'Renk/Yaka' },
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' }
  ],
  idle: [
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' }
  ],
  sales_product_country_segment_market_customer: [
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' },
    { key: 'country', label: 'Ülke' },
    { key: 'customer_segment', label: 'Segment' },
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' }
  ],
  sales_market_customer_product: [
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' },
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' }
  ],
  sales_country_segment_market_customer_product: [
    { key: 'country', label: 'Ülke' },
    { key: 'customer_segment', label: 'Segment' },
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' },
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' }
  ],
  order_product_customers: [
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' },
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' }
  ],
  order_market_details: [
    { key: 'market_key', label: 'Piyasa' },
    { key: 'customer_code', label: 'Müşteri Kodu' },
    { key: 'customer_name', label: 'Müşteri' },
    { key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
    { key: 'askili_yan', label: 'Askılı/Yan' },
    { key: 'kategori', label: 'Kategori' },
    { key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
    { key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
    { key: 'product_code', label: 'Ürün' },
    { key: 'color_yaka', label: 'Renk/Yaka' }
  ]
}

const activeGroupLevels = computed(() => tabGroupLevels[activeTab.value] || groupLevels)

const activeSelectedExpandLevelKeys = computed({
  get: () => selectedExpandLevelKeysByTab[activeTab.value] || [],
  set: value => {
    selectedExpandLevelKeysByTab[activeTab.value] = Array.isArray(value) ? value : []
  }
})

const expandLevelOptions = computed(() => activeGroupLevels.value.map(level => ({
  label: level.label,
  value: level.key
})))

const selectedExpandThroughLevel = computed(() => {
  const selected = new Set(activeSelectedExpandLevelKeys.value)
  const indexes = activeGroupLevels.value
    .map((level, index) => selected.has(level.key) ? index : -1)
    .filter(index => index >= 0)
  return indexes.length ? Math.max(...indexes) : -1
})

function maxAutoExpandThroughLevelForTab (tabKey = activeTab.value) {
  return Number.isFinite(maxAutoExpandLevelByTab[tabKey]) ? maxAutoExpandLevelByTab[tabKey] : 8
}

function maxBackendExpandedKeysForTab (tabKey = activeTab.value) {
  if (tabKey === 'product_detail') return 1000
  if (tabKey === 'products' || tabKey === 'idle') return 1000
  if (salesBreakdownTabKeys.includes(tabKey)) return 1000
  return 1000
}

const autoExpandThroughLevel = computed(() => {
  const selectedLevel = selectedExpandThroughLevel.value
  const lastGroupLevel = Math.max(0, activeGroupLevels.value.length - 2)
  const requestedLevel = selectedLevel >= 0 ? selectedLevel : 2
  return Math.min(requestedLevel, lastGroupLevel, maxAutoExpandThroughLevelForTab())
})

const fullExpandThroughLevel = computed(() => {
  const lastGroupLevel = Math.max(0, activeGroupLevels.value.length - 2)
  return Math.min(lastGroupLevel, maxAutoExpandThroughLevelForTab())
})

const filteredProductRows = computed(() => filterRowsForTable('products', rows.value, productColumns.value))
const isProductKpiTab = computed(() => activeTab.value === 'products' || activeTab.value === 'product_detail')
const activeProductTableKey = computed(() => activeTab.value === 'product_detail' ? 'product_detail' : 'products')
const detailProductRows = computed(() => {
  const selected = String(selectedDetailMainGroup.value || '').trim()
  if (!selected) return rows.value
  return rows.value.filter(row => productMainGroupValue(row) === selected)
})
const filteredDetailProductRows = computed(() => filterRowsForTable('product_detail', detailProductRows.value, productDetailColumns.value))
const filteredGeneralRows = computed(() => filterRowsForTable('general', generalRows.value, generalColumns))
const filteredOrderProductCustomerRows = computed(() => filterRowsForTable('order_product_customers', orderProductCustomerRows.value, orderProductCustomerColumns))
const filteredOrderMarketDetailRows = computed(() => filterRowsForTable('order_market_details', orderMarketDetailRows.value, orderMarketDetailColumns))
const filteredIdleRows = computed(() => filterRowsForTable('idle', idleRows.value, idleColumns))
const filteredMarketRows = computed(() => filterRowsForTable('markets', marketRows.value, marketColumns))
const filteredCountryRows = computed(() => filterRowsForTable('countries', countryRows.value, countryColumns))
const filteredCustomerRows = computed(() => filterRowsForTable('customers', customerRows.value, customerColumns))
const filteredActiveSalesBreakdownRows = computed(() => filterRowsForTable(activeTab.value, salesBreakdownRows[activeTab.value] || [], visibleSalesBreakdownColumns.value))
const activeGroupSourceRows = computed(() => {
  if (activeTab.value === 'products') return filteredProductRows.value
  if (activeTab.value === 'product_detail') return filteredDetailProductRows.value
  if (activeTab.value === 'idle') return filteredIdleRows.value
  if (activeTab.value === 'order_product_customers') return filteredOrderProductCustomerRows.value
  if (activeTab.value === 'order_market_details') return filteredOrderMarketDetailRows.value
  if (salesBreakdownTabKeys.includes(activeTab.value)) return filteredActiveSalesBreakdownRows.value
  return []
})

const columnFilterOptionMap = computed(() => {
  const out = {}
  const tableKey = isProductKpiTab.value ? activeProductTableKey.value : activeTab.value
  const source = tableFilterSource(tableKey)
  if (!source) return out
  out[tableKey] = {}
  const preparedOptionEntry = backendGroupedSupportedTab(tableKey)
    ? backendGroupedFilterOptions.value[tableKey]
    : null
  const preparedOptions = preparedOptionEntry?._key === backendGroupedFilterOptionsKey(tableKey)
    ? preparedOptionEntry.options || {}
    : {}
  for (const col of source.columns) {
    if (!isColumnFilterable(col.name)) continue
    if (backendGroupedSupportedTab(tableKey) && backendGroupedFilterFields.has(col.name)) {
      out[tableKey][col.name] = preparedOptions[col.name] || []
      continue
    }
    if (backendGroupedSupportedTab(tableKey) && !backendGroupedFilterFields.has(col.name)) {
      out[tableKey][col.name] = []
      continue
    }
    const seen = new Map()
    for (const row of source.rows) {
      const value = columnFilterValue(row, col)
      if (!value) continue
      if (!seen.has(value)) {
        seen.set(value, {
          label: columnFilterLabel(row, col),
          value
        })
      }
    }
    out[tableKey][col.name] = Array.from(seen.values())
      .sort((a, b) => String(a.label).localeCompare(String(b.label), 'tr', { numeric: true }))
  }
  return out
})

const productTableRows = computed(() => {
  if (shouldUseBackendGroupedRows('products')) return []
  const out = []
  appendGroupRows(out, filteredProductRows.value, 0, ['tab:products'], groupLevels, 'products')
  return out
})
const detailProductTableRows = computed(() => shouldUseBackendGroupedRows('product_detail') ? [] : buildGroupedTableRows('product_detail', filteredDetailProductRows.value))

const idleTableRows = computed(() => shouldUseBackendGroupedRows('idle') ? [] : buildGroupedTableRows('idle', filteredIdleRows.value))
const orderProductCustomerTableRows = computed(() => shouldUseBackendGroupedRows('order_product_customers') ? [] : buildGroupedTableRows('order_product_customers', filteredOrderProductCustomerRows.value))
const orderMarketDetailTableRows = computed(() => shouldUseBackendGroupedRows('order_market_details') ? [] : buildGroupedTableRows('order_market_details', filteredOrderMarketDetailRows.value))
const activeSalesBreakdownTableRows = computed(() => shouldUseBackendGroupedRows(activeTab.value) ? [] : buildGroupedTableRows(activeTab.value, filteredActiveSalesBreakdownRows.value))
const displayProductTableRows = computed(() => backendRowsForTab('products', productTableRows.value))
const displayDetailProductTableRows = computed(() => backendRowsForTab('product_detail', detailProductTableRows.value))
const displayProductKpiTableRows = computed(() => activeTab.value === 'product_detail' ? displayDetailProductTableRows.value : displayProductTableRows.value)
const displayIdleTableRows = computed(() => backendRowsForTab('idle', idleTableRows.value))
const displayOrderProductCustomerTableRows = computed(() => backendRowsForTab('order_product_customers', orderProductCustomerTableRows.value))
const displayOrderMarketDetailTableRows = computed(() => backendRowsForTab('order_market_details', orderMarketDetailTableRows.value))
const displayActiveSalesBreakdownTableRows = computed(() => backendRowsForTab(activeTab.value, activeSalesBreakdownTableRows.value))
const activeDisplayedTableRows = computed(() => {
  if (isProductKpiTab.value) return displayProductKpiTableRows.value
  if (activeTab.value === 'idle') return displayIdleTableRows.value
  if (activeTab.value === 'order_product_customers') return displayOrderProductCustomerTableRows.value
  if (activeTab.value === 'order_market_details') return displayOrderMarketDetailTableRows.value
  if (salesBreakdownTabKeys.includes(activeTab.value)) return displayActiveSalesBreakdownTableRows.value
  if (activeTab.value === 'general') return filteredGeneralRows.value
  if (activeTab.value === 'markets') return filteredMarketRows.value
  if (activeTab.value === 'countries') return filteredCountryRows.value
  if (activeTab.value === 'customers') return filteredCustomerRows.value
  return []
})

const detailMainGroupOptions = computed(() => {
  if (detailMainGroupOptionRows.value.length) {
    return [...detailMainGroupOptionRows.value]
      .sort((a, b) => Number(b.sales_qty_90d || 0) - Number(a.sales_qty_90d || 0) || a.label.localeCompare(b.label, 'tr'))
  }
  const grouped = new Map()
  for (const row of rows.value) {
    const value = productMainGroupValue(row)
    if (!value) continue
    const current = grouped.get(value) || { value, label: value, stock_qty: 0, sales_qty_90d: 0, _stockSeen: new Set() }
    const variantKey = productVariantMetricKey(row)
    if (variantKey && !current._stockSeen.has(variantKey)) {
      current._stockSeen.add(variantKey)
      current.stock_qty += Number(row?.stock_qty || 0)
    } else if (!variantKey) {
      current.stock_qty += Number(row?.stock_qty || 0)
    }
    current.sales_qty_90d += Number(row?.sales_qty_90d || 0)
    grouped.set(value, current)
  }
  return Array.from(grouped.values())
    .map(({ _stockSeen, ...option }) => option)
    .sort((a, b) => Number(b.sales_qty_90d || 0) - Number(a.sales_qty_90d || 0) || a.label.localeCompare(b.label, 'tr'))
})

function filterRowsBySelectedDetailMainGroup (sourceRows) {
  const selected = String(selectedDetailMainGroup.value || '').trim()
  const rows = Array.isArray(sourceRows) ? sourceRows : []
  if (!selected) return []
  return rows.filter(row => {
    const value = productMainGroupValue(row)
    if (value === selected) return true
    return Boolean(row?.__group && row.group_field !== 'urun_ana_grubu' && !value)
  })
}

function ensureDetailMainGroupSelection () {
  const options = detailMainGroupOptions.value
  if (!options.length) {
    selectedDetailMainGroup.value = ''
    return false
  }
  const preferred = options.find(option => String(option.value || '').toLocaleUpperCase('tr-TR') === 'TAKIM ELBISE')
  if (!selectedDetailMainGroup.value || !options.some(option => option.value === selectedDetailMainGroup.value)) {
    selectedDetailMainGroup.value = (preferred || options[0]).value
  }
  return true
}

const productGroupKeys = computed(() => {
  const keys = []
  collectGroupKeys(keys, filteredProductRows.value, 0, ['tab:products'], groupLevels)
  return keys
})

const productAutoExpandKeys = computed(() => {
  const keys = []
  collectGroupKeys(keys, activeGroupSourceRows.value, 0, [`tab:${activeTab.value}`], activeGroupLevels.value, autoExpandThroughLevel.value)
  return keys
})

const allProductGroupsExpanded = computed(() => {
  return selectedExpandThroughLevel.value >= fullExpandThroughLevel.value
})

function appendGroupRows (out, sourceRows, level, parentKeys, levels = groupLevels, tableKey = activeTab.value) {
  appendGroupRowsAt(out, sourceRows, level, 0, parentKeys, levels, tableKey)
}

function appendGroupRowsAt (out, sourceRows, level, visualLevel, parentKeys, levels = groupLevels, tableKey = activeTab.value) {
  if (out.length >= maxRenderedGroupedRows) return
  if (level >= levels.length) {
    return
  }

  const groupDef = levels[level]
  const grouped = new Map()
  for (const row of sourceRows) {
    const value = normalizeGroupValue(rawProductCellValue(row, groupDef.key))
    if (!grouped.has(value)) grouped.set(value, [])
    grouped.get(value).push(row)
  }

  Array.from(grouped.entries())
    .sort((a, b) => compareGroupedBucketsForTable(tableKey, a, b, groupDef))
    .forEach(([value, groupRows]) => {
      if (out.length >= maxRenderedGroupedRows) return
      if (shouldSkipGroupValue(groupDef.key, value)) {
        appendGroupRowsAt(out, groupRows, level + 1, visualLevel, parentKeys, levels, tableKey)
        return
      }
      const key = [...parentKeys, `${groupDef.key}:${value}`].join('|')
      out.push(makeGroupRow(key, visualLevel, groupDef, value, groupRows))
      if (isGroupExpanded(key)) {
        appendGroupRowsAt(out, groupRows, level + 1, visualLevel + 1, [...parentKeys, `${groupDef.key}:${value}`], levels, tableKey)
      }
    })
}

function buildGroupedTableRows (tableKey, sourceRows) {
  const levels = tabGroupLevels[tableKey] || groupLevels
  const out = []
  appendGroupRows(out, sourceRows, 0, [`tab:${tableKey}`], levels, tableKey)
  return out
}

function backendRowsForTab (tabKey, fallbackRows) {
  if (shouldUseBackendGroupedRows(tabKey)) {
    const rows = backendGroupedRows.value[tabKey]
    return Array.isArray(rows) ? rows : []
  }
  return tabKey === 'product_detail'
    ? filterRowsBySelectedDetailMainGroup(fallbackRows)
    : fallbackRows
}

function filterSourceRowsForTab (tabKey, fallbackRows) {
  return fallbackRows
}

function backendGroupedFilterOptionParams (tabKey) {
  const columns = filterColumnsForTableKey(tabKey)
  const fields = columns
    .map(col => col?.name || '')
    .filter(name => backendGroupedFilterFields.has(name))
  const params = {
    mode: tabKey,
    groupLevels: (tabGroupLevels[tabKey] || groupLevels).map(level => level.key),
    fields,
    limit: 50000
  }
  if (tabKey === 'product_detail') params.urunAnaGrubu = selectedDetailMainGroup.value
  return params
}

function backendGroupedFilterOptionsKey (tabKey, params = backendGroupedFilterOptionParams(tabKey)) {
  return JSON.stringify({
    tabKey,
    mainGroup: params.urunAnaGrubu || '',
    groupLevels: params.groupLevels || [],
    fields: params.fields || []
  })
}

async function ensureBackendGroupedFilterOptions (tabKey = activeTab.value, force = false) {
  if (!backendGroupedSupportedTab(tabKey)) return
  if (tabKey === 'product_detail' && !selectedDetailMainGroup.value) return
  const params = backendGroupedFilterOptionParams(tabKey)
  if (!params.fields.length) return
  const key = backendGroupedFilterOptionsKey(tabKey, params)
  if (!force && backendGroupedFilterOptions.value[tabKey]?._key === key) return
  try {
    const data = await performanceStore.fetchGroupedFilterOptions(params, { force })
    if (backendGroupedFilterOptionsKey(tabKey) !== key) return
    backendGroupedFilterOptions.value = {
      ...backendGroupedFilterOptions.value,
      [tabKey]: {
        _key: key,
        options: normalizeBackendGroupedFilterOptions(data)
      }
    }
  } catch (err) {
    console.warn('product performance grouped filter options failed', err)
  }
}

function normalizeBackendGroupedFilterOptions (data) {
  const out = {}
  const source = data && typeof data === 'object' ? data : {}
  for (const [field, values] of Object.entries(source)) {
    if (!Array.isArray(values)) continue
    const seen = new Map()
    for (const raw of values) {
      const value = normalizeBackendFilterValue(field, raw)
      if (!value || seen.has(value)) continue
      seen.set(value, {
        value,
        label: backendFilterOptionLabel(field, value)
      })
    }
    out[field] = Array.from(seen.values())
      .sort((a, b) => String(a.label).localeCompare(String(b.label), 'tr', { numeric: true }))
  }
  return out
}

function normalizeBackendFilterValue (name, value) {
  const text = String(value || '').trim()
  if (name === 'performance_bucket') {
    return bucketOptions.find(option => option.value === text || option.label === text)?.value || text
  }
  return text
}

function backendFilterOptionLabel (name, value) {
  if (name === 'performance_bucket') return bucketLabel(value)
  return String(value || '').trim()
}

function backendGroupedFilterState (tabKey) {
  const out = {}
  const columns = filterColumnsForTableKey(tabKey)
  for (const col of columns) {
    const name = col?.name || ''
    if (!isColumnFilterable(name)) continue
    const selected = selectedColumnFilters(tabKey, name)
    if (!selected.length) continue
    if (!backendGroupedFilterFields.has(name)) {
      continue
    }
    out[name] = selected.map(value => normalizeBackendFilterValue(name, value)).filter(Boolean)
  }
  return { filters: out, unsupported: false }
}

function shouldUseBackendGroupedRows (tabKey) {
  return backendGroupedSupportedTab(tabKey) && !backendGroupedFilterState(tabKey).unsupported
}

function tableFilterSource (tableKey) {
  if (backendGroupedSupportedTab(tableKey)) {
    return { rows: backendGroupedRows.value[tableKey] || [], columns: filterColumnsForTableKey(tableKey) }
  }
  if (tableKey === 'general') return { rows: generalRows.value, columns: generalColumns }
  if (tableKey === 'markets') return { rows: marketRows.value, columns: marketColumns }
  if (tableKey === 'countries') return { rows: countryRows.value, columns: countryColumns }
  if (tableKey === 'customers') return { rows: customerRows.value, columns: customerColumns }
  return null
}

function columnsForTableKey (tabKey) {
  if (tabKey === 'products') return visibleProductColumns.value
  if (tabKey === 'product_detail') return visibleProductDetailColumns.value
  if (tabKey === 'general') return visibleGeneralColumns.value
  if (tabKey === 'order_product_customers') return orderProductCustomerColumns
  if (tabKey === 'order_market_details') return orderMarketDetailColumns
  if (tabKey === 'idle') return idleColumns
  if (tabKey === 'markets') return marketColumns
  if (tabKey === 'countries') return countryColumns
  if (tabKey === 'customers') return customerColumns
  if (salesBreakdownTabKeys.includes(tabKey)) return visibleSalesBreakdownColumns.value
  return productColumns.value
}

function filterColumnsForTableKey (tabKey) {
  if (tabKey === 'products') return productColumns.value
  if (tabKey === 'product_detail') return productDetailColumns.value
  if (tabKey === 'general') return generalColumns
  if (tabKey === 'order_product_customers') return orderProductCustomerColumns
  if (tabKey === 'order_market_details') return orderMarketDetailColumns
  if (tabKey === 'idle') return idleColumns
  if (tabKey === 'markets') return marketColumns
  if (tabKey === 'countries') return countryColumns
  if (tabKey === 'customers') return customerColumns
  if (salesBreakdownTabKeys.includes(tabKey)) return visibleSalesBreakdownColumns.value
  return productColumns.value
}

function collectGroupKeys (out, sourceRows, level, parentKeys, levels = groupLevels, maxLevel = levels.length - 1) {
  if (out.length >= maxBulkExpandKeys) return
  if (level >= levels.length) return

  const groupDef = levels[level]
  const grouped = new Map()
  for (const row of sourceRows) {
    const value = normalizeGroupValue(rawProductCellValue(row, groupDef.key))
    if (!grouped.has(value)) grouped.set(value, [])
    grouped.get(value).push(row)
  }

  Array.from(grouped.entries())
    .sort((a, b) => a[0].localeCompare(b[0], 'tr'))
    .forEach(([value, groupRows]) => {
      if (out.length >= maxBulkExpandKeys) return
      if (shouldSkipGroupValue(groupDef.key, value)) {
        collectGroupKeys(out, groupRows, level + 1, parentKeys, levels, maxLevel)
        return
      }
      const key = [...parentKeys, `${groupDef.key}:${value}`].join('|')
      out.push(key)
      if (level < maxLevel) {
        collectGroupKeys(out, groupRows, level + 1, [...parentKeys, `${groupDef.key}:${value}`], levels, maxLevel)
      }
    })
}

function sortProductLeafRows (sourceRows) {
  return [...sourceRows].sort((a, b) => {
    const colorCmp = String(a?.color_code || '').localeCompare(String(b?.color_code || ''), 'tr', { numeric: true })
    if (colorCmp !== 0) return colorCmp
    const yakaCmp = String(a?.yaka_kodu || '').localeCompare(String(b?.yaka_kodu || ''), 'tr', { numeric: true })
    if (yakaCmp !== 0) return yakaCmp
    return String(a?.item_description || '').localeCompare(String(b?.item_description || ''), 'tr', { numeric: true })
  })
}

function sortLeafRowsForTable (tableKey, sourceRows) {
  const { sortBy, descending } = tableSortState(tableKey)
  if (!sortBy) return sortProductLeafRows(sourceRows)
  return sortFlatTableRows(sourceRows, sortBy, descending)
}

function tableSortState (tableKey) {
  const pagination = tablePagination[tableKey] || {}
  return {
    sortBy: pagination.sortBy || '',
    descending: pagination.descending !== false
  }
}

function compareGroupedBucketsForTable (tableKey, left, right, groupDef) {
  const [leftLabel, leftRows] = left
  const [rightLabel, rightRows] = right
  const { sortBy, descending } = tableSortState(tableKey)
  if (!sortBy || sortBy === groupDef.key) {
    return String(leftLabel).localeCompare(String(rightLabel), 'tr', { numeric: true, sensitivity: 'base' })
  }

  const leftValue = groupedBucketSortValue(leftRows, sortBy)
  const rightValue = groupedBucketSortValue(rightRows, sortBy)
  const cmp = compareColumnSortValues(leftValue, rightValue)
  if (cmp !== 0) return descending ? -cmp : cmp
  return String(leftLabel).localeCompare(String(rightLabel), 'tr', { numeric: true, sensitivity: 'base' })
}

function groupedBucketSortValue (sourceRows, sortBy) {
  if (!sourceRows?.length) return 0
  if (shouldUseDerivedGroupSortValue(sortBy)) {
    return sortValueForColumnName(aggregateGroupFields(sourceRows), sortBy)
  }
  if (sortBy === 'stock_qty') return distinctVariantStockQty(sourceRows)
  if (sortBy === 'idle_cost_usd') return distinctVariantStockCost(sourceRows)
  if (sortBy === 'base_price_usd' || sortBy === 'cost_price_usd') return weightedAverageProductCost(sourceRows, sortBy)
  if (shouldSumField(sortBy)) return sumRows(sourceRows, sortBy)
  if (shouldAverageField(sortBy)) return weightedAverageOrAverage(sourceRows, sortBy, weightFieldForMetric(sortBy))
  return sortValueForColumnName(sourceRows[0], sortBy)
}

function shouldUseDerivedGroupSortValue (sortBy) {
  return /^(performance_score|stock_turnover|stock_days|avg_price_usd|unit_profit|gross_profit|gross_margin|market_count|customer_count|sales_index)/i.test(String(sortBy || ''))
}

function sortProductGroupedTableRows (tableRows, sortBy, descending) {
  const source = Array.isArray(tableRows) ? tableRows : []
  if (backendGroupedSupportedTab(activeTab.value)) return source
  if (!sortBy || !source.some(row => row?.__group)) {
    return sortFlatTableRows(source, sortBy, descending)
  }

  const roots = []
  const stack = []

  for (const row of source) {
    const node = { row, children: [] }
    if (row?.__group) {
      const level = Math.max(0, Number(row.level || 0))
      stack.length = Math.min(stack.length, level)
      const parent = level > 0 ? stack[level - 1] : null
      if (parent) parent.children.push(node)
      else roots.push(node)
      stack[level] = node
      stack.length = level + 1
    } else {
      const parent = stack[stack.length - 1]
      if (parent) parent.children.push(node)
      else roots.push(node)
    }
  }

  const sorted = []
  const appendNodes = (nodes) => {
    const ordered = sortNodeSiblings(nodes, sortBy, descending)
    for (const node of ordered) {
      sorted.push(node.row)
      if (node.children.length) appendNodes(node.children)
    }
  }
  appendNodes(roots)
  return sorted
}

function sortNodeSiblings (nodes, sortBy, descending) {
  const source = mergeDuplicateGroupSiblingNodes(nodes)
  if (!sortBy) return source
  return source.sort((a, b) => {
    const cmp = compareColumnSortValues(sortValueForColumnName(a.row, sortBy), sortValueForColumnName(b.row, sortBy))
    return descending ? -cmp : cmp
  })
}

function mergeDuplicateGroupSiblingNodes (nodes) {
  const out = []
  const seen = new Map()
  for (const node of nodes) {
    if (!node?.row?.__group) {
      out.push(node)
      continue
    }
    const key = [
      Number(node.row.level || 0),
      String(node.row.group_field || ''),
      String(node.row.group_value || node.row.label || '')
    ].join('|')
    const existing = seen.get(key)
    if (existing) {
      existing.children.push(...node.children)
      continue
    }
    seen.set(key, node)
    out.push(node)
  }
  return out
}

function sortFlatTableRows (tableRows, sortBy, descending) {
  if (!sortBy) return [...tableRows]
  return [...tableRows].sort((a, b) => {
    const cmp = compareColumnSortValues(sortValueForColumnName(a, sortBy), sortValueForColumnName(b, sortBy))
    return descending ? -cmp : cmp
  })
}

function sortValueForColumnName (row, columnName) {
  const columns = [
    ...(productColumns.value || []),
    ...idleColumns,
    ...orderProductCustomerColumns,
    ...orderMarketDetailColumns,
    ...(visibleSalesBreakdownColumns.value || [])
  ]
  const col = columns.find(item => item.name === columnName || item.field === columnName) || { name: columnName, field: columnName }
  return sortValueForColumn(row, col)
}

function makeGroupRow (key, level, groupDef, value, groupRows) {
  const imageSource = groupRows.find(row => row?.product_code) || {}
  const aggregate = aggregateGroupFields(groupRows, groupDef.key)
  const colorSummary = groupDef.key === 'color_code' ? value : ''
  const yakaSummary = groupDef.key === 'yaka_kodu' ? value : ''
  const colorDescriptionSummary = groupDef.key === 'color_code' ? distinctSummary(groupRows, 'color_description') : ''
  return {
    __group: true,
    row_key: `group|${key}`,
    key,
    level,
    group_field: groupDef.key,
    group_value: value,
    label: colorYakaGroupLabel(groupDef.key, value, colorSummary, yakaSummary),
    count: groupRows.length,
    image_product_code: imageSource.product_code || '',
    image_color_code: imageSource.color_code || '',
    image_color_description: imageSource.color_description || '',
    image_yaka_kodu: imageSource.yaka_kodu || '',
    product_code: groupDef.key === 'product_code' ? value : '',
    color_code: colorSummary,
    color_description: colorDescriptionSummary,
    yaka_kodu: yakaSummary,
    item_description: groupDef.key === 'item_description' ? value : '',
    kategori: groupDef.key === 'kategori' ? value : '',
    urun_ilk_grubu: groupDef.key === 'urun_ilk_grubu' ? value : '',
    askili_yan: groupDef.key === 'askili_yan' ? value : '',
    urun_ana_grubu: groupDef.key === 'urun_ana_grubu' ? value : '',
    urun_alt_grubu: groupDef.key === 'urun_alt_grubu' ? value : '',
    market_key: groupDef.key === 'market_key' ? value : '',
    country: groupDef.key === 'country' ? value : '',
    customer_segment: groupDef.key === 'customer_segment' ? value : '',
    customer_code: groupDef.key === 'customer_code' ? value : '',
    customer_name: groupDef.key === 'customer_name' ? value : '',
    stock_qty: sumRows(groupRows, 'stock_qty'),
    sales_qty_90d: sumRows(groupRows, 'sales_qty_90d'),
    sales_qty_180d: sumRows(groupRows, 'sales_qty_180d'),
    sales_qty_365d: sumRows(groupRows, 'sales_qty_365d'),
    sales_qty_total: sumRows(groupRows, 'sales_qty_total'),
    sales_usd_90d: sumRows(groupRows, 'sales_usd_90d'),
    sales_usd_180d: sumRows(groupRows, 'sales_usd_180d'),
    sales_usd_365d: sumRows(groupRows, 'sales_usd_365d'),
    sales_usd_total: sumRows(groupRows, 'sales_usd_total'),
    gross_profit_usd_90d: sumRows(groupRows, 'gross_profit_usd_90d'),
    gross_profit_usd_180d: sumRows(groupRows, 'gross_profit_usd_180d'),
    gross_profit_usd_total: sumRows(groupRows, 'gross_profit_usd_total'),
    market_count_90d: distinctSpreadCount(groupRows, 'market_count_90d'),
    customer_count_90d: distinctSpreadCount(groupRows, 'customer_count_90d'),
    market_count_total: distinctSpreadCount(groupRows, 'market_count_total'),
    customer_count_total: distinctSpreadCount(groupRows, 'customer_count_total'),
    avg_price_usd_90d: weightedAverage(groupRows, 'sales_usd_90d', 'sales_qty_90d'),
    avg_price_usd_180d: weightedAverage(groupRows, 'sales_usd_180d', 'sales_qty_180d'),
    avg_price_usd_365d: weightedAverage(groupRows, 'sales_usd_365d', 'sales_qty_365d'),
    avg_price_usd_total: weightedAverage(groupRows, 'sales_usd_total', 'sales_qty_total'),
    base_price_usd: weightedAverageProductCost(groupRows, 'base_price_usd'),
    cost_price_usd: weightedAverageProductCost(groupRows, 'cost_price_usd'),
    unit_profit_base_90d: weightedAverageOrAverage(groupRows, 'unit_profit_base_90d', 'sales_qty_90d'),
    unit_profit_cost_90d: weightedAverageOrAverage(groupRows, 'unit_profit_cost_90d', 'sales_qty_90d'),
    unit_profit_base_180d: weightedAverageOrAverage(groupRows, 'unit_profit_base_180d', 'sales_qty_180d'),
    unit_profit_cost_180d: weightedAverageOrAverage(groupRows, 'unit_profit_cost_180d', 'sales_qty_180d'),
    unit_profit_base_total: weightedAverageOrAverage(groupRows, 'unit_profit_base_total', 'sales_qty_total'),
    unit_profit_cost_total: weightedAverageOrAverage(groupRows, 'unit_profit_cost_total', 'sales_qty_total'),
    gross_margin_base_90d: marginFromRows(groupRows, 'sales_usd_90d', 'sales_qty_90d', 'base_price_usd'),
    gross_margin_cost_90d: marginFromRows(groupRows, 'sales_usd_90d', 'sales_qty_90d', 'cost_price_usd'),
    gross_margin_base_180d: marginFromRows(groupRows, 'sales_usd_180d', 'sales_qty_180d', 'base_price_usd'),
    gross_margin_cost_180d: marginFromRows(groupRows, 'sales_usd_180d', 'sales_qty_180d', 'cost_price_usd'),
    gross_margin_base_total: marginFromRows(groupRows, 'sales_usd_total', 'sales_qty_total', 'base_price_usd'),
    gross_margin_cost_total: marginFromRows(groupRows, 'sales_usd_total', 'sales_qty_total', 'cost_price_usd'),
    gross_margin_90d: ratio(sumRows(groupRows, 'gross_profit_usd_90d'), sumRows(groupRows, 'sales_usd_90d')),
    gross_margin_180d: ratio(sumRows(groupRows, 'gross_profit_usd_180d'), sumRows(groupRows, 'sales_usd_180d')),
    stock_days_90d: weightedAverageOrAverage(groupRows, 'stock_days_90d', 'stock_qty'),
    stock_days_180d: weightedAverageOrAverage(groupRows, 'stock_days_180d', 'stock_qty'),
    stock_days_total: weightedAverageOrAverage(groupRows, 'stock_days_total', 'stock_qty'),
    sales_index_90d: averageRows(groupRows, 'sales_index_90d'),
    sales_index_total: averageRows(groupRows, 'sales_index_total'),
    performance_bucket: dominantValue(groupRows, 'performance_bucket'),
    ...aggregate,
    performance_score: Object.prototype.hasOwnProperty.call(aggregate, 'performance_score')
      ? aggregate.performance_score
      : groupPerformanceScore(aggregate, groupDef.key),
    recommendation: dominantValue(groupRows, 'recommendation') || `${formatNumber(groupRows.length, 0)} satır`
  }
}

function aggregateGroupFields (sourceRows, groupField = '') {
  const out = {}
  const fields = new Set()
  for (const row of sourceRows) {
    Object.keys(row || {}).forEach(key => fields.add(key))
  }

  for (const field of fields) {
    if (field === 'row_key' || field === 'key') continue
    if (/^gross_margin(_base|_cost)?(_|$)/i.test(field)) continue
    if (field === 'stock_qty' || /^avg_stock_/i.test(field)) {
      out[field] = distinctVariantNumber(sourceRows, field)
      continue
    }
    if (field === 'idle_cost_usd') {
      out[field] = distinctVariantStockCost(sourceRows)
      continue
    }
    if (field === 'base_price_usd' || field === 'cost_price_usd') {
      out[field] = weightedAverageProductCost(sourceRows, field)
      continue
    }
    if (isDistinctSpreadCountField(field)) {
      out[field] = distinctSpreadCount(sourceRows, field)
      continue
    }
    if (shouldSumField(field)) {
      out[field] = sumRows(sourceRows, field)
    } else if (shouldAverageField(field)) {
      out[field] = weightedAverageOrAverage(sourceRows, field, weightFieldForMetric(field))
    }
  }

  applyDerivedGroupMetrics(out, sourceRows, groupField)
  return out
}

function shouldSumField (field) {
  return /^(sales_qty_|sales_usd_|avg_daily_sales_|gross_profit_|invoice_count_|market_count_|customer_count_|product_group_count_|product_count_)/i.test(field) ||
    /(_qty|_usd|_count|_value_usd|line_count|invoice_count|order_count|product_count|market_count|customer_count|overdue_qty|stock_qty|net_stock_after_order|idle_cost_usd)$/i.test(field)
}

function shouldAverageField (field) {
  if (/^avg_daily_sales_/i.test(field)) return false
  return /^(avg_|unit_|base_price|cost_price|gross_margin|expected_margin|sales_index|performance_score|customer_score|stock_days|stock_turnover)/i.test(field) ||
    /(_price_usd|_margin|_index|_days)$/i.test(field)
}

function weightFieldForMetric (field) {
  if (field.startsWith('stock_turnover')) return `sales_qty_${countPeriodSuffix(field)}`
  if (field.includes('_180d')) return 'sales_qty_180d'
  if (field.includes('_365d')) return 'sales_qty_365d'
  if (field.includes('_total')) return 'sales_qty_total'
  if (field.includes('order') || field.includes('expected_')) return 'order_qty'
  if (field.includes('stock')) return 'stock_qty'
  return 'sales_qty_90d'
}

function applyDerivedGroupMetrics (out, sourceRows, groupField = '') {
  Object.assign(out, normalizeProductCostFields(out))
  const isGroup = Boolean(String(groupField || '').trim())
  const preservedCustomerScores = {}
  const preservedProductScores = {}
  const preservedStockTurnovers = {}
  for (const suffix of ['90d', '180d', '365d', 'total']) {
    if (Object.prototype.hasOwnProperty.call(out, `customer_score_${suffix}`)) {
      preservedCustomerScores[suffix] = Number(out[`customer_score_${suffix}`] || 0)
    }
    if (Object.prototype.hasOwnProperty.call(out, `performance_score_${suffix}`)) {
      preservedProductScores[suffix] = Number(out[`performance_score_${suffix}`] || 0)
    }
    if (isGroup && Object.prototype.hasOwnProperty.call(out, `stock_turnover_${suffix}`)) {
      preservedStockTurnovers[suffix] = Number(out[`stock_turnover_${suffix}`] || 0)
    }
  }
  if (
    Object.prototype.hasOwnProperty.call(out, 'performance_score') &&
    !Object.prototype.hasOwnProperty.call(preservedProductScores, '90d')
  ) {
    preservedProductScores['90d'] = Number(out.performance_score || 0)
  }
  for (const suffix of ['90d', '180d', '365d', 'total']) {
    const sales = Number(out[`sales_usd_${suffix}`] || 0)
    const qty = Number(out[`sales_qty_${suffix}`] || 0)
    const stockQty = Number(out.stock_qty || 0)
    const avgStock = Number(out[`avg_stock_${suffix}`] || 0)
    const turnoverBase = avgStock > 0 ? avgStock : (isGroup ? 0 : stockQty)
    const days = productPerformancePeriodDays(out, suffix)
    const avgDaily = days > 0 ? qty / days : Number(out[`avg_daily_sales_${suffix}`] || 0)
    if (days > 0) out[`avg_daily_sales_${suffix}`] = avgDaily
    if (avgDaily > 0 && turnoverBase > 0) out[`stock_days_${suffix}`] = turnoverBase / avgDaily
    else if (qty <= 0 && stockQty > 0) out[`stock_days_${suffix}`] = 9999
    else if (!Object.prototype.hasOwnProperty.call(out, `stock_days_${suffix}`)) out[`stock_days_${suffix}`] = 0
    out[`stock_turnover_${suffix}`] = annualizedStockTurnover(qty, turnoverBase, days)
    if (Object.prototype.hasOwnProperty.call(preservedStockTurnovers, suffix)) {
      out[`stock_turnover_${suffix}`] = preservedStockTurnovers[suffix]
    }
    if (qty > 0) out[`avg_price_usd_${suffix}`] = sales / qty
    const { costPrice, basePrice } = periodCostPair(out, suffix)
    out[`base_price_usd_${suffix}`] = basePrice
    out[`cost_price_usd_${suffix}`] = costPrice
    if (qty > 0) {
      const avgPrice = sales / qty
      out[`unit_profit_base_${suffix}`] = avgPrice - basePrice
      out[`unit_profit_cost_${suffix}`] = avgPrice - costPrice
      out[`gross_profit_base_usd_${suffix}`] = sales - qty * basePrice
      out[`gross_profit_cost_usd_${suffix}`] = sales - qty * costPrice
      out[`gross_profit_usd_${suffix}`] = out[`gross_profit_cost_usd_${suffix}`]
    } else {
      out[`unit_profit_base_${suffix}`] = 0
      out[`unit_profit_cost_${suffix}`] = 0
      out[`gross_profit_base_usd_${suffix}`] = 0
      out[`gross_profit_cost_usd_${suffix}`] = 0
      out[`gross_profit_usd_${suffix}`] = 0
    }
    out[`gross_margin_base_${suffix}`] = sales > 0 ? Number(out[`gross_profit_base_usd_${suffix}`] || 0) / sales : 0
    out[`gross_margin_cost_${suffix}`] = sales > 0 ? Number(out[`gross_profit_cost_usd_${suffix}`] || 0) / sales : 0
    out[`gross_margin_${suffix}`] = sales > 0 ? Number(out[`gross_profit_usd_${suffix}`] || 0) / sales : 0
  }

  const orderUsd = Number(out.order_usd || 0)
  if (orderUsd > 0) {
    if (out.expected_profit_base_usd !== undefined) out.expected_margin_base = Number(out.expected_profit_base_usd || 0) / orderUsd
    if (out.expected_profit_cost_usd !== undefined) out.expected_margin_cost = Number(out.expected_profit_cost_usd || 0) / orderUsd
  }

  const orderQty = Number(out.order_qty || 0)
  if (orderQty > 0) out.avg_order_price_usd = Number(out.order_usd || 0) / orderQty
  if (out.net_stock_after_order !== undefined || orderQty > 0) {
    out.net_stock_after_order = Number(out.stock_qty || 0) - orderQty
  }

  if (sourceRows.some(row => row.performance_bucket)) {
    out.performance_bucket = dominantValue(sourceRows, 'performance_bucket')
  }
  for (const suffix of ['90d', '180d', '365d', 'total']) {
    out[`customer_score_${suffix}`] = Object.prototype.hasOwnProperty.call(preservedCustomerScores, suffix)
      ? preservedCustomerScores[suffix]
      : customerSalesPeriodScore(periodMetricSource(out, suffix))
    out[`performance_score_${suffix}`] = Object.prototype.hasOwnProperty.call(preservedProductScores, suffix)
      ? preservedProductScores[suffix]
      : productSalesPeriodScore(productPeriodMetricSource(out, suffix))
  }
  out.performance_score = Number(out.performance_score_90d || 0)
}

function productPerformancePeriodDays (row, suffix) {
  if (suffix === '90d') return 90
  if (suffix === '180d') return 180
  if (suffix === '365d') return 360
  if (suffix !== 'total') return 0
  const start = parseProductPerformanceDate(row?.period_start) || new Date(Date.UTC(2022, 0, 1))
  const end = parseProductPerformanceDate(row?.period_end) || parseProductPerformanceDate(row?.kpi_date)
  if (!start || !end || end < start) return 0
  return Math.floor((end.getTime() - start.getTime()) / 86400000) + 1
}

function parseProductPerformanceDate (value) {
  const text = String(value || '').trim().slice(0, 10)
  if (!/^\d{4}-\d{2}-\d{2}$/.test(text)) return null
  const [year, month, day] = text.split('-').map(Number)
  return new Date(Date.UTC(year, month - 1, day))
}

function groupPerformanceScore (row, groupField = '') {
  if (Number(row?.order_qty || 0) > 0 || Number(row?.order_usd || 0) > 0) return orderGroupPerformanceScore(row)
  if (['customer_code', 'customer_name'].includes(groupField)) return customerSalesGroupPerformanceScore(row)
  return salesGroupPerformanceScore(row)
}

function salesGroupPerformanceScore (row) {
  return productSalesPeriodScore(productPeriodMetricSource(row, '90d'))
}

function productSalesPeriodScore (row) {
  const suffix = row?.suffix || '90d'
  const periodDays = productPerformancePeriodDays(row, suffix)
  return productScore100({
    suffix,
    periodDays,
    salesUSD: Number(row?.sales_usd_90d || 0),
    salesIndex: Number(row?.sales_index_90d || 0),
    margin: Number(row?.gross_margin_cost_90d ?? row?.gross_margin_90d ?? 0),
    stockTurnover: Number(row?.stock_turnover_90d || 0),
    marketCount: Number(row?.market_count_90d || 0),
    customerCount: Number(row?.customer_count_90d || 0)
  })
}

function productPeriodMetricSource (row, suffix) {
  const salesIndex = Number(row?.[`sales_index_${suffix}`] || 0)
  const qty = Number(row?.[`sales_qty_${suffix}`] || 0)
  const stockQty = Number(row?.stock_qty || 0)
  const avgStock = Number(row?.[`avg_stock_${suffix}`] || 0)
  const isGroup = Boolean(row?.__group || String(row?.group_field || '').trim())
  const turnoverBase = avgStock > 0 ? avgStock : (isGroup ? 0 : stockQty)
  const turnoverKey = `stock_turnover_${suffix}`
  const hasTurnover = Object.prototype.hasOwnProperty.call(row || {}, turnoverKey)
  return {
    suffix,
    sales_index_90d: salesIndex,
    sales_usd_90d: Number(row?.[`sales_usd_${suffix}`] || 0),
    gross_margin_cost_90d: Number(row?.[`gross_margin_cost_${suffix}`] ?? 0),
    gross_margin_90d: Number(row?.[`gross_margin_${suffix}`] ?? 0),
    sales_qty_90d: qty,
    stock_turnover_90d: hasTurnover ? Number(row?.[turnoverKey] || 0) : annualizedStockTurnover(qty, turnoverBase, productPerformancePeriodDays(row, suffix)),
    market_count_90d: periodCount(row, 'market_count', suffix),
    customer_count_90d: periodCount(row, 'customer_count', suffix),
    stock_qty: stockQty
  }
}

function customerSalesGroupPerformanceScore (row) {
  const periodToScore = {
    90: Number(row?.customer_score_90d || 0),
    180: Number(row?.customer_score_180d || 0),
    360: Number(row?.customer_score_365d || 0),
    total: Number(row?.customer_score_total || 0)
  }
  const selected = selectedPeriods.value.length ? selectedPeriods.value : ['90', '180', '360', 'total']
  const scores = selected.map(period => periodToScore[period]).filter(score => Number.isFinite(score) && score > 0)
  if (!scores.length) return 0
  return scores.reduce((sum, score) => sum + score, 0) / scores.length
}

function customerSalesPeriodScore (row) {
  return customerScore100({
    suffix: row?.suffix || '90d',
    salesUSD: Number(row?.sales_usd || 0),
    margin: Number(row?.gross_margin_cost ?? row?.gross_margin ?? 0),
    productGroupCount: Number(row?.product_group_count || row?.product_count || 0),
    salesQty: Number(row?.sales_qty || 0)
  })
}

function periodMetricSource (row, suffix) {
  return {
    suffix,
    sales_usd: Number(row?.[`sales_usd_${suffix}`] || 0),
    gross_margin_cost: Number(row?.[`gross_margin_cost_${suffix}`] ?? 0),
    gross_margin: Number(row?.[`gross_margin_${suffix}`] ?? 0),
    sales_qty: Number(row?.[`sales_qty_${suffix}`] || 0),
    product_group_count: periodCount(row, 'product_group_count', suffix),
    product_count: Number(row?.product_count || 0)
  }
}

function productScore100 ({ suffix = '90d', periodDays = 0, salesUSD = 0, margin = 0, stockTurnover = 0, marketCount = 0, customerCount = 0 } = {}) {
  if (Number(salesUSD || 0) <= 0) return 1
  const revenue = ratioScore(salesUSD, productRevenueTarget(suffix, periodDays))
  return clampScore(
    0.30 * marginScore(margin) +
    0.20 * ratioScore(stockTurnover, stockTurnoverTarget(suffix)) +
    0.20 * revenue +
    0.05 * ratioScore(marketCount, marketSpreadTarget(suffix)) +
    0.25 * ratioScore(customerCount, customerSpreadTarget(suffix, periodDays))
  )
}

function customerScore100 ({ suffix = '90d', salesUSD = 0, margin = 0, productGroupCount = 0, salesQty = 0 } = {}) {
  if (Number(salesUSD || 0) <= 0) return 1
  return clampScore(
    0.35 * ratioScore(salesUSD, customerRevenueTarget(suffix)) +
    0.30 * marginScore(margin) +
    0.20 * ratioScore(productGroupCount, 8) +
    0.15 * ratioScore(salesQty, customerQtyTarget(suffix))
  )
}

function ratioScore (value, target) {
  const n = Number(value || 0)
  const t = Number(target || 0)
  if (!Number.isFinite(n) || !Number.isFinite(t) || n <= 0 || t <= 0) return 0
  return Math.min(100, Math.max(0, n) * 100 / t)
}

function marginScore (margin) {
  return ratioScore(Math.max(0, Number(margin || 0)), 0.45)
}

function clampScore (score) {
  const n = Number(score || 0)
  if (!Number.isFinite(n) || n <= 0) return 0
  return Math.round(Math.min(100, n) * 10000) / 10000
}

function productRevenueTarget (suffix, periodDays = 0) {
  if (suffix === '180d') return 50000
  if (suffix === '365d') return 100000
  if (suffix === 'total') return 50000 * totalPeriodMultiplier(periodDays)
  return 25000
}

function customerRevenueTarget (suffix) {
  if (suffix === '180d') return 50000
  if (suffix === '365d') return 100000
  if (suffix === 'total') return 300000
  return 25000
}

function customerQtyTarget (suffix) {
  if (suffix === '180d') return 1000
  if (suffix === '365d') return 2000
  if (suffix === 'total') return 6000
  return 500
}

const STOCK_TURNOVER_YEAR_DAYS = 360

function annualizedStockTurnover (salesQty, avgStock, periodDays) {
  const qty = Number(salesQty || 0)
  const stock = Number(avgStock || 0)
  const days = Number(periodDays || 0)
  if (!Number.isFinite(qty) || !Number.isFinite(stock) || qty <= 0 || stock <= 0) return 0
  const raw = qty / stock
  return days > 0 ? raw * STOCK_TURNOVER_YEAR_DAYS / days : raw
}

function stockTurnoverTarget () {
  return 4
}

function marketSpreadTarget (suffix) {
  if (suffix === '90d') return 3
  if (suffix === '180d') return 3
  if (suffix === '365d') return 6
  return 6
}

function customerSpreadTarget (suffix, periodDays = 0) {
  if (suffix === '90d') return 8
  if (suffix === '180d') return 20
  if (suffix === '365d') return 50
  return 20 * totalPeriodMultiplier(periodDays)
}

function totalPeriodMultiplier (periodDays = 0) {
  let days = Number(periodDays || 0)
  if (!Number.isFinite(days) || days <= 0) {
    days = productPerformancePeriodDays({ kpi_date: new Date().toISOString().slice(0, 10) }, 'total')
  }
  return Math.max(1, days / 180)
}

function periodCount (row, prefix, suffix) {
  const keyed = Number(row?.[`${prefix}_${suffix}`] || 0)
  if (keyed > 0) return keyed
  if (suffix === 'total') return Number(row?.[`${prefix}_total`] || 0)
  return Number(row?.[`${prefix}_90d`] || row?.[prefix] || 0)
}

function orderGroupPerformanceScore (row) {
  const orderUSD = Number(row?.order_usd || 0)
  const margin = Number(row?.expected_margin_cost || 0)
  const orders = Number(row?.order_count || 0)
  const qty = Number(row?.order_qty || 0)
  const stockBonus = Number(row?.net_stock_after_order || 0) >= 0 ? 10 : 0
  return Math.min(35, orderUSD / 1000) +
    Math.min(25, Math.max(0, margin) * 60) +
    Math.min(15, orders * 2) +
    Math.min(15, qty / 10) +
    stockBonus
}

function sumRows (sourceRows, field) {
  return sourceRows.reduce((sum, row) => sum + Number(row[field] || 0), 0)
}

function productVariantStockKey (row) {
  const productCode = String(row?.product_code || '').trim()
  const colorCode = String(row?.color_code || '').trim()
  const yakaKodu = String(row?.yaka_kodu || '').trim()
  if (!productCode && !colorCode && !yakaKodu) return ''
  return `${productCode}|${colorCode}|${yakaKodu}`
}

function distinctVariantStockQty (sourceRows) {
  return distinctVariantNumber(sourceRows, 'stock_qty')
}

function distinctVariantNumber (sourceRows, field) {
  const seen = new Set()
  let total = 0
  let keyed = false
  for (const row of sourceRows) {
    const key = productVariantStockKey(row)
    const value = Number(row?.[field] || 0)
    if (!key) continue
    keyed = true
    if (seen.has(key)) continue
    seen.add(key)
    total += value
  }
  return keyed ? total : sumRows(sourceRows, field)
}

function distinctVariantStockCost (sourceRows) {
  const seen = new Set()
  let total = 0
  let keyed = false
  for (const row of sourceRows) {
    const key = productVariantStockKey(row)
    if (!key) continue
    keyed = true
    if (seen.has(key)) continue
    seen.add(key)
    const stockQty = Number(row?.stock_qty || 0)
    const costPrice = Number(row?.cost_price_usd || 0)
    total += stockQty * costPrice
  }
  return keyed ? total : sumRows(sourceRows, 'idle_cost_usd')
}

function distinctCount (sourceRows, field) {
  const values = new Set()
  for (const row of sourceRows) {
    const value = String(row[field] || '').trim()
    if (value && value !== '-') values.add(value)
  }
  return values.size
}

function isDistinctSpreadCountField (field) {
  return /^market_count(_|$)/i.test(field) || /^customer_count(_|$)/i.test(field)
}

function countPeriodSuffix (field) {
  if (field.includes('_180d')) return '180d'
  if (field.includes('_365d')) return '365d'
  if (field.includes('_total')) return 'total'
  return '90d'
}

function salesUsdFieldForSuffix (suffix) {
  return `sales_usd_${suffix}`
}

function distinctSpreadCount (sourceRows, field) {
  const suffix = countPeriodSuffix(field)
  return /^market_count/i.test(field)
    ? distinctRevenueMarketCount(sourceRows, suffix, field)
    : distinctRevenueCustomerCount(sourceRows, suffix, field)
}

function distinctRevenueMarketCount (sourceRows, suffix, fallbackField) {
  const salesField = salesUsdFieldForSuffix(suffix)
  const values = new Set()
  for (const row of sourceRows) {
    if (Number(row?.[salesField] || 0) <= 0) continue
    const marketKeys = stringListFromRow(row, `__market_keys_${suffix}`)
    for (const key of marketKeys) {
      const value = displayMarketName(key)
      if (value && value !== 'STOK' && value !== '-') values.add(value)
    }
    if (marketKeys.length > 0) continue
    const value = displayMarketName(row?.market_key)
    if (value && value !== 'STOK' && value !== '-') values.add(value)
  }
  if (values.size > 0) return values.size
  return sourceRows.reduce((sum, row) => {
    return Number(row?.[salesField] || 0) > 0 ? sum + Number(row?.[fallbackField] || 0) : sum
  }, 0)
}

function distinctRevenueCustomerCount (sourceRows, suffix, fallbackField) {
  const salesField = salesUsdFieldForSuffix(suffix)
  const values = new Set()
  for (const row of sourceRows) {
    if (Number(row?.[salesField] || 0) <= 0) continue
    const customerKeys = stringListFromRow(row, `__customer_keys_${suffix}`)
    for (const code of customerKeys) {
      if (code && code !== '-') values.add(code)
    }
    if (customerKeys.length > 0) continue
    const code = String(row?.customer_code || '').trim()
    if (code && code !== '-') values.add(code)
  }
  if (values.size > 0) return values.size
  return sourceRows.reduce((sum, row) => {
    return Number(row?.[salesField] || 0) > 0 ? sum + Number(row?.[fallbackField] || 0) : sum
  }, 0)
}

function stringListFromRow (row, field) {
  const value = row?.[field]
  if (Array.isArray(value)) return value.map(item => String(item || '').trim()).filter(Boolean)
  const text = String(value || '').trim()
  if (!text) return []
  if (text.startsWith('[')) {
    try {
      const parsed = JSON.parse(text)
      return Array.isArray(parsed) ? parsed.map(item => String(item || '').trim()).filter(Boolean) : []
    } catch {
      return []
    }
  }
  return [text]
}

function distinctSummary (sourceRows, field, fallbackField) {
  const values = new Set()
  for (const row of sourceRows) {
    const value = String(row[field] || (fallbackField ? row[fallbackField] : '') || '').trim()
    if (value && value !== '-') values.add(value)
    if (values.size > 1) return `${values.size} farklı`
  }
  return Array.from(values)[0] || ''
}

function distinctTextSummary (current, next) {
  const left = String(current || '').trim()
  const right = String(next || '').trim()
  if (!right || right === 'STOK') return left
  if (!left) return right
  if (left === right || left === 'Çoklu') return left
  return 'Çoklu'
}

function dominantValue (sourceRows, field) {
  const counts = new Map()
  for (const row of sourceRows) {
    const value = String(row[field] || '').trim()
    if (!value) continue
    counts.set(value, (counts.get(value) || 0) + 1)
  }
  return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0] || ''
}

function ratio (amount, base) {
  const divisor = Number(base || 0)
  if (!divisor) return 0
  return Number(amount || 0) / divisor
}

function averageRows (sourceRows, field) {
  const values = sourceRows.map(row => Number(row[field] || 0)).filter(value => Number.isFinite(value) && value !== 0)
  if (!values.length) return 0
  return values.reduce((sum, value) => sum + value, 0) / values.length
}

function weightedAverage (sourceRows, amountField, qtyField) {
  const qty = sumRows(sourceRows, qtyField)
  if (!qty) return 0
  return sumRows(sourceRows, amountField) / qty
}

function weightedAverageOrAverage (sourceRows, valueField, qtyField) {
  if (/^(performance_score|customer_score)/i.test(valueField)) {
    return weightedAverageScore(sourceRows, valueField, scoreSuffixForMetric(valueField))
  }
  const weightedRows = sourceRows
    .map(row => ({
      value: Number(row[valueField] || 0),
      qty: Number(row[qtyField] || 0)
    }))
    .filter(row => Number.isFinite(row.value) && Number.isFinite(row.qty) && row.qty > 0)
  const qty = weightedRows.reduce((sum, row) => sum + row.qty, 0)
  if (qty > 0) {
    return weightedRows.reduce((sum, row) => sum + row.value * row.qty, 0) / qty
  }
  return 0
}

function scoreSuffixForMetric (field) {
  if (field.includes('_180d')) return '180d'
  if (field.includes('_365d')) return '365d'
  if (field.includes('_total')) return 'total'
  return '90d'
}

function scoreWeight (row, suffix) {
  const salesQty = Number(row?.[`sales_qty_${suffix}`] || 0)
  if (salesQty > 0) return salesQty
  return 0
}

function weightedAverageScore (sourceRows, valueField, suffix) {
  const rows = sourceRows
    .filter(row => Object.prototype.hasOwnProperty.call(row || {}, valueField))
    .map(row => ({
      value: Number(row[valueField] || 0),
      weight: scoreWeight(row, suffix)
    }))
    .filter(row => Number.isFinite(row.value) && Number.isFinite(row.weight))
  const weight = rows.reduce((sum, row) => sum + Math.max(0, row.weight), 0)
  if (weight > 0) {
    return rows.reduce((sum, row) => sum + row.value * Math.max(0, row.weight), 0) / weight
  }
  return rows.length ? rows.reduce((sum, row) => sum + row.value, 0) / rows.length : 0
}

function weightedAverageProductCost (sourceRows, valueField) {
  const salesRows = sourceRows
    .map(row => ({
      value: Number(row[valueField] || 0),
      qty: Number(row.sales_qty_total || 0)
    }))
    .filter(row => Number.isFinite(row.value) && Number.isFinite(row.qty) && row.value > 0 && row.qty > 0)
  const salesQty = salesRows.reduce((sum, row) => sum + row.qty, 0)
  if (salesQty > 0) {
    return salesRows.reduce((sum, row) => sum + row.value * row.qty, 0) / salesQty
  }

  const variants = new Map()
  for (const row of sourceRows) {
    const key = productVariantStockKey(row)
    if (!key || variants.has(key)) continue
    variants.set(key, row)
  }
  const averageSource = variants.size > 0 ? Array.from(variants.values()) : sourceRows
  const values = averageSource
    .map(row => Number(row[valueField] || 0))
    .filter(value => Number.isFinite(value) && value > 0)
  if (!values.length) return 0
  return values.reduce((sum, value) => sum + value, 0) / values.length
}

function marginFromSalesCost (salesUSD, qty, unitCost) {
  const sales = Number(salesUSD || 0)
  if (!sales) return 0
  return (sales - (Number(qty || 0) * Number(unitCost || 0))) / sales
}

function marginFromRows (sourceRows, salesField, qtyField, unitCostField) {
  const sales = sumRows(sourceRows, salesField)
  if (!sales) return 0
  const costAmount = sourceRows.reduce((sum, row) => {
    return sum + Number(row[qtyField] || 0) * Number(row[unitCostField] || 0)
  }, 0)
  return (sales - costAmount) / sales
}

function normalizeGroupValue (value) {
  return String(value || '').trim() || '(Boş)'
}

function shouldSkipGroupValue (field, value) {
  return false
}

function groupCanExpand (rowOrKey) {
  const key = typeof rowOrKey === 'string' ? rowOrKey : rowOrKey?.key
  const depth = groupKeyDepth(key)
  const tabKey = tabKeyFromGroupKey(key)
  const levels = tabGroupLevels[tabKey] || groupLevels
  return depth >= 0 && depth < levels.length - 1
}

function isGroupExpanded (key) {
  if (!groupCanExpand(key)) return false
  const tabKey = tabKeyFromGroupKey(key)
  const depth = groupKeyDepth(key)
  const level = selectedExpandThroughLevelForTab(tabKey)
  return depth >= 0 && level >= depth
}

function groupKeyDepth (key) {
  const parts = String(key || '').split('|').filter(Boolean)
  return Math.max(-1, parts.length - 2)
}

function tabKeyFromGroupKey (key) {
  const first = String(key || '').split('|')[0] || ''
  return first.startsWith('tab:') ? first.slice(4) : activeTab.value
}

function selectedExpandThroughLevelForTab (tabKey) {
  const levels = tabGroupLevels[tabKey] || groupLevels
  const selected = new Set(selectedExpandLevelKeysByTab[tabKey] || [])
  const indexes = levels
    .map((level, index) => selected.has(level.key) ? index : -1)
    .filter(index => index >= 0)
  return indexes.length ? Math.max(...indexes) : -1
}

function activeTabPrefix () {
  return `tab:${activeTab.value}|`
}

function expandedLevelKeysThrough (level) {
  if (level < 0) return []
  const keys = []
  collectGroupKeys(keys, activeGroupSourceRows.value, 0, [`tab:${activeTab.value}`], activeGroupLevels.value, level)
  return keys
}

function replaceActiveTabExpandedGroups (keys) {
  const tabPrefix = activeTabPrefix()
  const next = Object.fromEntries(
    Object.entries(expandedGroups.value).filter(([key]) => !key.startsWith(tabPrefix))
  )
  for (const key of keys) next[key] = true
  expandedGroups.value = next
}

function setSelectedExpandLevelsThrough (level) {
  activeSelectedExpandLevelKeys.value = level < 0
    ? []
    : activeGroupLevels.value.slice(0, level + 1).map(item => item.key)
}

function applyActiveExpansionThroughLevel (level) {
  setSelectedExpandLevelsThrough(level)
  replaceActiveTabExpandedGroups([])
  lastManualExpandedGroupKey.value = ''
  scheduleLoadBackendGroupedRows()
}

function toggleGroup (key) {
  if (!groupCanExpand(key)) return
  const level = groupKeyDepth(key)
  applyActiveExpansionThroughLevel(isGroupExpanded(key) ? level - 1 : level)
}

async function toggleAllProductGroups () {
  if (allProductGroupsExpanded.value) await collapseAllProductGroups()
  else {
    detailLevelMenuOpen.value = false
    applyActiveExpansionThroughLevel(fullExpandThroughLevel.value)
    await nextTick()
  }
}

async function collapseAllProductGroups () {
  if (bulkExpandLoading.value) return
  detailLevelMenuOpen.value = false
  applyActiveExpansionThroughLevel(-1)
  bulkExpandLoading.value = true
  try {
    await nextTick()
  } finally {
    bulkExpandLoading.value = false
  }
}

async function expandSelectedProductGroups () {
  if (bulkExpandLoading.value) return
  detailLevelMenuOpen.value = false
  applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
  bulkExpandLoading.value = true
  try {
    await nextTick()
  } finally {
    bulkExpandLoading.value = false
  }
}

function groupLabelColumnName (row) {
  const field = String(row?.group_field || '')
  if (field === 'color_code' || field === 'yaka_kodu' || field === 'color_yaka') return 'color_yaka'
  return field || 'product_code'
}

function groupDisplayLabel (row) {
  const field = String(row?.group_field || '')
  if (field === 'color_code' || field === 'yaka_kodu' || field === 'color_yaka') {
    return colorYakaValue(row) || row?.label || row?.group_value || ''
  }
  return row?.label || row?.group_value || ''
}

function groupIndentPx (row) {
  return '0px'
}

function hasProductVariantImageKey (row) {
  const product = String(row?.image_product_code || row?.product_code || '').trim()
  const color = String(row?.image_color_code || row?.color_code || '').trim()
  return Boolean(product && color)
}

function canShowProductImage (row) {
  return isProductKpiTab.value && hasProductVariantImageKey(row)
}

function groupShowsImage (row) {
  return isProductKpiTab.value &&
    row?.__group &&
    ['product_code', 'color_code', 'yaka_kodu', 'color_yaka'].includes(String(row.group_field || '')) &&
    hasProductVariantImageKey(row)
}

const groupDimensionFields = new Set(dimensionColumnNames)

function groupRowClasses (row) {
  const level = Math.min(Number(row?.level || 0), 5)
  const field = String(row?.group_field || 'unknown').replace(/[^a-z0-9_-]/gi, '-').toLowerCase()
  return ['group-row', `group-row-level-${level}`, `group-row-field-${field}`]
}

function groupCellClass (colOrName) {
  const name = colOrName?.name || colOrName
  if (name === 'image') return 'product-image-cell'
  if (colOrName?.align === 'right') return 'text-right'
  return /(^|_)(score|qty|count|usd|days|turnover|index|margin)$/.test(name) || ['stock_qty', 'net_stock_after_order'].includes(name)
    ? 'text-right'
    : ''
}

function formatGroupCell (row, colOrName) {
  const name = colOrName?.name || colOrName
  if (name === 'image') return ''
  if (name === 'recommendation') return row.recommendation || ''
  if (name === 'color_yaka' && ['color_code', 'yaka_kodu', 'color_yaka'].includes(String(row?.group_field || ''))) return colorYakaValue(row)
  if (groupDimensionFields.has(name) && name !== row?.group_field) return ''
  return formatTableCell(row, colOrName)
}

function withProductMargins (row) {
  const next = normalizeProductCostFields(row)
  const out = {
    ...next,
    avg_price_usd_365d: Number(next?.sales_qty_365d || 0) > 0 ? Number(next?.sales_usd_365d || 0) / Number(next?.sales_qty_365d || 0) : 0,
    stock_turnover_90d: groupedOrExistingStockTurnover(next, '90d', 90),
    stock_turnover_180d: groupedOrExistingStockTurnover(next, '180d', 180),
    stock_turnover_365d: groupedOrExistingStockTurnover(next, '365d', 360),
    stock_turnover_total: groupedOrExistingStockTurnover(next, 'total', productPerformancePeriodDays(next, 'total'))
  }
  for (const suffix of ['90d', '180d', '365d', 'total']) {
    applyPeriodProfitFields(out, suffix)
  }
  return out
}

function withGeneralMargins (row) {
  const out = normalizeProductCostFields(row)
  applyPeriodProfitFields(out, 'total')
  return {
    ...out,
    stock_turnover_total: groupedOrExistingStockTurnover(out, 'total', productPerformancePeriodDays(out, 'total'))
  }
}

function normalizeProductCostFields (row) {
  const next = { ...row }
  normalizeProductCostPairFields(next, 'cost_price_usd', 'base_price_usd')
  for (const suffix of ['90d', '180d', '365d', 'total']) {
    normalizeProductCostPairFields(next, `cost_price_usd_${suffix}`, `base_price_usd_${suffix}`)
  }
  return next
}

function normalizeProductCostPairFields (row, costField, baseField) {
  const hasCost = Object.prototype.hasOwnProperty.call(row || {}, costField)
  const hasBase = Object.prototype.hasOwnProperty.call(row || {}, baseField)
  if (!hasCost && !hasBase) return
  let costPrice = Number(row?.[costField] || 0)
  let basePrice = Number(row?.[baseField] || 0)
  if (costPrice > 0 && basePrice > 0 && costPrice > basePrice) {
    const tmp = costPrice
    costPrice = basePrice
    basePrice = tmp
  }
  if (hasCost || costPrice > 0) row[costField] = costPrice
  if (hasBase || basePrice > 0) row[baseField] = basePrice
}

function periodCostPair (row, suffix) {
  const costField = `cost_price_usd_${suffix}`
  const baseField = `base_price_usd_${suffix}`
  let costPrice = Number(row?.[costField] || 0)
  let basePrice = Number(row?.[baseField] || 0)
  if (costPrice <= 0) costPrice = Number(row?.cost_price_usd || 0)
  if (basePrice <= 0) basePrice = Number(row?.base_price_usd || 0)
  if (costPrice > 0 && basePrice > 0 && costPrice > basePrice) {
    const tmp = costPrice
    costPrice = basePrice
    basePrice = tmp
  }
  return { costPrice, basePrice }
}

function applyPeriodProfitFields (row, suffix) {
  const qty = Number(row?.[`sales_qty_${suffix}`] || 0)
  const salesUSD = Number(row?.[`sales_usd_${suffix}`] || 0)
  const { costPrice, basePrice } = periodCostPair(row, suffix)
  row[`base_price_usd_${suffix}`] = basePrice
  row[`cost_price_usd_${suffix}`] = costPrice
  if (qty > 0) {
    row[`avg_price_usd_${suffix}`] = salesUSD / qty
    row[`unit_profit_base_${suffix}`] = salesUSD / qty - basePrice
    row[`unit_profit_cost_${suffix}`] = salesUSD / qty - costPrice
    row[`gross_profit_base_usd_${suffix}`] = salesUSD - qty * basePrice
    row[`gross_profit_cost_usd_${suffix}`] = salesUSD - qty * costPrice
    row[`gross_profit_usd_${suffix}`] = row[`gross_profit_cost_usd_${suffix}`]
  } else {
    row[`unit_profit_base_${suffix}`] = 0
    row[`unit_profit_cost_${suffix}`] = 0
    row[`gross_profit_base_usd_${suffix}`] = 0
    row[`gross_profit_cost_usd_${suffix}`] = 0
    row[`gross_profit_usd_${suffix}`] = 0
  }
  row[`gross_margin_base_${suffix}`] = marginFromSalesCost(salesUSD, qty, basePrice)
  row[`gross_margin_cost_${suffix}`] = marginFromSalesCost(salesUSD, qty, costPrice)
}

function stockTurnover (salesQty, stockQty, periodDays) {
  return annualizedStockTurnover(salesQty, stockQty, periodDays)
}

function existingOrStockTurnover (value, salesQty, stockQty, periodDays) {
  const current = Number(value)
  return value !== undefined && value !== null && Number.isFinite(current) ? current : stockTurnover(salesQty, stockQty, periodDays)
}

function groupedOrExistingStockTurnover (row, suffix, periodDays) {
  const salesQty = Number(row?.[`sales_qty_${suffix}`] || 0)
  const avgStock = Number(row?.[`avg_stock_${suffix}`] || 0)
  const stockQty = Number(row?.stock_qty || 0)
  if (row?.__group) {
    const current = Number(row?.[`stock_turnover_${suffix}`])
    if (Number.isFinite(current)) return current
    if (avgStock > 0) return stockTurnover(salesQty, avgStock, periodDays)
    return 0
  }
  const stockBase = avgStock > 0 ? avgStock : stockQty
  return existingOrStockTurnover(row?.[`stock_turnover_${suffix}`], salesQty, stockBase, periodDays)
}

function filterKey (tableKey, name) {
  return `${tableKey}:${name}`
}

function isColumnFilterable (name) {
  return name !== 'image'
}

function columnFilterOptions (tableKey, name) {
  if (backendGroupedSupportedTab(tableKey) && !backendGroupedFilterFields.has(name)) return []
  const options = columnFilterOptionMap.value[tableKey]?.[name] || []
  const search = String(columnFilterSearch[filterKey(tableKey, name)] || '').trim().toLocaleLowerCase('tr')
  if (!search) return options
  return options.filter(option => String(option.label || '').toLocaleLowerCase('tr').includes(search))
}

function hasColumnFilter (tableKey, name) {
  return selectedColumnFilters(tableKey, name).length > 0
}

function getColumnFilterBadgeValue (tableKey, name) {
  return selectedColumnFilters(tableKey, name).length
}

function selectedColumnFilters (tableKey, name) {
  const selected = columnFilters[filterKey(tableKey, name)]
  return Array.isArray(selected) ? selected : []
}

function hasActiveTableFilters (tableKey) {
  const prefix = `${tableKey}:`
  return Object.entries(columnFilters).some(([key, value]) => key.startsWith(prefix) && Array.isArray(value) && value.length > 0)
}

function isColumnFilterValueSelected (tableKey, name, value) {
  return selectedColumnFilters(tableKey, name).includes(value)
}

function toggleColumnFilterValue (tableKey, name, value) {
  runWithFilterBusy(() => {
    const key = filterKey(tableKey, name)
    const selected = selectedColumnFilters(tableKey, name)
    columnFilters[key] = selected.includes(value)
      ? selected.filter(item => item !== value)
      : [...selected, value]
  })
}

function clearColumnFilter (tableKey, name) {
  runWithFilterBusy(() => {
    columnFilters[filterKey(tableKey, name)] = []
  })
}

function selectAllColumnFilterOptions (tableKey, name) {
  runWithFilterBusy(() => {
    columnFilters[filterKey(tableKey, name)] = columnFilterOptions(tableKey, name).map(option => option.value)
  })
}

function runWithFilterBusy (apply) {
  apply()
  syncExpansionAfterFilterChange()
}

function syncExpansionAfterFilterChange () {
  if (!backendGroupedSupportedTab(activeTab.value)) return
  const level = selectedExpandThroughLevel.value
  applyActiveExpansionThroughLevel(level >= 0 ? autoExpandThroughLevel.value : -1)
}

function filterRowsForTable (tableKey, sourceRows, sourceColumns) {
  const activeFilters = sourceColumns
    .filter(col => isColumnFilterable(col.name))
    .map(col => ({
      col,
      selected: new Set(selectedColumnFilters(tableKey, col.name))
    }))
    .filter(item => item.selected.size > 0)
  if (!activeFilters.length) return sourceRows
  return sourceRows.filter(row => activeFilters.every(item => item.selected.has(columnFilterValue(row, item.col))))
}

function columnFilterValue (row, col) {
  return String(rawColumnCellValue(row, col) ?? '').trim()
}

function columnFilterLabel (row, col) {
  return formattedColumnCellValue(row, col) || columnFilterValue(row, col)
}

function colorYakaValue (row) {
  const groupField = String(row?.group_field || '')
  const groupValue = String(row?.group_value || row?.label || '').trim()
  const color = String(row?.image_color_code || row?.color_code || '').trim()
  const colorDescription = String(row?.image_color_description || row?.color_description || '').trim()
  const yaka = String(row?.image_yaka_kodu || row?.yaka_kodu || '').trim()
  const colorLabel = colorDisplayLabel(groupField === 'color_code' ? groupValue || color : color, colorDescription)
  if (row?.__group && groupField === 'color_yaka') {
    const parts = groupValue.split('/').map(part => part.trim()).filter(Boolean)
    const groupColor = parts[0] || ''
    const groupYaka = parts.slice(1).join('/')
    return [colorDisplayLabel(groupColor, colorDescription), groupYaka].filter(value => value && value !== '-').join('/')
  }
  if (row?.__group && groupField === 'color_code') return colorLabel
  if (row?.__group && groupField === 'yaka_kodu') {
    const parentColor = colorLabel && colorLabel !== '-' ? colorLabel : ''
    const groupYaka = groupValue && groupValue !== '-' ? groupValue : yaka
    return [parentColor, groupYaka].filter(Boolean).join('/')
  }
  return [colorLabel, yaka].filter(value => value && value !== '-').join('/')
}

function colorDisplayLabel (code, description) {
  const colorCode = String(code || '').trim()
  const desc = String(description || '').trim()
  if (!colorCode) return ''
  if (!desc) return colorCode
  return `${colorCode}-${desc.toLocaleUpperCase('tr-TR')}`
}

function colorYakaGroupLabel (groupField, groupValue, colorSummary, yakaSummary) {
  const field = String(groupField || '')
  const value = String(groupValue || '').trim()
  if (field === 'color_code') return value
  if (field === 'yaka_kodu') {
    const color = String(colorSummary || '').trim()
    const yaka = value || String(yakaSummary || '').trim()
    return [color, yaka].filter(part => part && part !== '-').join('/')
  }
  return value
}

function productCategoryValue (row) {
  return row?.kategori || ''
}

function productFirstGroupValue (row) {
  return cleanProductFirstGroupValue(row?.urun_ilk_grubu)
}

function productMainGroupValue (row) {
  const direct = String(row?.urun_ana_grubu || '').trim()
  if (direct) return direct
  if (row?.__group && row.group_field === 'urun_ana_grubu') {
    const groupValue = String(row.group_value || row.label || '').trim()
    if (groupValue) return groupValue
  }
  return String(row?.urun_alt_grubu || row?.kategori || '').trim()
}

function cleanOptionalGroupValue (value) {
  const text = String(value || '').trim()
  return text === '-' ? '' : text
}

function cleanProductFirstGroupValue (value) {
  const text = String(value || '').trim()
  if (text === '-') return ''
  const normalized = text
    .toLocaleUpperCase('tr-TR')
    .replaceAll('İ', 'I')
    .replaceAll('Ş', 'S')
    .replaceAll('Ğ', 'G')
    .replaceAll('Ü', 'U')
    .replaceAll('Ö', 'O')
    .replaceAll('Ç', 'C')
  return ['YETISKIN', 'YETISKIN/GARSON', 'GARSON'].includes(normalized) ? '' : text
}

function rawProductCellValue (row, name) {
  switch (name) {
    case 'color_yaka': return colorYakaValue(row)
    case 'kategori': return productCategoryValue(row)
    case 'urun_ilk_grubu': return productFirstGroupValue(row)
    case 'askili_yan': return cleanOptionalGroupValue(row?.askili_yan)
    case 'urun_ana_grubu': return productMainGroupValue(row)
    case 'market_key': return displayMarketName(row.market_key)
    case 'performance_bucket': return bucketLabel(row.performance_bucket)
    default: return row[name]
  }
}

function rawColumnCellValue (row, col) {
  const name = col?.name || col
  switch (name) {
    case 'color_yaka': return colorYakaValue(row)
    case 'kategori': return productCategoryValue(row)
    case 'urun_ilk_grubu': return productFirstGroupValue(row)
    case 'askili_yan': return cleanOptionalGroupValue(row?.askili_yan)
    case 'urun_ana_grubu': return productMainGroupValue(row)
    case 'market_key': return displayMarketName(row.market_key)
    case 'performance_bucket': return bucketLabel(row.performance_bucket)
    case 'is_overdue': return row.is_overdue ? 'Evet' : 'Hayır'
    default:
      if (row[name] !== undefined && row[name] !== null) return row[name]
      if (typeof col?.field === 'function') return col.field(row)
      if (typeof col?.field === 'string') return row[col.field]
      return ''
  }
}

function attachColumnSorts (targetColumns) {
  targetColumns.forEach(col => {
    if (!col || col.name === 'image') return
    col.sortable = col.sortable !== false
    col.sort = (_a, _b, rowA, rowB) => compareColumnSortValues(sortValueForColumn(rowA, col), sortValueForColumn(rowB, col))
  })
}

function sortValueForColumn (row, col) {
  const name = col?.name || col
  const raw = rawColumnCellValue(row, col)
  const direct = row?.[name]
  if (typeof direct === 'number' && Number.isFinite(direct)) return direct
  if (typeof raw === 'number' && Number.isFinite(raw)) return raw
  if (direct instanceof Date) return direct.getTime()
  if (raw instanceof Date) return raw.getTime()
  if (typeof direct === 'boolean') return direct ? 1 : 0
  if (typeof raw === 'boolean') return raw ? 1 : 0

  if (isNumericSortColumn(name, col)) {
    const numeric = parseSortNumber(direct ?? raw)
    return Number.isFinite(numeric) ? numeric : 0
  }

  return String(raw ?? '').trim().toLocaleLowerCase('tr-TR')
}

function compareColumnSortValues (left, right) {
  const leftNumber = typeof left === 'number' && Number.isFinite(left)
  const rightNumber = typeof right === 'number' && Number.isFinite(right)
  if (leftNumber && rightNumber) return left - right
  return String(left ?? '').localeCompare(String(right ?? ''), 'tr', { numeric: true, sensitivity: 'base' })
}

function isNumericSortColumn (name, col) {
  return col?.align === 'right' || isMoneyField(name) || isPercentField(name) || isIntegerField(name) || isDecimalField(name)
}

function parseSortNumber (value) {
  if (typeof value === 'number') return value
  if (value === null || value === undefined || value === '') return NaN
  const text = String(value).trim()
  if (!text) return NaN
  const normalized = text
    .replace(/\s/g, '')
    .replace(/[%$\u20ba\u20ac\u00a3]/g, '')
    .replace(/\.(?=\d{3}(\D|$))/g, '')
    .replace(/,/g, '.')
    .replace(/[^\d.-]/g, '')
  return Number(normalized)
}

function formattedColumnCellValue (row, col) {
  if (typeof col?.field === 'function') return col.field(row)
  return formatProductCell(row, col?.name || col)
}

function formatTableCell (row, colOrName) {
  const col = typeof colOrName === 'object' ? colOrName : null
  const name = col?.name || colOrName
  if (name === 'image') return ''
  if (name === 'color_yaka') return colorYakaValue(row)
  if (['kategori', 'urun_ilk_grubu', 'urun_ana_grubu'].includes(name)) return rawProductCellValue(row, name) || ''
  if (name === 'market_key') return displayMarketName(row.market_key)
  if (name === 'performance_bucket') return bucketLabel(row.performance_bucket)
  if (name === 'is_overdue') return row.is_overdue ? 'Evet' : 'Hayır'
  if (isMoneyField(name)) return formatMoney(row[name], 'USD')
  if (isPercentField(name)) return formatPercent(row[name])
  if (isIntegerField(name)) return formatNumber(row[name], 0)
  if (isDecimalField(name)) return formatNumber(row[name], 2)
  if (typeof col?.field === 'function') return col.field(row)
  return formatProductCell(row, name)
}

function isMoneyField (name) {
  return /(^|_)(usd|cost_value_usd|value_usd)$/.test(name) ||
    /(price_usd|cost_usd|profit.*usd|sales_usd|order_usd|base_price_usd|cost_price_usd|idle_cost_usd|stock_cost_value_usd|risk_stock_cost_value_usd)/.test(name)
}

function isPercentField (name) {
  return /(margin|gross_margin|expected_margin)/.test(name)
}

function isIntegerField (name) {
  return /(qty|count|stock_qty|line_count|invoice_count|order_count|product_count|market_count|customer_count|overdue_qty|net_stock_after_order)$/.test(name)
}

function isDecimalField (name) {
  return /(days|turnover|index|score|avg_daily)/.test(name)
}

function formatProductCell (row, name) {
  switch (name) {
    case 'stock_qty':
    case 'sales_qty_90d':
    case 'sales_qty_180d':
    case 'sales_qty_365d':
    case 'sales_qty_total':
    case 'market_count_90d':
    case 'customer_count_90d':
    case 'market_count_total':
    case 'customer_count_total':
      return formatNumber(row[name], 0)
    case 'stock_days_90d':
    case 'stock_days_180d':
    case 'stock_days_total':
    case 'stock_turnover_90d':
    case 'stock_turnover_180d':
    case 'stock_turnover_365d':
    case 'stock_turnover_total':
    case 'sales_index_90d':
    case 'sales_index_total':
    case 'performance_score':
      return formatNumber(row[name], 2)
    case 'sales_usd_90d':
    case 'sales_usd_180d':
    case 'sales_usd_365d':
    case 'sales_usd_total':
    case 'avg_price_usd_90d':
    case 'avg_price_usd_180d':
    case 'avg_price_usd_365d':
    case 'avg_price_usd_total':
    case 'base_price_usd':
    case 'cost_price_usd':
    case 'unit_profit_base_90d':
    case 'unit_profit_cost_90d':
    case 'unit_profit_base_180d':
    case 'unit_profit_cost_180d':
    case 'unit_profit_base_total':
    case 'unit_profit_cost_total':
    case 'gross_profit_usd_90d':
    case 'gross_profit_usd_180d':
    case 'gross_profit_usd_total':
    case 'gross_profit_base_usd_90d':
    case 'gross_profit_cost_usd_90d':
    case 'gross_profit_base_usd_180d':
    case 'gross_profit_cost_usd_180d':
    case 'gross_profit_base_usd_365d':
    case 'gross_profit_cost_usd_365d':
    case 'gross_profit_base_usd_total':
    case 'gross_profit_cost_usd_total':
      return formatMoney(row[name], 'USD')
    case 'gross_margin_base_90d':
    case 'gross_margin_cost_90d':
    case 'gross_margin_base_180d':
    case 'gross_margin_cost_180d':
    case 'gross_margin_base_365d':
    case 'gross_margin_cost_365d':
    case 'gross_margin_base_total':
    case 'gross_margin_cost_total':
    case 'gross_margin_90d':
    case 'gross_margin_180d':
      return formatPercent(row[name])
    case 'kategori':
      return productCategoryValue(row)
    case 'urun_ilk_grubu':
      return productFirstGroupValue(row)
    case 'urun_ana_grubu':
      return productMainGroupValue(row)
    case 'market_key':
      return displayMarketName(row.market_key)
    default:
      return rawProductCellValue(row, name) || ''
  }
}

function normalizeRow (row) {
  const product = String(row?.product_code || '').trim()
  const color = String(row?.color_code || '').trim()
  const yaka = String(row?.yaka_kodu || '').trim()
  const market = String(row?.market_key || '').trim()
  return withPeriodScores(withGeneralMargins(withProductMargins({
    ...row,
    row_key: `${product}|${color}|${yaka}|${market}`
  })))
}

function withPeriodScores (row) {
  const next = { ...row }
  if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_90d')) next.performance_score_90d = productSalesPeriodScore(productPeriodMetricSource(next, '90d'))
  if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_180d')) next.performance_score_180d = productSalesPeriodScore(productPeriodMetricSource(next, '180d'))
  if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_365d')) next.performance_score_365d = productSalesPeriodScore(productPeriodMetricSource(next, '365d'))
  if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_total')) next.performance_score_total = productSalesPeriodScore(productPeriodMetricSource(next, 'total'))
  if (!Object.prototype.hasOwnProperty.call(next, 'performance_score')) next.performance_score = next.performance_score_90d
  return next
}

function performanceVariantKey (row) {
  return [
    String(row?.product_code || '').trim(),
    String(row?.color_code || '').trim(),
    String(row?.yaka_kodu || '').trim(),
    displayMarketName(row?.market_key)
  ].join('|')
}

function generalMetricFields (row) {
  return {
    period_start: row?.period_start || '',
    period_end: row?.period_end || '',
    sales_qty_total: Number(row?.sales_qty_total || 0),
    sales_usd_total: Number(row?.sales_usd_total || 0),
    avg_daily_sales_total: Number(row?.avg_daily_sales_total || 0),
    stock_days_total: Number(row?.stock_days_total || 0),
    avg_price_usd_total: Number(row?.avg_price_usd_total || 0),
    gross_profit_usd_total: Number(row?.gross_profit_usd_total || 0),
    gross_margin_total: Number(row?.gross_margin_total || 0),
    unit_profit_cost_total: Number(row?.unit_profit_cost_total || 0),
    unit_profit_base_total: Number(row?.unit_profit_base_total || 0),
    market_count_total: Number(row?.market_count_total || 0),
    customer_count_total: Number(row?.customer_count_total || 0),
    invoice_count_total: Number(row?.invoice_count_total || 0),
    sales_index_total: Number(row?.sales_index_total || 0)
  }
}

function normalizeGeneralRow (row) {
  const product = String(row?.product_code || '').trim()
  const color = String(row?.color_code || '').trim()
  const yaka = String(row?.yaka_kodu || '').trim()
  const market = String(row?.market_key || '').trim()
  return withPeriodScores(withGeneralMargins({
    ...row,
    row_key: `general|${product}|${color}|${yaka}|${market}`
  }))
}

function normalizeOrderAnalysisRow (row) {
  const product = String(row?.product_code || '').trim()
  const color = String(row?.color_code || '').trim()
  const yaka = String(row?.yaka_kodu || '').trim()
  const market = String(row?.market_key || '').trim()
  return withOrderPeriodScores({
    ...row,
    row_key: `orders|${product}|${color}|${yaka}|${market}`
  })
}

function normalizeOrderGroupRow (row) {
  const key = String(row?.group_key || '').trim()
  const mode = String(row?.breakdown || '').trim()
  return withOrderPeriodScores({
    ...row,
    row_key: `order-group|${mode}|${key}|${row?.market_key || ''}|${row?.customer_code || ''}`
  })
}

function normalizeOrderProductCustomerRow (row) {
  const product = String(row?.product_code || '').trim()
  const color = String(row?.color_code || '').trim()
  const yaka = String(row?.yaka_kodu || '').trim()
  const market = String(row?.market_key || '').trim()
  const customer = String(row?.customer_code || '').trim()
  return withOrderPeriodScores({
    ...row,
    row_key: `order-product-customer|${product}|${color}|${yaka}|${market}|${customer}`
  })
}

function normalizeOrderMarketDetailRow (row) {
  return withOrderPeriodScores({
    ...row,
    row_key: `order-market-detail|${row?.market_key || ''}|${row?.customer_code || ''}|${row?.order_number || ''}|${row?.product_code || ''}|${row?.color_code || ''}|${row?.yaka_kodu || ''}`
  })
}

function withOrderPeriodScores (row) {
  const next = { ...row }
  const score = Number(next.performance_score || orderGroupPerformanceScore(next))
  next.performance_score_90d = Number(next.performance_score_90d || score)
  next.performance_score_180d = Number(next.performance_score_180d || score)
  next.performance_score_365d = Number(next.performance_score_365d || score)
  next.performance_score_total = Number(next.performance_score_total || score)
  next.performance_score = score
  return next
}

function normalizeSalesBreakdownRow (row) {
  const next = withPeriodScores({ ...row })
  next.customer_score_90d = Number(next.customer_score_90d || customerSalesPeriodScore(periodMetricSource(next, '90d')))
  next.customer_score_180d = Number(next.customer_score_180d || customerSalesPeriodScore(periodMetricSource(next, '180d')))
  next.customer_score_365d = Number(next.customer_score_365d || customerSalesPeriodScore(periodMetricSource(next, '365d')))
  next.customer_score_total = Number(next.customer_score_total || customerSalesPeriodScore(periodMetricSource(next, 'total')))
  return {
    ...next,
    row_key: `sales-breakdown|${row?.breakdown || ''}|${row?.product_code || ''}|${row?.color_code || ''}|${row?.yaka_kodu || ''}|${row?.country || ''}|${row?.customer_segment || ''}|${row?.market_key || ''}|${row?.customer_code || ''}`
  }
}

function orderGroupLabel (row) {
  if (row?.breakdown === 'customer') {
    return [row.customer_code, row.customer_name].filter(Boolean).join(' - ') || '-'
  }
  return row?.group_key || row?.market_key || '-'
}

function productImageKey (row) {
  if (!hasProductVariantImageKey(row)) return ''
  const product = String(row?.image_product_code || row?.product_code || '').trim()
  const color = String(row?.image_color_code || row?.color_code || '').trim()
  const yaka = String(row?.image_yaka_kodu || row?.yaka_kodu || '').trim()
  return `${product}|${color}|${yaka}`
}

function productVariantMetricKey (row) {
  const product = String(row?.image_product_code || row?.product_code || '').trim()
  if (!product) return ''
  const color = String(row?.image_color_code || row?.color_code || '').trim()
  const yaka = String(row?.image_yaka_kodu || row?.yaka_kodu || '').trim()
  return `${product}|${color}|${yaka}`
}

function displayMarketName (value) {
  const parts = String(value || '').split('|').map(part => part.trim()).filter(Boolean)
  return parts.length ? parts[parts.length - 1] : ''
}

function resolveProductImageUrl (item) {
  return resolveProductImageUrls(item)[0] || ''
}

function resolveProductImageUrls (item) {
  if (!item || typeof item !== 'object') return []
  const urls = []
  const addURL = value => {
    const url = String(value || '').trim()
    if (url && !urls.includes(url)) urls.push(url)
  }

  const thumbURL = String(item.thumb_url || item.thumbUrl || '').trim()
  addURL(thumbURL)

  const fullURL = String(item.full_url || item.fullUrl || '').trim()
  addURL(fullURL)

  const contentURL = String(item.content_url || item.ContentURL || '').trim()
  if (contentURL.startsWith('/api/')) addURL(contentURL)
  else if (contentURL.startsWith('/')) addURL(`/api${contentURL}`)
  return urls
}

async function fetchProductImagesForRow (row) {
  if (!hasProductVariantImageKey(row)) return []
  const key = productImageKey(row)
  if (imageFailedKeys.value.has(key)) return []
  if (Object.prototype.hasOwnProperty.call(imageListByKey.value, key)) {
    return imageListByKey.value[key]
  }
  if (performanceStore.hasImageKey(key)) {
    const urls = performanceStore.imageList(key)
    imageListByKey.value = { ...imageListByKey.value, [key]: urls }
    imageUrlByKey.value = { ...imageUrlByKey.value, [key]: urls[0] || '' }
    return urls
  }

  const code = String(row?.image_product_code || row?.product_code || '').trim()
  if (!code) return []

  const color = String(row?.image_color_code || row?.color_code || '').trim()
  const yaka = String(row?.image_yaka_kodu || row?.yaka_kodu || '').trim()

  try {
    const resp = await api.get('/product-images', {
      params: {
        code,
        dim1: color || undefined,
        dim3: yaka || undefined
      },
      timeout: 60000
    })
    const list = Array.isArray(resp?.data) ? resp.data : []
    const urls = list.flatMap(resolveProductImageUrls).filter(Boolean)
    performanceStore.setImageCache(key, urls)
    if (urls.length && imageFailedKeys.value.has(key)) {
      const nextFailed = new Set(imageFailedKeys.value)
      nextFailed.delete(key)
      imageFailedKeys.value = nextFailed
    }
    imageListByKey.value = { ...imageListByKey.value, [key]: urls }
    imageUrlByKey.value = { ...imageUrlByKey.value, [key]: urls[0] || '' }
    return urls
  } catch {
    performanceStore.setImageCache(key, [])
    imageListByKey.value = { ...imageListByKey.value, [key]: [] }
    imageUrlByKey.value = { ...imageUrlByKey.value, [key]: '' }
    return []
  }
}

function getCachedProductImageUrl (row) {
  if (!canShowProductImage(row)) return ''
  const key = productImageKey(row)
  if (imageFailedKeys.value.has(key)) return ''
  const url = imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
  if (!url) queueVisibleProductImage(row)
  return url
}

function queueVisibleProductImage (row) {
  const key = productImageKey(row)
  if (!key || imageLoadingKeys.value.has(key) || imageFailedKeys.value.has(key)) return
  if (Object.prototype.hasOwnProperty.call(imageListByKey.value, key) || performanceStore.hasImageKey(key)) return
  imageLoadingKeys.value = new Set([...imageLoadingKeys.value, key])
  void fetchProductImagesForRow(row).finally(() => {
    const next = new Set(imageLoadingKeys.value)
    next.delete(key)
    imageLoadingKeys.value = next
  })
}

function markProductImageFailed (row, failedUrl = '') {
  const key = productImageKey(row)
  if (!key) return
  const currentUrl = String(failedUrl || imageUrlByKey.value[key] || performanceStore.imageUrl(key) || '').trim()
  const list = imageListByKey.value[key] || performanceStore.imageList(key) || []
  const nextList = Array.isArray(list) ? list.filter(url => url && !sameImageUrl(url, currentUrl)) : []
  if (nextList.length) {
    performanceStore.setImageCache(key, nextList)
    imageListByKey.value = { ...imageListByKey.value, [key]: nextList }
    imageUrlByKey.value = { ...imageUrlByKey.value, [key]: nextList[0] || '' }
    return
  }
  imageFailedKeys.value = new Set([...imageFailedKeys.value, key])
  performanceStore.setImageCache(key, [])
  imageListByKey.value = { ...imageListByKey.value, [key]: [] }
  imageUrlByKey.value = { ...imageUrlByKey.value, [key]: '' }
}

function sameImageUrl (left, right) {
  const a = String(left || '').trim()
  const b = String(right || '').trim()
  if (!a || !b) return false
  if (a === b) return true
  try {
    return new URL(a, window.location.origin).href === new URL(b, window.location.origin).href
  } catch {
    return a.endsWith(b) || b.endsWith(a)
  }
}

function markDialogImageFailed (url) {
  const failedUrl = String(url || '').trim()
  if (!failedUrl) return
  imageDialogUrls.value = imageDialogUrls.value.filter(item => item && item !== failedUrl)
  if (imageSlide.value >= imageDialogUrls.value.length) imageSlide.value = 0
  if (imageDialogRow.value) markProductImageFailed(imageDialogRow.value, failedUrl)
}

async function primeProductImages (sourceRows) {
  if (!isProductKpiTab.value) return
  imagePrimeActiveRequests += 1
  imagePrimeLoading.value = true
  const items = []
  const seen = new Set()
  try {
    for (const row of sourceRows || []) {
      if (!canShowProductImage(row)) continue
      const key = productImageKey(row)
      const code = String(row?.image_product_code || row?.product_code || '').trim()
      if (!key || !code || imageFailedKeys.value.has(key) || seen.has(key) || Object.prototype.hasOwnProperty.call(imageListByKey.value, key) || performanceStore.hasImageKey(key)) continue
      seen.add(key)
      items.push({
        key,
        code,
        dim1: String(row?.image_color_code || row?.color_code || '').trim(),
        dim3: String(row?.image_yaka_kodu || row?.yaka_kodu || '').trim()
      })
      if (items.length >= 160) break
    }
    if (!items.length) return

    const batch = await performanceStore.fetchImageBatch(items)
    const nextLists = { ...imageListByKey.value }
    const nextUrls = { ...imageUrlByKey.value }
    for (const item of items) {
      const urls = batch?.lists?.[item.key] || performanceStore.imageList(item.key) || []
      if (!urls.length) imageFailedKeys.value = new Set([...imageFailedKeys.value, item.key])
      nextLists[item.key] = urls
      nextUrls[item.key] = imageFailedKeys.value.has(item.key) ? '' : (batch?.urls?.[item.key] || urls[0] || '')
    }
    imageListByKey.value = nextLists
    imageUrlByKey.value = nextUrls
  } catch {
    await Promise.all(items.slice(0, 24).map(item => fetchProductImagesForRow({
      product_code: item.code,
      color_code: item.dim1,
      yaka_kodu: item.dim3
    })))
  } finally {
    imagePrimeActiveRequests = Math.max(0, imagePrimeActiveRequests - 1)
    imagePrimeLoading.value = imagePrimeActiveRequests > 0
  }
}

async function openProductImageDialog (row) {
  imageDialogRow.value = row
  const urls = await fetchProductImagesForRow(row)
  imageDialogUrls.value = urls
  imageSlide.value = 0
  imageDialogTitle.value = String(row?.image_product_code || row?.product_code || '-')
  imageDialogSubtitle.value = [
    row?.item_description,
    row?.image_color_code || row?.color_code,
    row?.image_yaka_kodu || row?.yaka_kodu
  ].filter(Boolean).join(' | ')
  imageDialog.value = true
  await loadProductDetailRows(row)
}

async function openPerformanceDialog (row) {
  performanceDialogRow.value = row
  performanceDialogTitle.value = String(row?.product_code || '-')
  performanceDialogSubtitle.value = [
    row?.item_description,
    row?.color_code,
    row?.yaka_kodu,
    row?.market_key
  ].filter(Boolean).join(' | ')
  performanceDialog.value = true
  await loadProductDetailRows(row)
}

async function loadProductDetailRows (row) {
  detailLoading.value = true
  detailSalesRows.value = []
  detailStockSizes.value = []
  try {
    const params = {
      product_code: row?.image_product_code || row?.product_code || '',
      color_code: row?.image_color_code || row?.color_code || '',
      yaka_kodu: row?.image_yaka_kodu || row?.yaka_kodu || ''
    }
    if (!params.product_code) return
    const [salesResp, stockResp] = await Promise.all([
      api.get('/pricing/product-performance/sales-details', { params: { ...params, limit: 500 }, timeout: 60000 }),
      api.get('/pricing/product-performance/stock-sizes', { params, timeout: 60000 })
    ])
    detailSalesRows.value = (Array.isArray(salesResp?.data) ? salesResp.data : []).map((item, idx) => ({
      ...item,
      detail_key: `${item.sales_date}|${item.ref_number}|${item.customer_code}|${idx}`
    })).sort((a, b) => Number(b.sales_usd || 0) - Number(a.sales_usd || 0))
    detailStockSizes.value = Array.isArray(stockResp?.data) ? stockResp.data : []
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ürün detay analizi alınamadı' })
  } finally {
    detailLoading.value = false
  }
}

function backendGroupedSupportedTab (tabKey = activeTab.value) {
  return tabKey === 'products' ||
    tabKey === 'product_detail' ||
    tabKey === 'idle' ||
    tabKey === 'order_product_customers' ||
    tabKey === 'order_market_details' ||
    salesBreakdownTabKeys.includes(tabKey)
}

function activeExpandedGroupKeys () {
  const tabPrefix = `tab:${activeTab.value}|`
  const validFields = new Set(activeGroupLevels.value.map(level => level.key))
  const maxKeys = maxBackendExpandedKeysForTab(activeTab.value)
  const keys = Object.entries(expandedGroups.value)
    .filter(([, value]) => value === true)
    .map(([key]) => key)
    .filter(key => key.startsWith(tabPrefix))
    .filter(key => key.split('|').slice(1).every(part => validFields.has(String(part).split(':')[0])))
    .sort((a, b) => {
      const depthDiff = a.split('|').length - b.split('|').length
      if (depthDiff !== 0) return depthDiff
      return a.localeCompare(b, 'tr', { numeric: true })
    })
  const manualKey = lastManualExpandedGroupKey.value
  const out = []
  if (manualKey && keys.includes(manualKey)) out.push(manualKey)
  for (const key of keys) {
    if (out.length >= maxKeys) break
    if (key !== manualKey) out.push(key)
  }
  return out
}

function pruneActiveExpandedGroups () {
  const tabPrefix = `tab:${activeTab.value}|`
  const allowed = new Set(activeExpandedGroupKeys())
  const next = {}
  for (const [key, value] of Object.entries(expandedGroups.value)) {
    if (!value) continue
    if (key.startsWith(tabPrefix) && !allowed.has(key)) continue
    next[key] = value
  }
  expandedGroups.value = next
}

function scheduleLoadBackendGroupedRows () {
  if (backendGroupedSupportedTab(activeTab.value)) backendGroupedLoading.value = true
  if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
  backendGroupedTimer = window.setTimeout(() => {
    void loadBackendGroupedRows()
  }, 260)
}

function cleanupTopScrollbarSync () {
  if (activeTableMiddleEl) {
    activeTableMiddleEl.removeEventListener('scroll', syncTopScrollbarFromTable)
  }
  if (topScrollbarRef.value) {
    topScrollbarRef.value.removeEventListener('scroll', syncTableFromTopScrollbar)
  }
  if (topScrollbarResizeObserver) {
    topScrollbarResizeObserver.disconnect()
    topScrollbarResizeObserver = null
  }
  activeTableMiddleEl = null
}

function updateTopScrollbarSize () {
  const outer = topScrollbarRef.value
  const inner = topScrollbarInnerRef.value
  const tableMiddle = activeTableMiddleEl
  if (!outer || !inner || !tableMiddle) return
  inner.style.width = `${tableMiddle.scrollWidth}px`
  outer.scrollLeft = tableMiddle.scrollLeft
  outer.style.display = tableMiddle.scrollWidth > tableMiddle.clientWidth + 2 ? 'block' : 'none'
}

function syncTopScrollbarFromTable () {
  if (topScrollbarSyncing || !topScrollbarRef.value || !activeTableMiddleEl) return
  topScrollbarSyncing = true
  topScrollbarRef.value.scrollLeft = activeTableMiddleEl.scrollLeft
  topScrollbarSyncing = false
}

function syncTableFromTopScrollbar () {
  if (topScrollbarSyncing || !topScrollbarRef.value || !activeTableMiddleEl) return
  topScrollbarSyncing = true
  activeTableMiddleEl.scrollLeft = topScrollbarRef.value.scrollLeft
  topScrollbarSyncing = false
}

function setupTopScrollbarSync () {
  if (typeof document === 'undefined') return
  void nextTick(() => {
    cleanupTopScrollbarSync()
    activeTableMiddleEl = document.querySelector('.product-performance-page .performance-table .q-table__middle')
    if (!activeTableMiddleEl || !topScrollbarRef.value) return
    activeTableMiddleEl.addEventListener('scroll', syncTopScrollbarFromTable, { passive: true })
    topScrollbarRef.value.addEventListener('scroll', syncTableFromTopScrollbar, { passive: true })
    if (typeof ResizeObserver !== 'undefined') {
      topScrollbarResizeObserver = new ResizeObserver(updateTopScrollbarSize)
      topScrollbarResizeObserver.observe(activeTableMiddleEl)
      const table = activeTableMiddleEl.querySelector('table')
      if (table) topScrollbarResizeObserver.observe(table)
    }
    updateTopScrollbarSize()
    window.setTimeout(updateTopScrollbarSize, 120)
  })
}

function switchPerformanceTab (tabName) {
  if (!tabName || tabName === activeTab.value || loading.value) return
  activeTab.value = tabName
}

async function loadBackendGroupedRows (options = {}) {
  const tabKey = activeTab.value
  if (!backendGroupedSupportedTab(tabKey)) return
  const filterState = backendGroupedFilterState(tabKey)
  if (filterState.unsupported) {
    backendGroupedLoading.value = false
    return
  }
  if (tabKey === 'product_detail' && !ensureDetailMainGroupSelection()) {
    backendGroupedRows.value = {
      ...backendGroupedRows.value,
      product_detail: []
    }
    backendGroupedLoading.value = false
    return
  }
  void ensureBackendGroupedFilterOptions(tabKey)
  pruneActiveExpandedGroups()
  backendGroupedLoading.value = true
  const filtersKey = JSON.stringify(filterState.filters)
  const expandThroughLevel = selectedExpandThroughLevel.value
  const sortState = tableSortState(tabKey)
  const requestKey = `${tabKey}|${expandThroughLevel}|${selectedDetailMainGroup.value || ''}|${filtersKey}|${sortState.sortBy}|${sortState.descending ? 1 : 0}`
  const params = {
    mode: tabKey,
    groupLevels: activeGroupLevels.value.map(level => level.key),
    expandedKeys: activeExpandedGroupKeys(),
    expandThroughLevel,
    limit: tabKey === 'products' || tabKey === 'product_detail' || tabKey === 'idle' ? productKpiFetchLimit : 50000,
    filters: filterState.filters,
    sortBy: sortState.sortBy,
    descending: sortState.descending
  }
  if (tabKey === 'product_detail') {
    params.urunAnaGrubu = selectedDetailMainGroup.value
  }
  try {
    const startedAt = performance.now()
    const rows = await performanceStore.fetchGroupedRows(params, { force: options.force === true })
    const currentFilterState = backendGroupedFilterState(activeTab.value)
    const currentSortState = tableSortState(activeTab.value)
    const currentKey = `${activeTab.value}|${selectedExpandThroughLevel.value}|${selectedDetailMainGroup.value || ''}|${JSON.stringify(currentFilterState.filters)}|${currentSortState.sortBy}|${currentSortState.descending ? 1 : 0}`
    if (requestKey !== currentKey) return
    const normalizedRows = Array.isArray(rows) ? rows.map(row => normalizeGroupedDisplayRow(tabKey, row)) : []
    backendGroupedRows.value = {
      ...backendGroupedRows.value,
      [tabKey]: normalizedRows
    }
    console.info('[ProductPerformance][ui] grouped applied', {
      tab: tabKey,
      rows: normalizedRows.length,
      elapsedSec: Number(((performance.now() - startedAt) / 1000).toFixed(2))
    })
  } catch (err) {
    console.warn('product performance grouped rows failed', err)
  } finally {
    backendGroupedLoading.value = false
  }
}

function normalizeGroupedDisplayRow (tabKey, row) {
  const nextRow = { ...row }
  normalizeGroupedProductHierarchyValues(nextRow)
  return withPeriodScores(withGeneralMargins(withProductMargins(nextRow)))
}

function normalizeGroupedProductHierarchyValues (row) {
  row.urun_ilk_grubu = cleanProductFirstGroupValue(row.urun_ilk_grubu)
  if (row.__group && row.group_field === 'urun_ilk_grubu') {
    const cleanValue = cleanProductFirstGroupValue(row.group_value || row.label || row.urun_ilk_grubu)
    row.group_value = cleanValue
    row.label = cleanValue
    row.urun_ilk_grubu = cleanValue
  }
  return row
}

function responseRowCount (data) {
  if (Array.isArray(data)) return data.length
  if (Array.isArray(data?.rows)) return data.rows.length
  if (data && typeof data === 'object') return Object.keys(data).length
  return 0
}

async function timedProductPerformanceGet (label, url, config = {}) {
  const startedAt = performance.now()
  console.info('[ProductPerformance][api] start', {
    label,
    url,
    params: config?.params || {}
  })
  try {
    const resp = await api.get(url, config)
    console.info('[ProductPerformance][api] done', {
      label,
      rows: responseRowCount(resp?.data),
      elapsedSec: Number(((performance.now() - startedAt) / 1000).toFixed(2))
    })
    return resp
  } catch (err) {
    console.warn('[ProductPerformance][api] failed', {
      label,
      elapsedSec: Number(((performance.now() - startedAt) / 1000).toFixed(2)),
      message: err?.response?.data || err?.message || err
    })
    throw err
  }
}

function productPerformanceExcelExportTableKey () {
  if (isProductKpiTab.value) return activeProductTableKey.value
  if (backendGroupedSupportedTab(activeTab.value)) return activeTab.value
  return 'products'
}

function buildProductPerformanceExcelExportFilters () {
  const tableKey = productPerformanceExcelExportTableKey()
  const out = {}
  for (const col of filterColumnsForTableKey(tableKey) || []) {
    const name = String(col?.name || '').trim()
    if (!productPerformanceExcelExportFilterFields.has(name)) continue
    const selected = selectedColumnFilters(tableKey, name)
    if (selected.length) out[name] = selected
  }
  if (activeTab.value === 'product_detail') {
    const mainGroup = String(selectedDetailMainGroup.value || '').trim()
    if (mainGroup && !out.urun_ana_grubu?.includes(mainGroup)) {
      out.urun_ana_grubu = [...(out.urun_ana_grubu || []), mainGroup]
    }
  }
  return out
}

function productPerformanceExcelFilename (headers = {}) {
  const disposition = String(headers?.['content-disposition'] || headers?.['Content-Disposition'] || '').trim()
  const utfMatch = disposition.match(/filename\*=UTF-8''([^;]+)/i)
  if (utfMatch?.[1]) return decodeURIComponent(utfMatch[1].replaceAll('"', '').trim())
  const match = disposition.match(/filename="?([^";]+)"?/i)
  return match?.[1]?.trim() || `product_performance_color_yaka_${new Date().toISOString().slice(0, 10)}.xlsx`
}

function downloadProductPerformanceExcelBlob (blob, filename) {
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  document.body.appendChild(a)
  a.click()
  a.remove()
  URL.revokeObjectURL(url)
}

async function exportProductPerformanceExcel () {
  if (excelExportLoading.value) return
  excelExportLoading.value = true
  try {
    const tableKey = productPerformanceExcelExportTableKey()
    const pagination = tablePagination[tableKey] || tablePagination.products || {}
    const response = await api.post('/pricing/product-performance/export-excel', {
      filters: buildProductPerformanceExcelExportFilters(),
      sort_by: pagination.sortBy || '',
      descending: pagination.descending !== false
    }, {
      responseType: 'blob',
      timeout: 180000
    })
    downloadProductPerformanceExcelBlob(response.data, productPerformanceExcelFilename(response.headers || {}))
  } catch (err) {
    const detail = await extractApiErrorDetail(err)
    Notify.create({ type: 'negative', message: detail || 'Excel çıktısı alınamadı' })
  } finally {
    excelExportLoading.value = false
  }
}

async function reload () {
  loading.value = true
  startLoadingTimer('Rapor verileri hazırlanıyor...')
  try {
    rows.value = []
    detailMainGroupOptionRows.value = []
    generalRowsLoaded.value = false
    generalRows.value = []
    orderProductCustomersLoaded.value = false
    orderProductCustomerRows.value = []
    orderMarketDetailsLoaded.value = false
    orderMarketDetailRows.value = []
    orderAnalysisLoaded.value = false
    orderAnalysisRows.value = []
    orderMarketRows.value = []
    orderCustomerRows.value = []
    marketRowsLoaded.value = false
    marketRows.value = []
    countryRowsLoaded.value = false
    countryRows.value = []
    customerRowsLoaded.value = false
    customerRows.value = []
    for (const key of salesBreakdownTabKeys) salesBreakdownRows[key] = []

    loadingStage.value = 'Ana KPI ve özet yükleniyor...'
    const [summaryResp] = await Promise.all([
      timedProductPerformanceGet('summary', '/pricing/product-performance/summary', { timeout: 180000 }),
      loadDetailMainGroupOptions(true)
    ])
    summary.value = summaryResp?.data || {}
    loadingStage.value = 'Rapor ekranı hazırlanıyor...'
    await nextTick()
    ensureDetailMainGroupSelection()
    applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ürün performans verisi alınamadı' })
  } finally {
    loading.value = false
    stopLoadingTimer()
  }
}

async function loadDetailMainGroupOptions (force = false) {
  const optionRows = await performanceStore.fetchGroupedRows({
    mode: 'products',
    groupLevels: ['urun_ana_grubu'],
    expandThroughLevel: -1,
    limit: productKpiFetchLimit
  }, { force })
  detailMainGroupOptionRows.value = (Array.isArray(optionRows) ? optionRows : [])
    .filter(row => row?.__group)
    .map(row => {
      const normalized = normalizeGroupedDisplayRow('products', row)
      const value = productMainGroupValue(normalized) || String(normalized.group_value || normalized.label || '').trim()
      return {
        value,
        label: value || '-',
        stock_qty: Number(normalized.stock_qty || 0),
        sales_qty_90d: Number(normalized.sales_qty_90d || 0)
      }
    })
    .filter(option => option.value)
}

function startLoadingTimer (stage = '') {
  loadingStage.value = stage
  loadingStartedAt.value = Date.now()
  loadingNow.value = Date.now()
  if (loadingTimer) window.clearInterval(loadingTimer)
  loadingTimer = window.setInterval(() => {
    loadingNow.value = Date.now()
  }, 1000)
}

function stopLoadingTimer () {
  if (loadingTimer) {
    window.clearInterval(loadingTimer)
    loadingTimer = null
  }
  loadingStartedAt.value = 0
  loadingNow.value = 0
  loadingStage.value = ''
}

async function preloadAllReportTabs () {
  const tasks = [
    loadGeneralRows(false),
    loadOrderProductCustomers(false),
    loadOrderMarketDetails(false),
    loadMarketRows(false),
    loadCountryRows(false),
    loadCustomerRows(false),
    loadAllSalesBreakdownRows(false)
  ]
  const results = await Promise.allSettled(tasks)
  const failed = results.filter(item => item.status === 'rejected')
  if (failed.length) {
    console.warn('[ProductPerformance][preload] partial failure', failed.map(item => item.reason?.message || item.reason))
  }
}

async function loadAllSalesBreakdownRows (showLoading = true) {
  if (showLoading) loading.value = true
  try {
    await Promise.all(salesBreakdownTabKeys.map(key => loadSalesBreakdownRows(key, false)))
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadSalesBreakdownRows (tabKey, showLoading = true) {
  if (!salesBreakdownTabKeys.includes(tabKey)) return
  if (Array.isArray(salesBreakdownRows[tabKey]) && salesBreakdownRows[tabKey].length) return
  if (showLoading) loading.value = true
  try {
    const mode = salesBreakdownModes[tabKey] || tabKey
    const resp = await timedProductPerformanceGet(`sales:${tabKey}`, '/pricing/product-performance/sales-breakdown', {
      params: { mode, limit: reportFetchLimit },
      timeout: 180000
    })
    salesBreakdownRows[tabKey] = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeSalesBreakdownRow)
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Satış kırılım verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadGeneralRows (showLoading = true) {
  if (generalRowsLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('general', '/pricing/product-performance/general', { params: { limit: reportFetchLimit }, timeout: 180000 })
    const normalizedGeneralRows = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeGeneralRow)
    generalRows.value = normalizedGeneralRows
    generalRowsLoaded.value = true

    const generalByVariant = new Map(normalizedGeneralRows.map(row => [performanceVariantKey(row), generalMetricFields(row)]))
    rows.value = rows.value.map(row => {
      const generalMetrics = generalByVariant.get(performanceVariantKey(row)) || {}
      return Object.keys(generalMetrics).length ? normalizeRow({ ...row, ...generalMetrics }) : row
    })
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Genel performans verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadOrderProductCustomers (showLoading = true) {
  if (orderProductCustomersLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('orders:product-customers', '/pricing/product-performance/orders/product-customers', { params: { limit: reportFetchLimit }, timeout: 180000 })
    orderProductCustomerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderProductCustomerRow)
    orderProductCustomersLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ürün müşteri sipariş analizi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadOrderMarketDetails (showLoading = true) {
  if (orderMarketDetailsLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('orders:market-details', '/pricing/product-performance/orders/market-details', { params: { limit: reportFetchLimit }, timeout: 180000 })
    orderMarketDetailRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderMarketDetailRow)
    orderMarketDetailsLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Piyasa sipariş detay analizi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadMarketRows (showLoading = true) {
  if (marketRowsLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('markets', '/pricing/product-performance/markets', { params: { limit: reportFetchLimit }, timeout: 180000 })
    marketRows.value = aggregateMarketRowsByDisplayName(Array.isArray(resp?.data) ? resp.data : [])
    marketRowsLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Piyasa performans verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

function aggregateMarketRowsByDisplayName (sourceRows) {
  const grouped = new Map()
  for (const row of sourceRows || []) {
    const display = displayMarketName(row?.market_key) || String(row?.market_key || '').trim() || '-'
    const current = grouped.get(display) || {
      ...row,
      row_key: `market|${display}`,
      market_key: display,
      product_count: 0,
      star_count: 0,
      stock_risk_count: 0,
      stock_qty: 0,
      stock_cost_value_usd: 0,
      risk_stock_cost_value_usd: 0,
      sales_qty_90d: 0,
      sales_usd_90d: 0,
      gross_profit_usd_90d: 0,
      _margin_weight: 0,
      _margin_total: 0,
      _stock_days_weight: 0,
      _stock_days_total: 0
    }
    current.product_count += Number(row?.product_count || 0)
    current.star_count += Number(row?.star_count || 0)
    current.stock_risk_count += Number(row?.stock_risk_count || 0)
    current.stock_qty += Number(row?.stock_qty || 0)
    current.stock_cost_value_usd += Number(row?.stock_cost_value_usd || 0)
    current.risk_stock_cost_value_usd += Number(row?.risk_stock_cost_value_usd || 0)
    current.sales_qty_90d += Number(row?.sales_qty_90d || 0)
    current.sales_usd_90d += Number(row?.sales_usd_90d || 0)
    current.gross_profit_usd_90d += Number(row?.gross_profit_usd_90d || 0)
    const marginWeight = Math.abs(Number(row?.sales_usd_90d || 0))
    current._margin_weight += marginWeight
    current._margin_total += Number(row?.avg_gross_margin_90d || 0) * marginWeight
    const stockDaysWeight = Math.abs(Number(row?.stock_qty || 0))
    current._stock_days_weight += stockDaysWeight
    current._stock_days_total += Number(row?.avg_stock_days_90d || 0) * stockDaysWeight
    grouped.set(display, current)
  }
  return Array.from(grouped.values())
    .map(row => {
      row.avg_gross_margin_90d = row._margin_weight > 0 ? row._margin_total / row._margin_weight : 0
      row.avg_stock_days_90d = row._stock_days_weight > 0 ? row._stock_days_total / row._stock_days_weight : 0
      delete row._margin_weight
      delete row._margin_total
      delete row._stock_days_weight
      delete row._stock_days_total
      return row
    })
    .sort((a, b) => Number(b.risk_stock_cost_value_usd || 0) - Number(a.risk_stock_cost_value_usd || 0) ||
      Number(b.stock_cost_value_usd || 0) - Number(a.stock_cost_value_usd || 0) ||
      Number(b.sales_usd_90d || 0) - Number(a.sales_usd_90d || 0))
}

async function loadCountryRows (showLoading = true) {
  if (countryRowsLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('countries', '/pricing/product-performance/countries', { params: { limit: reportFetchLimit }, timeout: 180000 })
    countryRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
      ...row,
      country_key: `${row.country || '-'}|${row.customer_segment || '-'}|${row.market_key || '-'}`
    }))
    countryRowsLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ülke performans verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function loadCustomerRows (showLoading = true) {
  if (customerRowsLoaded.value) return
  if (showLoading) loading.value = true
  try {
    const resp = await timedProductPerformanceGet('customers', '/pricing/product-performance/customers', {
      params: { limit: reportFetchLimit, breakdown: customerBreakdown.value },
      timeout: 180000
    })
    customerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
      ...row,
      customer_key: `${row.breakdown || '-'}|${row.market_key || '-'}|${row.country || '-'}|${row.customer_segment || '-'}|${row.customer_code || '-'}`
    }))
    customerRowsLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Müşteri performans verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

async function ensureActiveTabData (showLoading = true) {
  const tab = activeTab.value
  if (tab === 'general') return loadGeneralRows(showLoading)
  if (tab === 'markets') return loadMarketRows(showLoading)
  if (tab === 'countries') return loadCountryRows(showLoading)
  if (tab === 'customers') return loadCustomerRows(showLoading)
  if (backendGroupedSupportedTab(tab)) {
    scheduleLoadBackendGroupedRows()
  }
}

async function loadOrderAnalysis (showLoading = true) {
  if (showLoading) loading.value = true
  try {
    const [productResp, marketResp, customerResp] = await Promise.all([
      timedProductPerformanceGet('orders:products', '/pricing/product-performance/orders', { params: { limit: reportFetchLimit }, timeout: 90000 }),
      timedProductPerformanceGet('orders:groups:market', '/pricing/product-performance/orders/groups', { params: { limit: reportFetchLimit, breakdown: 'market' }, timeout: 90000 }),
      timedProductPerformanceGet('orders:groups:customer', '/pricing/product-performance/orders/groups', { params: { limit: reportFetchLimit, breakdown: 'customer' }, timeout: 90000 })
    ])
    orderAnalysisRows.value = (Array.isArray(productResp?.data) ? productResp.data : []).map(normalizeOrderAnalysisRow)
    orderMarketRows.value = (Array.isArray(marketResp?.data) ? marketResp.data : []).map(normalizeOrderGroupRow)
    orderCustomerRows.value = (Array.isArray(customerResp?.data) ? customerResp.data : []).map(normalizeOrderGroupRow)
    orderAnalysisLoaded.value = true
  } catch (err) {
    Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Sipariş analiz verisi alınamadı' })
  } finally {
    if (showLoading) loading.value = false
  }
}

function bucketLabel (value) {
  return bucketOptions.find(x => x.value === value)?.label || value || '-'
}

function bucketColor (value) {
  switch (value) {
    case 'YILDIZ_URUN': return 'green-7'
    case 'STOKSUZ_TALEP': return 'blue-7'
    case 'STOK_RISKI': return 'orange-8'
    case 'FIYAT_BASKISI': return 'red-7'
    case 'FIYAT_FIRSATI': return 'teal-7'
    case 'MALIYET_YOK': return 'blue-grey-7'
    default: return 'grey-7'
  }
}

watch(activeTab, () => {
  void ensureActiveTabData(false).then(() => {
    if (backendGroupedSupportedTab(activeTab.value)) applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
  })
  setupTopScrollbarSync()
})

watch(activeBackendGroupedSortSignature, () => {
  if (!backendGroupedSupportedTab(activeTab.value) || loading.value) return
  scheduleLoadBackendGroupedRows()
})

watch(activeDisplayedTableRows, () => {
  setupTopScrollbarSync()
}, { flush: 'post' })

watch(detailMainGroupOptions, () => {
  ensureDetailMainGroupSelection()
}, { immediate: true })

watch(selectedDetailMainGroup, () => {
  if (activeTab.value === 'product_detail') {
    void nextTick(() => {
      const level = selectedExpandThroughLevel.value
      applyActiveExpansionThroughLevel(level >= 0 ? autoExpandThroughLevel.value : -1)
    })
  }
})

watch(activeDisplayedTableRows, tableRows => {
  if (!isProductKpiTab.value) return
  const imageRows = tableRows
    .filter(row => !row.__group || groupShowsImage(row))
    .slice(0, 240)
  void primeProductImages(imageRows)
}, { immediate: true })

onMounted(() => {
  void reload()
  setupTopScrollbarSync()
})

onBeforeUnmount(() => {
  cleanupTopScrollbarSync()
  if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
  stopLoadingTimer()
})
</script>

<style scoped>
.product-performance-page {
  position: relative;
  background: #f6f7f9;
}

.page-busy-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: #f6f7f9;
  backdrop-filter: blur(2px);
  pointer-events: all;
}

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

.page-busy-time {
  color: #5f6b7a;
  font-size: 13px;
  font-weight: 600;
}

.performance-top-bar {
  min-height: 34px;
}

.performance-page-title {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.12;
}

.performance-page-subtitle {
  font-size: 10px;
  line-height: 1.15;
  margin-top: 1px;
}

.performance-top-actions {
  min-height: 30px;
}

.performance-top-actions :deep(.q-btn) {
  min-height: 26px;
  padding: 2px 8px;
  font-size: 11px;
  line-height: 1.1;
}

.performance-top-actions :deep(.q-btn .q-icon) {
  font-size: 16px;
}

.period-selector {
  min-height: 26px;
  border: 1px solid #d5dce5;
  border-radius: 6px;
  display: flex;
  align-items: center;
}

.period-selector :deep(.q-option-group) {
  gap: 4px;
}

.period-selector :deep(.q-checkbox) {
  min-height: 22px;
}

.period-selector :deep(.q-checkbox__inner) {
  font-size: 26px;
  width: 26px;
  height: 26px;
}

.period-selector :deep(.q-checkbox__label) {
  font-size: 11px;
  line-height: 1.1;
}

.performance-tab-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 4px;
}

.performance-tab-button {
  min-height: 24px;
  padding: 3px 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border: 1px solid #d5dce5;
  border-radius: 6px;
  background: #fff;
  color: #4b5563;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.15;
  text-align: center;
  cursor: pointer;
}

.performance-tab-button span {
  white-space: normal;
  overflow-wrap: anywhere;
}

.performance-tab-button.active {
  border-color: var(--q-primary);
  color: var(--q-primary);
  background: #eef6ff;
}

.performance-tab-button:disabled {
  cursor: wait;
  opacity: 0.62;
}

.main-group-bar {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 6px;
  border: 1px solid #d7dde5;
  border-radius: 6px;
  background: #ffffff;
}

.main-group-button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 4px 10px;
  border: 1px solid #c9d2dd;
  border-radius: 6px;
  background: #f7f9fb;
  color: #26384c;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}

.main-group-button small {
  color: #64748b;
  font-size: 10px;
  font-weight: 600;
}

.main-group-button.active {
  border-color: #1d4ed8;
  background: #dbeafe;
  color: #173a75;
}

.main-group-button:disabled {
  cursor: wait;
  opacity: 0.62;
}

.metric-card {
  min-height: 38px;
}

.metric-card :deep(.q-card__section) {
  padding: 5px 7px;
}

.metric-card :deep(.text-caption) {
  font-size: 10px;
  line-height: 1.1;
}

.metric-value {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.08;
  margin-top: 1px;
}

.detail-level-menu {
  width: 230px;
  padding: 7px;
}

.performance-table {
  max-height: calc(100vh - 160px);
  font-size: 11px;
}

.performance-top-scrollbar {
  display: none;
  height: 14px;
  overflow-x: auto;
  overflow-y: hidden;
  background: #fff;
  border: 1px solid #d5dce5;
  border-radius: 4px;
}

.performance-top-scrollbar-inner {
  height: 1px;
}

.performance-table :deep(.q-table__middle) {
  max-height: calc(100vh - 220px);
  overflow: auto;
}

.performance-table :deep(.q-table__middle::-webkit-scrollbar:horizontal) {
  height: 0;
}

.performance-table :deep(.q-table__bottom) {
  display: none;
}

.performance-table :deep(.q-table) {
  border-collapse: separate;
  border-spacing: 0;
}

.performance-table :deep(.q-table th),
.performance-table :deep(.q-table td) {
  border-right: 1px solid #d5dce5;
  border-bottom: 1px solid #d5dce5;
  vertical-align: middle;
  white-space: normal;
  padding: 4px 5px;
}

.performance-table :deep(.q-table th) {
  height: 42px;
  min-height: 42px;
  padding: 3px 4px;
}

.performance-table :deep(.q-table tbody tr),
.performance-table :deep(.q-table tbody td) {
  height: auto;
  min-height: 38px;
}

.performance-table :deep(.q-table tbody td) {
  max-width: 320px;
  padding-top: 4px;
  padding-bottom: 4px;
  overflow-wrap: anywhere;
}

.performance-table :deep(.q-table tbody td span:not(.q-badge):not(.q-icon)) {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
}

.performance-table :deep(.q-table th.text-right),
.performance-table :deep(.q-table td.text-right) {
  min-width: 98px;
  width: 98px;
  max-width: none;
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: normal;
}

.performance-table :deep(.q-table td.text-right span:not(.q-badge):not(.q-icon)) {
  display: inline-block;
  -webkit-line-clamp: unset;
  -webkit-box-orient: initial;
  max-width: none;
  white-space: nowrap;
  overflow: visible;
  line-height: 1.25;
}

.performance-table :deep(.q-table th.text-right .header-label) {
  white-space: normal;
  overflow-wrap: anywhere;
}

.performance-table :deep(.q-table th:not(.text-right)),
.performance-table :deep(.q-table td:not(.text-right)) {
  white-space: normal;
  overflow-wrap: anywhere;
}

.performance-table :deep(.q-table tbody td .q-field),
.performance-table :deep(.q-table tbody td input),
.performance-table :deep(.q-table tbody td textarea) {
  min-width: 72px;
  max-width: 100%;
}

.performance-table :deep(.q-table tbody td .q-field__control),
.performance-table :deep(.q-table tbody td .q-field__native),
.performance-table :deep(.q-table tbody td .q-field__input) {
  min-height: 24px;
  font-size: 10px;
  line-height: 1.2;
}

.performance-table :deep(.q-table th.color-yaka-cell),
.performance-table :deep(.q-table td.color-yaka-cell) {
  width: 170px;
  min-width: 170px;
  max-width: 190px;
  font-size: 10.5px;
}

.performance-table :deep(.q-table th:first-child),
.performance-table :deep(.q-table td:first-child) {
  border-left: 1px solid #d5dce5;
}

.performance-table :deep(.q-table thead),
.performance-table :deep(.q-table thead tr),
.performance-table :deep(.q-table th) {
  position: sticky;
  top: 0;
  z-index: 20;
  background: #f8fbff;
}

.performance-table :deep(th) {
  border-top: 1px solid #d5dce5;
}

.product-breakdown-table :deep(.q-table) {
  min-width: 3170px;
  table-layout: fixed;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(-n+10)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(-n+10)) {
  position: sticky;
  z-index: 2;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(-n+10)) {
  z-index: 30;
  background: #f8fbff;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table tbody tr:not(.group-row) td:nth-child(-n+10)) {
  background: #fff;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(1)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(1)) {
  left: 0;
  width: 190px;
  min-width: 190px;
  max-width: 190px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(2)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(2)) {
  left: 190px;
  width: 140px;
  min-width: 140px;
  max-width: 140px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(3)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(3)) {
  left: 330px;
  width: 110px;
  min-width: 110px;
  max-width: 110px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(4)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(4)) {
  left: 440px;
  width: 150px;
  min-width: 150px;
  max-width: 150px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(5)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(5)) {
  left: 590px;
  width: 170px;
  min-width: 170px;
  max-width: 170px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(6)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(6)) {
  left: 760px;
  width: 150px;
  min-width: 150px;
  max-width: 150px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(7)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(7)) {
  left: 910px;
  width: 130px;
  min-width: 130px;
  max-width: 130px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(8)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(8)) {
  left: 1040px;
  width: 90px;
  min-width: 90px;
  max-width: 90px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(9)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(9)) {
  left: 1130px;
  width: 80px;
  min-width: 80px;
  max-width: 80px;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(10)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(10)) {
  left: 1210px;
  width: 130px;
  min-width: 130px;
  max-width: 130px;
  box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
}

.product-breakdown-table.compact-detail-columns :deep(.q-table) {
  min-width: 2450px;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(-n+4)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(-n+4)) {
  position: sticky;
  z-index: 2;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(-n+4)) {
  z-index: 30;
  background: #f8fbff;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table tbody tr:not(.group-row) td:nth-child(-n+4)) {
  background: #fff;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(1)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(1)) {
  left: 0;
  width: 86px;
  min-width: 86px;
  max-width: 86px;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(2)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(2)) {
  left: 86px;
  width: 150px;
  min-width: 150px;
  max-width: 150px;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(3)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(3)) {
  left: 236px;
  width: 112px;
  min-width: 112px;
  max-width: 112px;
}

.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(4)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(4)) {
  left: 348px;
  width: 132px;
  min-width: 132px;
  max-width: 132px;
  box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
}

.product-breakdown-table.product-detail-table :deep(.q-table) {
  min-width: 2620px;
}

.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(n+8)) {
  position: static;
  left: auto;
  z-index: auto;
  box-shadow: none;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(n+8)) {
  left: auto;
  z-index: 20;
  box-shadow: none;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(-n+7)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(-n+7)) {
  position: sticky;
  z-index: 2;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(-n+7)) {
  z-index: 30;
  background: #f8fbff;
}

.product-breakdown-table.product-detail-table :deep(.q-table tbody tr:not(.group-row) td:nth-child(-n+7)) {
  background: #fff;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(1)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(1)) {
  left: 0;
  width: 190px;
  min-width: 190px;
  max-width: 190px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(2)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(2)) {
  left: 190px;
  width: 150px;
  min-width: 150px;
  max-width: 150px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(3)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(3)) {
  left: 340px;
  width: 150px;
  min-width: 150px;
  max-width: 150px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(4)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(4)) {
  left: 490px;
  width: 130px;
  min-width: 130px;
  max-width: 130px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(5)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(5)) {
  left: 620px;
  width: 170px;
  min-width: 170px;
  max-width: 170px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(6)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(6)) {
  left: 790px;
  width: 170px;
  min-width: 170px;
  max-width: 190px;
}

.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(7)),
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(7)) {
  left: 960px;
  width: 120px;
  min-width: 120px;
  max-width: 120px;
  box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
}

.sticky-dim-table :deep(.q-table) {
  min-width: 1700px;
  table-layout: fixed;
}

.sticky-dim-table :deep(.q-table th),
.sticky-dim-table :deep(.q-table td) {
  min-width: 118px;
  width: 118px;
  max-width: 180px;
}

.sticky-dim-table :deep(.q-table th.text-right),
.sticky-dim-table :deep(.q-table td.text-right),
.product-breakdown-table :deep(.q-table th.text-right),
.product-breakdown-table :deep(.q-table td.text-right) {
  min-width: 124px;
  width: 124px;
  max-width: none;
  white-space: nowrap;
}

.product-breakdown-table :deep(.q-table td.text-right) {
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}

.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table th:nth-child(n+11):not(.text-right)),
.product-breakdown-table:not(.product-detail-table):not(.compact-detail-columns) :deep(.q-table td:nth-child(n+11):not(.text-right)),
.product-breakdown-table.compact-detail-columns :deep(.q-table th:nth-child(n+5):not(.text-right)),
.product-breakdown-table.compact-detail-columns :deep(.q-table td:nth-child(n+5):not(.text-right)),
.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(n+4):not(.text-right)),
.sticky-dim-table.sticky-dim-3 :deep(.q-table td:nth-child(n+4):not(.text-right)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table th:nth-child(n+5):not(.text-right)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table td:nth-child(n+5):not(.text-right)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table th:nth-child(n+6):not(.text-right)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table td:nth-child(n+6):not(.text-right)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table th:nth-child(n+7):not(.text-right)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table td:nth-child(n+7):not(.text-right)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table th:nth-child(n+8):not(.text-right)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table td:nth-child(n+8):not(.text-right)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table th:nth-child(n+9):not(.text-right)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table td:nth-child(n+9):not(.text-right)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table th:nth-child(n+10):not(.text-right)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table td:nth-child(n+10):not(.text-right)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table th:nth-child(n+11):not(.text-right)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table td:nth-child(n+11):not(.text-right)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table th:nth-child(n+12):not(.text-right)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table td:nth-child(n+12):not(.text-right)) {
  min-width: 140px;
  width: 140px;
  max-width: 210px;
}

.sticky-dim-table :deep(.q-table th:nth-child(1)),
.sticky-dim-table :deep(.q-table td:nth-child(1)) {
  left: 0;
  position: sticky;
  z-index: 2;
  background: #fff;
}

.sticky-dim-table :deep(.q-table th:nth-child(2)),
.sticky-dim-table :deep(.q-table td:nth-child(2)) {
  left: 118px;
}

.sticky-dim-table :deep(.q-table th:nth-child(3)),
.sticky-dim-table :deep(.q-table td:nth-child(3)) {
  left: 236px;
}

.sticky-dim-table :deep(.q-table th:nth-child(4)),
.sticky-dim-table :deep(.q-table td:nth-child(4)) {
  left: 354px;
}

.sticky-dim-table :deep(.q-table th:nth-child(5)),
.sticky-dim-table :deep(.q-table td:nth-child(5)) {
  left: 472px;
}

.sticky-dim-table :deep(.q-table th:nth-child(6)),
.sticky-dim-table :deep(.q-table td:nth-child(6)) {
  left: 590px;
}

.sticky-dim-table :deep(.q-table th:nth-child(7)),
.sticky-dim-table :deep(.q-table td:nth-child(7)) {
  left: 708px;
}

.sticky-dim-table :deep(.q-table th:nth-child(8)),
.sticky-dim-table :deep(.q-table td:nth-child(8)) {
  left: 826px;
}

.sticky-dim-table :deep(.q-table th:nth-child(9)),
.sticky-dim-table :deep(.q-table td:nth-child(9)) {
  left: 944px;
}

.sticky-dim-table :deep(.q-table th:nth-child(10)),
.sticky-dim-table :deep(.q-table td:nth-child(10)) {
  left: 1062px;
}

.sticky-dim-table :deep(.q-table th:nth-child(11)),
.sticky-dim-table :deep(.q-table td:nth-child(11)) {
  left: 1180px;
}

.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(-n+3)),
.sticky-dim-table.sticky-dim-3 :deep(.q-table td:nth-child(-n+3)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table th:nth-child(-n+4)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table td:nth-child(-n+4)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table th:nth-child(-n+5)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table td:nth-child(-n+5)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table th:nth-child(-n+6)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table td:nth-child(-n+6)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table th:nth-child(-n+7)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table td:nth-child(-n+7)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table th:nth-child(-n+8)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table td:nth-child(-n+8)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table th:nth-child(-n+9)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table td:nth-child(-n+9)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table th:nth-child(-n+10)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table td:nth-child(-n+10)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table th:nth-child(-n+11)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table td:nth-child(-n+11)) {
  position: sticky;
  z-index: 2;
}

.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(-n+3)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table th:nth-child(-n+4)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table th:nth-child(-n+5)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table th:nth-child(-n+6)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table th:nth-child(-n+7)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table th:nth-child(-n+8)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table th:nth-child(-n+9)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table th:nth-child(-n+10)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table th:nth-child(-n+11)) {
  z-index: 30;
  background: #f8fbff;
}

.sticky-dim-table :deep(.q-table tbody td:nth-child(-n+11)) {
  background: #fff;
}

.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(3)),
.sticky-dim-table.sticky-dim-3 :deep(.q-table td:nth-child(3)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table th:nth-child(4)),
.sticky-dim-table.sticky-dim-4 :deep(.q-table td:nth-child(4)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table th:nth-child(5)),
.sticky-dim-table.sticky-dim-5 :deep(.q-table td:nth-child(5)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table th:nth-child(6)),
.sticky-dim-table.sticky-dim-6 :deep(.q-table td:nth-child(6)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table th:nth-child(7)),
.sticky-dim-table.sticky-dim-7 :deep(.q-table td:nth-child(7)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table th:nth-child(8)),
.sticky-dim-table.sticky-dim-8 :deep(.q-table td:nth-child(8)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table th:nth-child(9)),
.sticky-dim-table.sticky-dim-9 :deep(.q-table td:nth-child(9)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table th:nth-child(10)),
.sticky-dim-table.sticky-dim-10 :deep(.q-table td:nth-child(10)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table th:nth-child(11)),
.sticky-dim-table.sticky-dim-11 :deep(.q-table td:nth-child(11)) {
  box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
}

.sales-color-yaka-table :deep(.q-table th:nth-child(1)),
.sales-color-yaka-table :deep(.q-table td:nth-child(1)) {
  left: 0;
  width: 170px;
  min-width: 170px;
  max-width: 190px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(2)),
.sales-color-yaka-table :deep(.q-table td:nth-child(2)) {
  left: 170px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(3)),
.sales-color-yaka-table :deep(.q-table td:nth-child(3)) {
  left: 288px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(4)),
.sales-color-yaka-table :deep(.q-table td:nth-child(4)) {
  left: 406px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(5)),
.sales-color-yaka-table :deep(.q-table td:nth-child(5)) {
  left: 524px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(6)),
.sales-color-yaka-table :deep(.q-table td:nth-child(6)) {
  left: 642px;
  width: 130px;
  min-width: 130px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(7)),
.sales-color-yaka-table :deep(.q-table td:nth-child(7)) {
  left: 772px;
  width: 170px;
  min-width: 170px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(8)),
.sales-color-yaka-table :deep(.q-table td:nth-child(8)) {
  left: 942px;
  width: 116px;
  min-width: 116px;
}

.sales-color-yaka-table :deep(.q-table th:nth-child(9)),
.sales-color-yaka-table :deep(.q-table td:nth-child(9)) {
  left: 1058px;
  width: 190px;
  min-width: 190px;
  box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
}

.filterable-header-cell {
  vertical-align: middle;
}

.header-with-filter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18px;
  align-items: center;
  gap: 2px;
  min-width: 82px;
  width: 100%;
}

.header-with-filter > span {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-height: 1.12;
  white-space: normal;
  overflow: hidden;
  overflow-wrap: anywhere;
  text-overflow: clip;
  font-size: 10px;
  font-weight: 700;
}

.header-filter-btn {
  width: 18px;
  height: 18px;
  min-height: 18px;
  padding: 0;
}

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

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

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

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

.excel-filter-options {
  max-height: 260px;
  margin-top: 4px;
  border: 1px solid #e0e3e7;
  border-radius: 6px;
}

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

.excel-filter-empty {
  padding: 12px;
  color: #7a8390;
  text-align: center;
}

.performance-table :deep(.q-table tbody tr.group-row td) {
  background: #edf3fb;
  font-weight: 600;
  color: #1f2937;
  border-color: #c4d2e2;
  white-space: nowrap;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-0 td) {
  background: #123b63;
  color: #fff;
  border-color: #0b2845;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-1 td) {
  background: #1f6f54;
  color: #fff;
  border-color: #18563f;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-2 td) {
  background: #ffe2a8;
  color: #1f2937;
  border-color: #d7a84d;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-3 td) {
  background: #d8f0dc;
  color: #1f2937;
  border-color: #9bc7a3;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-4 td) {
  background: #e7ecff;
  color: #1f2937;
  border-color: #b8c2ef;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-level-5 td) {
  background: #f2f3f5;
  color: #1f2937;
  border-color: #c4c9d1;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-field-color_yaka td) {
  background: #f7e3bf;
  color: #1f2937;
  border-top: 2px solid #c99032;
  border-bottom: 2px solid #c99032;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-field-color_yaka td:first-child) {
  border-left: 6px solid #b76a00;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-field-market_key td) {
  background: #d9e8ff;
  color: #132238;
  border-top: 2px solid #7da9e6;
  border-bottom: 2px solid #7da9e6;
}

.performance-table :deep(.q-table tbody tr.group-row.group-row-field-market_key td:first-child) {
  border-left: 6px solid #2563b8;
}

.performance-table :deep(.q-table tbody tr.group-row td span),
.performance-table :deep(.q-table tbody tr.group-row .group-title),
.performance-table :deep(.q-table tbody tr.group-row .q-btn),
.performance-table :deep(.q-table tbody tr.group-row .q-icon) {
  color: inherit;
  opacity: 1;
}

.group-cell-label {
  display: flex;
  align-items: center;
  gap: 3px;
  width: 100%;
  min-width: 0;
  max-width: 100%;
}

.group-cell-label :deep(.q-btn) {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  min-height: 20px;
}

.group-title {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: 0;
  white-space: normal;
  overflow: visible;
  overflow-wrap: anywhere;
  line-height: 1.18;
}

.product-image-cell {
  width: 190px;
  min-width: 190px;
  max-width: 190px;
  padding: 10px !important;
  text-align: center;
  vertical-align: middle;
}

.product-thumb-button {
  width: 162px;
  height: 216px;
  border: 1px solid #d9dde3;
  border-radius: 8px;
  background: #fff;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.product-thumb,
.product-thumb-placeholder {
  width: 100%;
  height: 100%;
}

.product-thumb {
  display: block;
  object-fit: contain;
  object-position: center;
}

.product-thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #9aa3af;
  background: #f3f5f7;
  border-radius: 4px;
}

.product-image-dialog {
  background: #f6f7f9;
}

.product-image-dialog-body {
  height: calc(100vh - 74px);
  padding: 12px;
  overflow: hidden;
}

.performance-image-layout {
  display: grid;
  grid-template-columns: minmax(280px, 330px) minmax(360px, 1fr) minmax(440px, 1.25fr);
  gap: 12px;
  height: 100%;
  min-height: 0;
}

.image-product-fields,
.image-sales-panel,
.image-media-panel {
  min-height: 0;
}

.image-product-fields,
.image-sales-panel {
  overflow: auto;
  background: #fff;
  border: 1px solid #d9dde3;
  border-radius: 8px;
  padding: 12px;
}

.image-section-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--q-primary);
  text-transform: uppercase;
  letter-spacing: 0;
  margin-bottom: 8px;
}

.image-field-row {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: 8px;
  align-items: start;
  min-height: 30px;
  padding: 6px 0;
  border-bottom: 1px solid #edf0f3;
  font-size: 12px;
}

.image-field-row .k {
  color: #6b7280;
  font-weight: 600;
}

.image-field-row .v {
  color: #1f2937;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.image-product-section {
  position: relative;
  margin-top: 14px;
}

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

.image-size-stock-cell {
  min-height: 54px;
  padding: 8px 6px;
  border: 1px solid #dfe4ea;
  border-radius: 6px;
  background: #f8fafc;
  text-align: center;
}

.image-size-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: #6b7280;
}

.image-size-qty {
  display: block;
  margin-top: 3px;
  font-size: 16px;
  font-weight: 800;
  color: #111827;
}

.image-empty-text {
  color: #7a8390;
  font-size: 12px;
}

.product-image-carousel {
  height: 100%;
  background: #fff;
  border: 1px solid #d9dde3;
  border-radius: 8px;
}

.product-image-large {
  width: 100%;
  height: 100%;
}

.product-image-empty {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #7a8390;
  background: #fff;
  border: 1px solid #d9dde3;
  border-radius: 8px;
}

.image-metric-card .q-card__section {
  padding: 8px;
}

.image-metric-card .metric-value {
  font-size: 14px;
}

.product-performance-dialog {
  background: #f6f7f9;
}

.product-performance-dialog :deep(.q-table__container),
.product-image-dialog :deep(.q-table__container) {
  width: 100%;
  min-width: 0;
}

.product-performance-dialog :deep(.q-table__middle),
.product-image-dialog :deep(.q-table__middle) {
  overflow-x: hidden;
}

.dialog-detail-table,
.dialog-stock-table {
  width: 100%;
  min-width: 0;
  font-size: 11px;
}

.dialog-detail-table :deep(.q-table),
.dialog-stock-table :deep(.q-table) {
  width: 100%;
  table-layout: fixed;
}

.dialog-detail-table :deep(.q-table th),
.dialog-detail-table :deep(.q-table td),
.dialog-stock-table :deep(.q-table th),
.dialog-stock-table :deep(.q-table td) {
  padding: 4px 5px;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
  line-height: 1.18;
  vertical-align: top;
}

.dialog-detail-table :deep(.q-table th) {
  font-size: 10px;
}

.dialog-detail-table :deep(.q-table th:nth-child(1)),
.dialog-detail-table :deep(.q-table td:nth-child(1)) {
  width: 9%;
}

.dialog-detail-table :deep(.q-table th:nth-child(2)),
.dialog-detail-table :deep(.q-table td:nth-child(2)) {
  width: 12%;
}

.dialog-detail-table :deep(.q-table th:nth-child(3)),
.dialog-detail-table :deep(.q-table td:nth-child(3)) {
  width: 11%;
}

.dialog-detail-table :deep(.q-table th:nth-child(4)),
.dialog-detail-table :deep(.q-table td:nth-child(4)) {
  width: 9%;
}

.dialog-detail-table :deep(.q-table th:nth-child(5)),
.dialog-detail-table :deep(.q-table td:nth-child(5)) {
  width: 12%;
}

.dialog-detail-table :deep(.q-table th:nth-child(6)),
.dialog-detail-table :deep(.q-table td:nth-child(6)) {
  width: 23%;
}

.dialog-detail-table :deep(.q-table th:nth-child(7)),
.dialog-detail-table :deep(.q-table td:nth-child(7)),
.dialog-detail-table :deep(.q-table th:nth-child(8)),
.dialog-detail-table :deep(.q-table td:nth-child(8)),
.dialog-detail-table :deep(.q-table th:nth-child(9)),
.dialog-detail-table :deep(.q-table td:nth-child(9)) {
  width: 8%;
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: normal;
}

.dialog-stock-table :deep(.q-table th:nth-child(1)),
.dialog-stock-table :deep(.q-table td:nth-child(1)) {
  width: 58%;
}

.dialog-stock-table :deep(.q-table th:nth-child(2)),
.dialog-stock-table :deep(.q-table td:nth-child(2)) {
  width: 42%;
  white-space: nowrap;
}

@media (max-width: 1260px) {
  .product-image-dialog-body {
    overflow: auto;
  }

  .performance-image-layout {
    grid-template-columns: 1fr;
    height: auto;
  }

  .image-media-panel {
    min-height: 520px;
  }
}
</style>


