<template>
  <q-page class="q-pa-md full-width order-prod-page">
    <div class="row items-center justify-between page-header">
      <div>
        <div class="text-h6 text-weight-bold">Uretime Verilen Urunleri Guncelle</div>
      </div>
      <q-btn
        color="primary"
        icon="refresh"
        label="Yenile"
        :loading="store.loading"
        @click="refreshAll"
      />
      <q-btn
        class="q-ml-sm"
        color="secondary"
        icon="save"
        label="Secili Degisiklikleri Kaydet"
        :loading="store.saving"
        :disable="store.loading || store.saving || isBulkSubmitting"
        @click="onBulkSubmit"
      />
    </div>

    <div class="filter-bar row q-col-gutter-md">
      <div class="col-5">
        <q-input
          :model-value="cariLabel"
          label="Cari Secimi"
          filled
          dense
          readonly
        />
      </div>
      <div class="col-3">
        <q-input
          v-model="descFilter"
          label="Aciklama Ara"
          filled
          dense
          clearable
        />
      </div>
      <div class="col-2">
        <q-input
          :model-value="header?.OrderNumber || ''"
          label="Siparis No"
          filled
          dense
          readonly
        />
      </div>
      <div class="col-2">
        <q-input
          :model-value="formatDate(header?.OrderDate)"
          label="Olusturulma Tarihi"
          filled
          dense
          readonly
        />
      </div>
      <div class="col-2">
        <q-input
          v-model="headerAverageDueDate"
          label="Tahmini Termin Tarihi"
          filled
          dense
          type="date"
        />
      </div>
    </div>

    <div class="table-wrap">
      <q-table
        class="q-mt-md prod-table"
        flat
        bordered
        dense
        separator="cell"
        row-key="RowKey"
        :rows="filteredRows"
        :columns="columns"
        :loading="store.loading"
        no-data-label="Uretime verilecek urun bulunamadi"
        :rows-per-page-options="[0]"
        :table-style="{ tableLayout: 'fixed', width: '100%' }"
        hide-bottom
      >
        <template #header-cell-select="props">
          <q-th :props="props" class="text-center" style="width: 44px">
            <q-checkbox
              size="sm"
              :model-value="allSelectedVisible"
              :indeterminate="someSelectedVisible && !allSelectedVisible"
              @update:model-value="toggleSelectAllVisible"
            />
          </q-th>
        </template>

        <template #body-cell-select="props">
          <q-td :props="props" class="text-center" style="width: 44px">
            <q-checkbox
              size="sm"
              :model-value="!!selectedMap[props.row.RowKey]"
              :disable="store.saving"
              @update:model-value="(val) => toggleRowSelection(props.row.RowKey, val)"
            />
          </q-td>
        </template>

        <template #body-cell-NewItemCode="props">
          <q-td :props="props">
            <q-select
              v-model="props.row.NewItemEntryMode"
              dense
              filled
              emit-value
              map-options
              :options="newItemEntryModeOptions"
              label="Kod Giris Tipi"
              @update:model-value="val => onNewItemEntryModeChange(props.row, val)"
            />

            <q-select
              v-if="props.row.NewItemEntryMode === 'selected'"
              class="q-mt-xs"
              v-model="props.row.NewItemCode"
              dense
              filled
              use-input
              fill-input
              hide-selected
              input-debounce="0"
              emit-value
              map-options
              option-label="label"
              option-value="value"
              :options="productCodeSelectOptions"
              label="Eski Kod Sec"
              @filter="onFilterProductCode"
              @update:model-value="val => onSelectProduct(props.row, val)"
            />

            <q-input
              v-else-if="props.row.NewItemEntryMode === 'typed'"
              class="q-mt-xs"
              v-model="props.row.NewItemCode"
              dense
              filled
              maxlength="13"
              placeholder="X999-XX99999"
              label="Yeni Kod Ekle"
              :class="newItemInputClass(props.row)"
              @update:model-value="val => onNewItemChange(props.row, val, 'typed')"
            />

            <div v-if="props.row.NewItemMode && props.row.NewItemMode !== 'empty'" class="q-mt-xs row items-center no-wrap">
              <q-badge :color="newItemBadgeColor(props.row)" text-color="white">
                {{ newItemBadgeLabel(props.row) }}
              </q-badge>
              <span class="text-caption q-ml-sm text-grey-8">{{ newItemHintText(props.row) }}</span>
              <q-btn
                v-if="props.row.NewItemMode === 'new'"
                class="q-ml-sm"
                dense
                flat
                size="sm"
                color="warning"
                label="Urun Boyutlandirma"
                @click="openCdItemDialog(props.row.NewItemCode)"
              />
              <q-btn
                v-if="props.row.NewItemMode && props.row.NewItemMode !== 'empty'"
                class="q-ml-xs"
                dense
                flat
                size="sm"
                color="primary"
                label="Urun Ozellikleri"
                @click="openAttributeDialog(props.row.NewItemCode)"
              />
            </div>
          </q-td>
        </template>

        <template #body-cell-NewColor="props">
          <q-td :props="props">
            <q-select
              v-model="props.row.NewColor"
              :options="getColorOptions(props.row)"
              option-label="colorLabel"
              option-value="color_code"
              use-input
              fill-input
              hide-selected
              input-debounce="0"
              emit-value
              map-options
              dense
              filled
              label="Yeni Renk"
              :disable="isColorSelectionLocked(props.row)"
              @update:model-value="() => onNewColorChange(props.row)"
            />
          </q-td>
        </template>

        <template #body-cell-NewDim2="props">
          <q-td :props="props">
            <q-select
              v-model="props.row.NewDim2"
              :options="getSecondColorOptions(props.row)"
              option-label="item_dim2_label"
              option-value="item_dim2_code"
              use-input
              fill-input
              hide-selected
              input-debounce="0"
              emit-value
              map-options
              dense
              filled
              label="Yeni 2. Renk"
              :disable="isColorSelectionLocked(props.row)"
              @update:model-value="() => onNewDim2Change(props.row)"
            />
          </q-td>
        </template>

        <template #body-cell-NewDueDate="props">
          <q-td :props="props">
            <q-input
              v-model="props.row.NewDueDate"
              dense
              filled
              type="date"
              label="Yeni Termin"
            />
          </q-td>
        </template>

        <template #body-cell-NewDesc="props">
          <q-td :props="props" class="cell-new">
            <q-input
              v-model="props.row.NewDesc"
              dense
              filled
              type="textarea"
              autogrow
              label="Yeni Aciklama"
            />
          </q-td>
        </template>
      </q-table>
    </div>

    <q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
      Hata: {{ store.error }}
    </q-banner>

    <q-dialog v-model="cdItemDialogOpen" persistent>
      <q-card style="min-width: 980px; max-width: 98vw;">
        <q-card-section class="row items-center q-pb-none">
          <div class="text-h6">Urun Boyutlandirma</div>
          <q-space />
          <q-badge color="warning" text-color="black">
            {{ cdItemTargetCode || '-' }}
          </q-badge>
        </q-card-section>

        <q-card-section class="q-pt-md">
          <div class="row q-col-gutter-sm items-center q-mb-md bg-grey-2 q-pa-sm rounded-borders">
            <div class="col-12 col-md-8">
              <q-select
                v-model="copySourceCode"
                dense
                filled
                use-input
                fill-input
                hide-selected
                input-debounce="0"
                emit-value
                map-options
                option-label="label"
                option-value="value"
                label="Benzer Eski Urun Kodundan Getir"
                placeholder="Kopyalanacak urun kodunu yazin"
                :options="productCodeSelectOptions"
                @filter="onFilterProductCode"
              />
            </div>
            <div class="col-12 col-md-4">
              <q-btn
                color="secondary"
                icon="content_copy"
                label="Ozellikleri Kopyala"
                class="full-width"
                :disable="!copySourceCode"
                @click="copyFromOldProduct('cdItem')"
              />
            </div>
          </div>

          <div class="row q-col-gutter-sm">
            <div class="col-12 col-md-4">
              <q-select v-model="cdItemDraftForm.ItemDimTypeCode" dense filled use-input fill-input hide-selected input-debounce="0" emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemDimTypeCodes')" label="Boyut Secenekleri" />
            </div>
            <div class="col-12 col-md-4">
              <q-select v-model="cdItemDraftForm.ProductHierarchyID" dense filled use-input fill-input hide-selected input-debounce="0" emit-value map-options option-label="label" option-value="value" :options="lookupOptions('productHierarchyIDs')" label="Urun Hiyerarsi Grubu" />
            </div>
          </div>
        </q-card-section>

        <q-card-actions align="right">
          <q-btn flat label="Vazgec" color="grey-8" v-close-popup />
          <q-btn color="primary" label="Taslagi Kaydet" @click="saveCdItemDraft" />
        </q-card-actions>
      </q-card>
    </q-dialog>

    <q-dialog v-model="attributeDialogOpen" persistent>
      <q-card style="min-width: 1100px; max-width: 98vw;">
        <q-card-section class="row items-center q-pb-none">
          <div class="text-h6">Urun Ozellikleri (2. Pop-up)</div>
          <q-space />
          <q-badge color="primary">{{ attributeTargetCode || '-' }}</q-badge>
        </q-card-section>

        <q-card-section class="q-pt-md">
          <div class="row q-col-gutter-sm items-center q-mb-md bg-grey-2 q-pa-sm rounded-borders">
            <div class="col-12 col-md-8">
              <q-select
                v-model="copySourceCode"
                dense
                filled
                use-input
                fill-input
                hide-selected
                input-debounce="0"
                emit-value
                map-options
                option-label="label"
                option-value="value"
                label="Benzer Eski Urun Kodundan Getir"
                placeholder="Kopyalanacak urun kodunu yazin"
                :options="productCodeSelectOptions"
                @filter="onFilterProductCode"
              />
            </div>
            <div class="col-12 col-md-4">
              <q-btn
                color="secondary"
                icon="content_copy"
                label="Ozellikleri Kopyala"
                class="full-width"
                :disable="!copySourceCode"
                @click="copyFromOldProduct('attributes')"
              />
            </div>
          </div>
        </q-card-section>

        <q-card-section style="max-height: 68vh; overflow: auto;">
          <div
            v-for="(row, idx) in attributeRows"
            :key="`${row.AttributeTypeCodeNumber}-${idx}`"
            class="row q-col-gutter-sm q-mb-xs items-center"
          >
            <div class="col-12 col-md-5">
              <q-input :model-value="row.TypeLabel" dense filled readonly />
            </div>
            <div class="col-12 col-md-7">
              <q-select
                v-model="row.AttributeCode"
                dense
                filled
                use-input
                fill-input
                hide-selected
                input-debounce="0"
                @filter="(val, update) => onFilterAttributeOption(row, val, update)"
                emit-value
                map-options
                option-label="label"
                option-value="value"
                :options="row.Options"
              />
            </div>
          </div>
        </q-card-section>

        <q-card-actions align="right">
          <q-btn flat label="Vazgec" color="grey-8" v-close-popup />
          <q-btn color="primary" label="Ozellikleri Taslaga Kaydet" @click="saveAttributeDraft" />
        </q-card-actions>
      </q-card>
    </q-dialog>
    <q-inner-loading :showing="store.saving">
      <q-spinner-gears size="50px" color="primary" />
      <div class="q-mt-md text-subtitle1">Degisiklikler kaydediliyor, lutfen bekleyiniz...</div>
    </q-inner-loading>
  </q-page>
</template>

<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useQuasar } from 'quasar'
import { useOrderProductionItemStore } from 'src/stores/OrderProductionItemStore'
import api from 'src/services/api'
import { normalizeSearchText } from 'src/utils/searchText'

const route = useRoute()
const $q = useQuasar()
const store = useOrderProductionItemStore()
const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/
const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999'

function nowMs () {
  if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    return performance.now()
  }
  return Date.now()
}

const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
const header = computed(() => store.header || {})
const cariLabel = computed(() => {
  const code = header.value?.CurrAccCode || ''
  const name = header.value?.CurrAccDescription || ''
  if (!code && !name) return ''
  if (!name) return code
  return `${code} - ${name}`
})

const rows = ref([])
const descFilter = ref('')
const productOptions = ref([])
const selectedMap = ref({})
const headerAverageDueDate = ref('')
const cdItemDialogOpen = ref(false)
const cdItemTargetCode = ref('')
const copySourceCode = ref(null)
const suppressAutoSetupDialogs = ref(false)
const cdItemDraftForm = ref(createEmptyCdItemDraft(''))
const attributeDialogOpen = ref(false)
const attributeTargetCode = ref('')
const attributeRows = ref([])
const isBulkSubmitting = ref(false)

const columns = [
  { name: 'select', label: '', field: 'select', align: 'center', sortable: false, style: 'width:44px;', headerStyle: 'width:44px;' },
  { name: 'OldItemCode', label: 'Eski Urun Kodu', field: 'OldItemCode', align: 'left', sortable: true, style: 'min-width:90px;white-space:normal', headerStyle: 'min-width:90px;white-space:normal', headerClasses: 'col-old', classes: 'col-old' },
  { name: 'OldColor', label: 'Eski Urun Rengi', field: 'OldColorLabel', align: 'left', sortable: true, style: 'min-width:120px;white-space:normal', headerStyle: 'min-width:120px;white-space:normal', headerClasses: 'col-old', classes: 'col-old' },
  { name: 'OldDim2', label: 'Eski 2. Renk', field: 'OldDim2', align: 'left', sortable: true, style: 'min-width:80px;white-space:normal', headerStyle: 'min-width:80px;white-space:normal', headerClasses: 'col-old', classes: 'col-old' },
  { name: 'OldDesc', label: 'Eski Aciklama', field: 'OldDesc', align: 'left', sortable: false, style: 'min-width:130px;', headerStyle: 'min-width:130px;', headerClasses: 'col-old col-desc', classes: 'col-old col-desc' },
  { name: 'OldSizes', label: 'Bedenler', field: 'OldSizesLabel', align: 'left', sortable: false, style: 'min-width:90px;', headerStyle: 'min-width:90px;', headerClasses: 'col-old col-wrap', classes: 'col-old col-wrap' },
  { name: 'OldTotalQty', label: 'Siparis Adedi', field: 'OldTotalQtyLabel', align: 'right', sortable: false, style: 'min-width:90px;', headerStyle: 'min-width:90px;', headerClasses: 'col-old', classes: 'col-old' },
  { name: 'OldDueDate', label: 'Eski Termin', field: 'OldDueDate', align: 'left', sortable: true, style: 'min-width:100px;', headerStyle: 'min-width:100px;', headerClasses: 'col-old', classes: 'col-old' },
  { name: 'NewItemCode', label: 'Yeni Urun Kodu', field: 'NewItemCode', align: 'left', sortable: false, style: 'min-width:130px;', headerStyle: 'min-width:130px;', headerClasses: 'col-new col-new-first', classes: 'col-new col-new-first' },
  { name: 'NewColor', label: 'Yeni Urun Rengi', field: 'NewColor', align: 'left', sortable: false, style: 'min-width:100px;', headerStyle: 'min-width:100px;', headerClasses: 'col-new', classes: 'col-new' },
  { name: 'NewDim2', label: 'Yeni 2. Renk', field: 'NewDim2', align: 'left', sortable: false, style: 'min-width:100px;', headerStyle: 'min-width:100px;', headerClasses: 'col-new', classes: 'col-new' },
  { name: 'NewDueDate', label: 'Yeni Termin', field: 'NewDueDate', align: 'left', sortable: false, style: 'min-width:120px;', headerStyle: 'min-width:120px;', headerClasses: 'col-new', classes: 'col-new' },
  { name: 'NewDesc', label: 'Yeni Aciklama', field: 'NewDesc', align: 'left', sortable: false, style: 'min-width:140px;', headerStyle: 'min-width:140px;', headerClasses: 'col-new col-desc', classes: 'col-new col-desc' }
]

onMounted(async () => {
  await refreshAll()
})

watch(orderHeaderID, async (id) => {
  await refreshAll()
})

watch(
  () => store.items,
  (items) => {
    rows.value = groupItems(items || [], rows.value)
  },
  { immediate: true }
)

watch(
  () => store.products,
  (products) => {
    productOptions.value = products || []
  },
  { immediate: true }
)

function formatDate (val) {
  if (!val) return ''
  const text = String(val)
  return text.length >= 10 ? text.slice(0, 10) : text
}

function normalizeDateInput (val) {
  return formatDate(val || '')
}

const hasHeaderAverageDueDateChange = computed(() => (
  normalizeDateInput(headerAverageDueDate.value) !==
  normalizeDateInput(header.value?.AverageDueDate)
))

watch(
  () => header.value?.AverageDueDate,
  (value) => {
    headerAverageDueDate.value = normalizeDateInput(value)
  },
  { immediate: true }
)

const filteredRows = computed(() => {
  const needle = normalizeSearchText(descFilter.value)
  if (!needle) return rows.value
  return rows.value.filter(r =>
    normalizeSearchText(r?.OldDesc).includes(needle)
  )
})

const visibleRowKeys = computed(() => filteredRows.value.map(r => r.RowKey))
const selectedVisibleCount = computed(() => visibleRowKeys.value.filter(k => !!selectedMap.value[k]).length)
const allSelectedVisible = computed(() => visibleRowKeys.value.length > 0 && selectedVisibleCount.value === visibleRowKeys.value.length)
const someSelectedVisible = computed(() => selectedVisibleCount.value > 0)
const newItemEntryModeOptions = [
  { label: 'Eski Kod Sec', value: 'selected' },
  { label: 'Yeni Kod Ekle', value: 'typed' }
]
const productCodeAllOptions = computed(() =>
  (productOptions.value || []).map(p => {
    const code = String(p?.ProductCode || '').trim().toUpperCase()
    return { label: code, value: code }
  }).filter(x => !!x.value && x.value.length === 13)
)
const productCodeSelectOptions = ref([])

watch(
  productCodeAllOptions,
  (list) => {
    productCodeSelectOptions.value = Array.isArray(list) ? [...list] : []
  },
  { immediate: true }
)

function onFilterProductCode (val, update) {
  const needle = normalizeSearchText(val)
  update(() => {
    if (!needle) {
      productCodeSelectOptions.value = [...productCodeAllOptions.value]
      return
    }
    productCodeSelectOptions.value = (productCodeAllOptions.value || []).filter(opt => {
      const label = normalizeSearchText(opt?.label || '')
      const value = normalizeSearchText(opt?.value || '')
      return label.includes(needle) || value.includes(needle)
    })
  })
}

function applyNewItemVisualState (row, source = 'typed') {
  const info = store.classifyItemCode(row?.NewItemCode || '')
  row.NewItemCode = info.normalized
  row.NewItemMode = info.mode
  row.NewItemSource = info.mode === 'empty' ? '' : source
}

function syncRowsForKnownExistingCode (itemCode) {
  const code = String(itemCode || '').trim().toUpperCase()
  if (!code) return
  for (const row of (rows.value || [])) {
    if (String(row?.NewItemCode || '').trim().toUpperCase() !== code) continue
    row.NewItemCode = code
    row.NewItemMode = 'existing'
    if (!row.NewItemEntryMode) {
      row.NewItemEntryMode = row.NewItemSource === 'selected' ? 'selected' : 'typed'
    }
  }
}

function newItemInputClass (row) {
  return {
    'new-item-existing': row?.NewItemMode === 'existing',
    'new-item-new': row?.NewItemMode === 'new'
  }
}

function newItemBadgeColor (row) {
  return row?.NewItemMode === 'existing' ? 'positive' : 'warning'
}

function newItemBadgeLabel (row) {
  return row?.NewItemMode === 'existing' ? 'MEVCUT KOD' : 'YENI KOD'
}

function newItemHintText (row) {
  if (row?.NewItemMode === 'existing') {
    return row?.NewItemSource === 'selected'
      ? 'Urun listesinden secildi'
      : 'Elle girildi (sistemde bulundu)'
  }
  if (row?.NewItemMode === 'new') {
    return store.getCdItemDraft(row?.NewItemCode) ? 'Yeni kod: cdItem taslagi hazir' : 'Yeni kod: cdItem taslagi gerekli'
  }
  return ''
}

function onSelectProduct (row, code) {
  row.NewItemEntryMode = 'selected'
  onNewItemChange(row, code, 'selected')
}

function onNewItemEntryModeChange (row, mode) {
  row.NewItemEntryMode = String(mode || '').trim()
  row.NewItemCode = ''
  row.NewColor = ''
  row.NewDim2 = ''
  row.NewItemMode = 'empty'
  row.NewItemSource = ''
}

function escapeRegExp (value) {
  return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

function buildNextCodeFromPrefix (prefix) {
  const normalizedPrefix = String(prefix || '').trim().toUpperCase()
  if (!/^[A-Z][0-9]{3}-[A-Z]{3}$/.test(normalizedPrefix)) return ''

  const codeRegex = new RegExp(`^${escapeRegExp(normalizedPrefix)}(\\d{5})$`)
  let maxSuffix = 0

  for (const p of (productOptions.value || [])) {
    const code = String(p?.ProductCode || '').trim().toUpperCase()
    const m = code.match(codeRegex)
    if (!m) continue
    const n = Number(m[1] || 0)
    if (Number.isFinite(n) && n > maxSuffix) maxSuffix = n
  }

  const next = maxSuffix > 0 ? maxSuffix + 1 : 1
  return `${normalizedPrefix}${String(next).padStart(5, '0')}`
}

function onNewItemChange (row, val, source = 'typed') {
  const prevCode = String(row?.NewItemCode || '').trim().toUpperCase()
  let next = String(val || '').trim().toUpperCase()

  if (source === 'typed' && row?.NewItemEntryMode === 'typed' && /^[A-Z][0-9]{3}-[A-Z]{3}$/.test(next)) {
    const autoCode = buildNextCodeFromPrefix(next)
    if (autoCode) {
      next = autoCode
      $q.notify({ type: 'info', message: `Yeni kod otomatik tamamlandi: ${autoCode}` })
    }
  }

  if (next.length > 13) {
    $q.notify({ type: 'negative', message: 'Model kodu en fazla 13 karakter olabilir.' })
    row.NewItemCode = next.slice(0, 13)
    applyNewItemVisualState(row, source)
    return
  }
  if (next.length === 13 && !isValidBaggiModelCode(next)) {
    $q.notify({ type: 'negative', message: BAGGI_CODE_ERROR })
    row.NewItemCode = prevCode
    applyNewItemVisualState(row, source)
    return
  }
  row.NewItemCode = next ? next.toUpperCase() : ''
  applyNewItemVisualState(row, source)
  row.NewColor = ''
  row.NewDim2 = ''
  row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
  if (row.NewItemCode && isValidBaggiModelCode(row.NewItemCode)) {
    if (row.NewItemMode === 'new') {
      store.fetchNewColors(row.NewItemCode)
    } else {
      store.fetchColors(row.NewItemCode)
    }
  }
  if (suppressAutoSetupDialogs.value) return
  if (row.NewItemMode === 'new' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) {
    openNewCodeSetupFlow(row.NewItemCode)
  } else if (row.NewItemMode === 'existing' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) {
    openAttributeDialog(row.NewItemCode)
  }
}

function isAttributeDraftComplete (rows) {
  if (!Array.isArray(rows) || !rows.length) return false
  return rows.every(r => String(r?.AttributeCode || '').trim())
}

function isNewCodeSetupComplete (itemCode) {
  const code = String(itemCode || '').trim().toUpperCase()
  if (!code) return false
  const hasCdItem = !!store.getCdItemDraft(code)
  const attrRows = store.getProductAttributeDraft(code)
  return hasCdItem && isAttributeDraftComplete(attrRows)
}

function isColorSelectionLocked (row) {
  const code = String(row?.NewItemCode || '').trim().toUpperCase()
  return !code
}

function openNewCodeSetupFlow (itemCode) {
  const code = String(itemCode || '').trim().toUpperCase()
  if (!code) return
  if (!store.getCdItemDraft(code)) {
    openCdItemDialog(code)
    return
  }
  if (!isAttributeDraftComplete(store.getProductAttributeDraft(code))) {
    openAttributeDialog(code)
  }
}

function onNewColorChange (row) {
  row.NewColor = normalizeShortCode(row.NewColor, 3)
  row.NewDim2 = ''
  row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
  if (row.NewItemCode && row.NewColor) {
    if (String(row?.NewItemMode || '').trim() === 'new') {
      store.fetchNewSecondColors(row.NewItemCode, row.NewColor)
    } else {
      store.fetchSecondColors(row.NewItemCode, row.NewColor)
    }
  }
}

function onNewDim2Change (row) {
  row.NewDim2 = normalizeShortCode(row.NewDim2, 3)
  row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
}

function getColorOptions (row) {
  const code = row?.NewItemCode || ''
  const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
  const list = isNewMode
    ? (store.newColorOptionsByCode[code] || [])
    : (store.colorOptionsByCode[code] || [])
  return list.map(c => ({
    ...c,
    colorLabel: `${c.color_code} - ${c.color_description || ''}`.trim()
  }))
}

function getSecondColorOptions (row) {
  const code = row?.NewItemCode || ''
  const color = row?.NewColor || ''
  const key = `${code}::${color}`
  const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
  const list = isNewMode
    ? (store.newSecondColorOptionsByKey[key] || [])
    : (store.secondColorOptionsByKey[key] || [])
  return list.map(c => ({
    ...c,
    item_dim2_label: `${c.item_dim2_code} - ${c.color_description || ''}`.trim()
  }))
}

function toggleRowSelection (rowKey, checked) {
  const next = { ...selectedMap.value }
  if (checked) next[rowKey] = true
  else delete next[rowKey]
  selectedMap.value = next
}

function toggleSelectAllVisible (checked) {
  const next = { ...selectedMap.value }
  for (const key of visibleRowKeys.value) {
    if (checked) next[key] = true
    else delete next[key]
  }
  selectedMap.value = next
}

function normalizeShortCode (value, maxLen) {
  return String(value || '').trim().toUpperCase().slice(0, maxLen)
}

function isValidBaggiModelCode (code) {
  return BAGGI_CODE_PATTERN.test(code)
}

function formatCodeColorDim2 (itemCode, color, dim2) {
  const item = String(itemCode || '').trim().toUpperCase()
  const c1 = normalizeShortCode(color, 3)
  const c2 = normalizeShortCode(dim2, 3)
  const c1Safe = c1 || '-'
  const c2Safe = c2 || '-'
  return `${item}/${c1Safe}/${c2Safe}`
}

function buildAutoUpdateNote (row) {
  const oldInfo = formatCodeColorDim2(row?.OldItemCode, row?.OldColor, row?.OldDim2)
  const nextInfo = formatCodeColorDim2(row?.NewItemCode, row?.NewColor, row?.NewDim2)
  return `Bu siparis satirinda kod ${oldInfo} bilgisinden ${nextInfo} bilgisine guncellenmistir.`
}

function isSelectionCompleteByOldShape (row) {
  const hasModel = String(row?.NewItemCode || '').trim().length > 0
  if (!hasModel) return false

  const oldHasColor = String(row?.OldColor || '').trim().length > 0
  const hasNewColor = normalizeShortCode(row?.NewColor, 3).length === 3

  if (oldHasColor) return hasNewColor
  return true
}

function stripAutoUpdateNote (text) {
  const desc = String(text || '').trim()
  if (!desc) return ''
  const marker = ' Bu siparis satirinda kod '
  const idx = desc.indexOf(marker)
  if (idx > -1) return desc.slice(0, idx).trim()
  if (desc.startsWith('Bu siparis satirinda kod ')) return ''
  return desc
}

function mergeDescWithAutoNote (row, baseDesc) {
  const desc = stripAutoUpdateNote(baseDesc)
  if (!isSelectionCompleteByOldShape(row)) return desc
  const note = buildAutoUpdateNote(row)
  if (!note) return desc
  if (desc.includes(note)) return desc
  if (!desc) return note
  return `${desc} ${note}`
}

function validateRowInput (row) {
  const entryMode = String(row?.NewItemEntryMode || '').trim()
  const newItemCode = String(row.NewItemCode || '').trim().toUpperCase()
  const newColor = normalizeShortCode(row.NewColor, 3)
  const newDim2 = normalizeShortCode(row.NewDim2, 3)
  const oldColor = String(row.OldColor || '').trim()

  if (!entryMode) return 'Lutfen once kod giris tipini seciniz (Eski Kod Sec / Yeni Kod Ekle).'
  if (!newItemCode) return 'Yeni model kodu zorunludur.'
  if (!isValidBaggiModelCode(newItemCode)) {
    return BAGGI_CODE_ERROR
  }
  if (oldColor && !newColor) return 'Eski kayitta 1. renk oldugu icin yeni 1. renk zorunludur.'
  if (newColor && newColor.length !== 3) return 'Yeni 1. renk kodu 3 karakter olmalidir.'
  if (newDim2 && newDim2.length !== 3) return 'Yeni 2. renk kodu 3 karakter olmalidir.'
  if (newDim2 && !newColor) return '2. renk girmek icin 1. renk zorunludur.'

  row.NewItemCode = newItemCode
  row.NewColor = newColor
  row.NewDim2 = newDim2
  row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
  return ''
}

function collectLinesFromRows (selectedRows) {
  const lines = []
  for (const row of selectedRows) {
    const errMsg = validateRowInput(row)
    if (errMsg) {
      return { errMsg, lines: [] }
    }

    const baseLine = {
      NewItemCode: String(row.NewItemCode || '').trim().toUpperCase(),
      NewColor: normalizeShortCode(row.NewColor, 3),
      NewDim2: normalizeShortCode(row.NewDim2, 3),
      NewDesc: mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc),
      OldDueDate: row.OldDueDate || '',
      NewDueDate: row.NewDueDate || ''
    }

    const oldItemCode = String(row.OldItemCode || '').trim().toUpperCase()
    const oldColor = normalizeShortCode(row.OldColor, 3)
    const oldDim2 = normalizeShortCode(row.OldDim2, 3)
    const oldDesc = String(row.OldDesc || '').trim()
    const oldDueDateValue = row.OldDueDate || ''
    const newDueDateValue = row.NewDueDate || ''

    const hasChange = (
      baseLine.NewItemCode !== oldItemCode ||
      baseLine.NewColor !== oldColor ||
      baseLine.NewDim2 !== oldDim2 ||
      String(baseLine.NewDesc || '').trim() !== oldDesc ||
      newDueDateValue !== oldDueDateValue
    )
    if (!hasChange) continue

    const orderLines = Array.isArray(row.OrderLines) && row.OrderLines.length
      ? row.OrderLines
      : (row.OrderLineIDs || []).map(id => ({
          OrderLineID: id,
          ItemDim1Code: ''
        }))

    for (const line of orderLines) {
      lines.push({
        ...baseLine,
        OrderLineID: line?.OrderLineID,
        ItemDim1Code: store.toPayloadDim1Code(row, line?.ItemDim1Code || '')
      })
    }
  }
  return { errMsg: '', lines }
}

function hasRowChange (row) {
  const newItemCode = String(row?.NewItemCode || '').trim().toUpperCase()
  const newColor = normalizeShortCode(row?.NewColor, 3)
  const newDim2 = normalizeShortCode(row?.NewDim2, 3)
  const newDesc = mergeDescWithAutoNote(row, row?.NewDesc || row?.OldDesc)
  const oldItemCode = String(row?.OldItemCode || '').trim().toUpperCase()
  const oldColor = normalizeShortCode(row?.OldColor, 3)
  const oldDim2 = normalizeShortCode(row?.OldDim2, 3)
  const oldDesc = String(row?.OldDesc || '').trim()
  const oldDueDateValue = row?.OldDueDate || ''
  const newDueDateValue = row?.NewDueDate || ''

  return (
    newItemCode !== oldItemCode ||
    newColor !== oldColor ||
    newDim2 !== oldDim2 ||
    String(newDesc || '').trim() !== oldDesc ||
    newDueDateValue !== oldDueDateValue
  )
}

function collectOptionalColorWarnings (rows) {
  const warnings = []
  for (const row of (rows || [])) {
    const code = String(row?.NewItemCode || '').trim().toUpperCase()
    if (!code) continue
    const color = normalizeShortCode(row?.NewColor, 3)
    const dim2 = normalizeShortCode(row?.NewDim2, 3)
    if (!color) {
      warnings.push(`${code} icin renk secmediniz.`)
      continue
    }
    if (!dim2) {
      warnings.push(`${code} icin 2. renk bos kalacak.`)
    }
  }
  return [...new Set(warnings)]
}

function confirmOptionalColorWarnings (rows) {
  const warnings = collectOptionalColorWarnings(rows)
  if (!warnings.length) return Promise.resolve(true)
  return new Promise((resolve) => {
    $q.dialog({
      title: 'Renk Uyarisi',
      message: `${warnings.join('<br>')}<br><br>Devam etmek istiyor musunuz?`,
      html: true,
      ok: { label: 'Evet, Devam Et', color: 'warning' },
      cancel: { label: 'Vazgec', flat: true }
    }).onOk(() => resolve(true)).onCancel(() => resolve(false)).onDismiss(() => resolve(false))
  })
}

function createEmptyCdItemDraft (itemCode) {
  return {
    ItemTypeCode: '1',
    ItemCode: String(itemCode || '').trim().toUpperCase(),
    ItemDimTypeCode: '1',
    ProductTypeCode: '1',
    ProductHierarchyID: '',
    UnitOfMeasureCode1: 'AD',
    ItemAccountGrCode: '',
    ItemTaxGrCode: '%10',
    ItemPaymentPlanGrCode: '',
    ItemDiscountGrCode: '',
    ItemVendorGrCode: '',
    PromotionGroupCode: '',
    ProductCollectionGrCode: '0',
    StorePriceLevelCode: '0',
    PerceptionOfFashionCode: '0',
    CommercialRoleCode: '0',
    StoreCapacityLevelCode: '',
    CustomsTariffNumberCode: '',
    CompanyCode: '1'
  }
}

function lookupOptions (key) {
  if (key === 'itemDimTypeCodes') {
    return [
      { value: '1', label: '1 - RENK' },
      { value: '2', label: '2 - RENK-BEDEN' },
      { value: '3', label: '3 - RENK-BEDEN-YAKA' }
    ]
  }

  const list = store.cdItemLookups?.[key] || []
  return list
    .map(x => {
      const code = String(x?.code || '').trim()
      const desc = String(x?.description || '').trim()
      return {
        value: code,
        label: desc ? `${code} - ${desc}` : code,
        _desc: desc
      }
    })
    .filter(opt => !isDummyLookupOption(key, opt.value, opt._desc))
    .map(({ value, label }) => ({ value, label }))
}

function isDummyLookupOption (key, codeRaw, descRaw) {
  const code = String(codeRaw || '').trim().toUpperCase()
  const desc = String(descRaw || '').trim().toUpperCase()

  if (!code) return true
  if (code === '0' || code === '00' || code === '000' || code === '0000') return true
  if (desc.includes('DUMMY')) return true

  return false
}

async function copyFromOldProduct (targetType = 'cdItem') {
  const sourceCode = String(copySourceCode.value || '').trim().toUpperCase()
  if (!sourceCode) return

  $q.loading.show({ message: 'Ozellikler kopyalaniyor...' })
  try {
    if (targetType === 'cdItem') {
      const data = await store.fetchCdItemByCode(sourceCode)
      if (data) {
        const targetCode = String(cdItemTargetCode.value || '').trim().toUpperCase()
        const draft = createEmptyCdItemDraft(targetCode)
        for (const k of Object.keys(draft)) {
          if (data[k] !== undefined && data[k] !== null) {
            draft[k] = String(data[k])
          }
        }
        // Source item kopyalansa da hedef popup kodu degismemeli.
        draft.ItemCode = targetCode
        cdItemDraftForm.value = draft
        persistCdItemDraft()
        $q.notify({ type: 'positive', message: 'Boyutlandirma bilgileri kopyalandi.' })
      } else {
        $q.notify({ type: 'warning', message: 'Kaynak urun bilgisi bulunamadi.' })
      }
    } else if (targetType === 'attributes') {
      const data = await store.fetchProductItemAttributes(sourceCode, 1, true)
      if (Array.isArray(data) && data.length > 0) {
        // Mevcut attributeRows uzerindeki degerleri guncelle
        for (const row of attributeRows.value) {
          const sourceAttr = data.find(d => Number(d.attribute_type_code || d.AttributeTypeCode) === Number(row.AttributeTypeCodeNumber))
          if (sourceAttr) {
            const attrCode = String(sourceAttr.attribute_code || sourceAttr.AttributeCode || '').trim()
            if (attrCode) {
              // Seceneklerde var mi kontrol et, yoksa ekle (UI'da gorunmesi icin)
              if (!row.AllOptions.some(opt => String(opt.value).trim() === attrCode)) {
                row.AllOptions.unshift({ value: attrCode, label: attrCode })
                row.Options = [...row.AllOptions]
              }
              row.AttributeCode = attrCode
            }
          }
        }
        const targetCode = String(attributeTargetCode.value || '').trim().toUpperCase()
        if (targetCode) {
          store.setProductAttributeDraft(targetCode, JSON.parse(JSON.stringify(attributeRows.value || [])))
        }
        $q.notify({ type: 'positive', message: 'Urun ozellikleri kopyalandi.' })
      } else {
        $q.notify({ type: 'warning', message: 'Kaynak urun ozellikleri bulunamadi.' })
      }
    }
  } catch (err) {
    console.error('[OrderProductionUpdate] copyFromOldProduct failed', err)
    $q.notify({ type: 'negative', message: 'Kopyalama sirasinda hata olustu.' })
  } finally {
    $q.loading.hide()
  }
}

async function openCdItemDialog (itemCode) {
  const code = String(itemCode || '').trim().toUpperCase()
  if (!code) return
  copySourceCode.value = null
  await store.fetchCdItemLookups()

  cdItemTargetCode.value = code
  const existing = store.getCdItemDraft(code)
  const draft = createEmptyCdItemDraft(code)
  if (existing) {
    for (const [k, v] of Object.entries(existing)) {
      if (v == null) continue
      draft[k] = String(v)
    }
  }
  cdItemDraftForm.value = draft
  cdItemDialogOpen.value = true
}

function persistCdItemDraft () {
  const targetCode = String(cdItemTargetCode.value || '').trim().toUpperCase()
  const payload = normalizeCdItemDraftForPayload({
    ...(cdItemDraftForm.value || {}),
    ItemCode: targetCode || String(cdItemDraftForm.value?.ItemCode || '').trim().toUpperCase()
  })
  if (!payload.ItemCode) return null
  store.setCdItemDraft(payload.ItemCode, payload)
  return payload
}

function normalizeCdItemDraftForPayload (draftRaw) {
  const d = draftRaw || {}
  const toIntOrNil = (v) => {
    const n = Number(v)
    return Number.isFinite(n) && n > 0 ? n : null
  }
  const toStrOrNil = (v) => {
    const s = String(v || '').trim()
    return s || null
  }
  return {
    ItemTypeCode: toIntOrNil(d.ItemTypeCode) || 1,
    ItemCode: String(d.ItemCode || '').trim().toUpperCase(),
    ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode) || 1,
    ProductTypeCode: null,
    ProductHierarchyID: toIntOrNil(d.ProductHierarchyID),
    UnitOfMeasureCode1: 'AD',
    ItemAccountGrCode: null,
    ItemTaxGrCode: '%10',
    ItemPaymentPlanGrCode: null,
    ItemDiscountGrCode: null,
    ItemVendorGrCode: null,
    PromotionGroupCode: null,
    ProductCollectionGrCode: null,
    StorePriceLevelCode: null,
    PerceptionOfFashionCode: null,
    CommercialRoleCode: null,
    StoreCapacityLevelCode: null,
    CustomsTariffNumberCode: null,
    CompanyCode: '1'
  }
}

async function saveCdItemDraft () {
  const payload = persistCdItemDraft()
  if (!payload?.ItemCode) {
    $q.notify({ type: 'negative', message: 'ItemCode bos olamaz.' })
    return
  }
  console.info('[OrderProductionUpdate] saveCdItemDraft', {
    code: payload.ItemCode,
    itemDimTypeCode: payload.ItemDimTypeCode,
    productHierarchyID: payload.ProductHierarchyID
  })
  cdItemDialogOpen.value = false
  await openAttributeDialog(payload.ItemCode)
}

function buildAttributeRowsFromLookup (list) {
  const grouped = new Map()
  for (const it of (list || [])) {
    const typeCode = Number(it?.attribute_type_code || it?.AttributeTypeCode || 0)
    if (!typeCode) continue
    if (!grouped.has(typeCode)) {
      grouped.set(typeCode, {
        typeCode,
        typeDesc: String(it?.attribute_type_description || it?.AttributeTypeDescription || '').trim() || String(typeCode),
        options: []
      })
    }
    const g = grouped.get(typeCode)
    const code = String(it?.attribute_code || it?.AttributeCode || '').trim()
    const desc = String(it?.attribute_description || it?.AttributeDescription || '').trim()
    if (!code) continue
    g.options.push({
      value: code,
      label: `${code} - ${desc || code}`
    })
  }

  const rows = [...grouped.values()]
    .sort((a, b) => a.typeCode - b.typeCode)
    .map(g => ({
      AttributeTypeCodeNumber: g.typeCode,
      TypeLabel: `${g.typeCode} - ${g.typeDesc}`,
      AttributeCode: '',
      AllOptions: [...g.options],
      Options: [...g.options]
    }))
  return rows
}

function onFilterAttributeOption (row, val, update) {
  const raw = String(val || '')
  const needle = normalizeSearchText(raw)
  const base = Array.isArray(row?.AllOptions) ? row.AllOptions : (Array.isArray(row?.Options) ? row.Options : [])
  update(() => {
    if (!needle) {
      row.Options = [...base]
      return
    }
    row.Options = base.filter(opt => {
      const label = normalizeSearchText(opt?.label || '')
      const value = normalizeSearchText(opt?.value || '')
      return label.includes(needle) || value.includes(needle)
    })
  })
}

function mergeAttributeDraftWithLookupOptions (draftRows, lookupRows) {
  const byType = new Map(
    (lookupRows || []).map(r => [Number(r?.AttributeTypeCodeNumber || 0), r]).filter(x => x[0] > 0)
  )

  return (draftRows || []).map(d => {
    const typeCode = Number(d?.AttributeTypeCodeNumber || 0)
    const base = byType.get(typeCode)
    const selectedCode = String(d?.AttributeCode || '').trim()

    const baseAllOptions = Array.isArray(base?.AllOptions)
      ? [...base.AllOptions]
      : (Array.isArray(base?.Options) ? [...base.Options] : [])

    const draftAllOptions = Array.isArray(d?.AllOptions)
      ? [...d.AllOptions]
      : (Array.isArray(d?.Options) ? [...d.Options] : [])

    const allOptions = baseAllOptions.length ? baseAllOptions : draftAllOptions

    if (selectedCode && !allOptions.some(opt => String(opt?.value || '').trim() === selectedCode)) {
      allOptions.unshift({ value: selectedCode, label: selectedCode })
    }

    return {
      ...(base || d),
      ...d,
      AttributeTypeCodeNumber: typeCode,
      AttributeCode: selectedCode,
      AllOptions: allOptions,
      Options: [...allOptions]
    }
  })
}

async function openAttributeDialog (itemCode) {
  const code = String(itemCode || '').trim().toUpperCase()
  if (!code) return
  copySourceCode.value = null
  attributeTargetCode.value = code
  const existingDraft = JSON.parse(JSON.stringify(store.getProductAttributeDraft(code) || []))
  const modeInfo = store.classifyItemCode(code)
  const fetched = await store.fetchProductAttributes(1)
  const fromLookup = buildAttributeRowsFromLookup(fetched)
  console.info('[OrderProductionUpdate] openAttributeDialog lookup', {
    code,
    mode: modeInfo.mode,
    fetchedCount: Array.isArray(fetched) ? fetched.length : 0,
    rowCount: fromLookup.length
  })
  if (!fromLookup.length) {
    $q.notify({ type: 'negative', message: 'Urun ozellikleri listesi alinamadi. Lutfen daha sonra tekrar deneyin.' })
    return
  }

  // Draft varsa popup her zaman draft'tan acilir (yeniden acinca secimler kaybolmasin).
  if (Array.isArray(existingDraft) && existingDraft.length) {
    attributeRows.value = JSON.parse(JSON.stringify(
      mergeAttributeDraftWithLookupOptions(existingDraft, fromLookup)
    ))
    console.info('[OrderProductionUpdate] openAttributeDialog rowsPrepared', {
      code,
      mode: modeInfo.mode,
      useDraft: true,
      rowCount: Array.isArray(attributeRows.value) ? attributeRows.value.length : 0,
      optionCounts: (attributeRows.value || []).map(r => ({
        type: Number(r?.AttributeTypeCodeNumber || 0),
        options: Array.isArray(r?.Options) ? r.Options.length : 0,
        allOptions: Array.isArray(r?.AllOptions) ? r.AllOptions.length : 0,
        selected: String(r?.AttributeCode || '').trim()
      }))
    })
    for (const row of (attributeRows.value || [])) {
      if (!Array.isArray(row.AllOptions)) row.AllOptions = Array.isArray(row.Options) ? [...row.Options] : []
      if (!Array.isArray(row.Options)) row.Options = [...row.AllOptions]
    }
    attributeDialogOpen.value = true
    return
  }

  const dbCurrent = await store.fetchProductItemAttributes(code, 1, true)
  console.info('[OrderProductionUpdate] openAttributeDialog dbCurrent', {
    code,
    dbCurrentCount: Array.isArray(dbCurrent) ? dbCurrent.length : 0
  })
  if (Array.isArray(dbCurrent) && dbCurrent.length) {
    store.markItemCodeKnownExisting(code, true)
    syncRowsForKnownExistingCode(code)
  }

  const dbMap = new Map(
    (dbCurrent || []).map(x => [
      Number(x?.attribute_type_code || x?.AttributeTypeCode || 0),
      String(x?.attribute_code || x?.AttributeCode || '').trim()
    ]).filter(x => x[0] > 0)
  )

  const baseRows = fromLookup.map(row => {
    const currentCode = dbMap.get(Number(row.AttributeTypeCodeNumber || 0)) || ''
    const currentOptions = Array.isArray(row.AllOptions)
      ? [...row.AllOptions]
      : (Array.isArray(row.Options) ? [...row.Options] : [])
    if (currentCode && !currentOptions.some(opt => String(opt?.value || '').trim() === currentCode)) {
      currentOptions.unshift({ value: currentCode, label: currentCode })
    }
    return {
      ...row,
      AttributeCode: currentCode,
      OriginalAttributeCode: currentCode,
      AllOptions: currentOptions,
      Options: [...currentOptions]
    }
  })

  const useDraft = Array.isArray(existingDraft) && existingDraft.length
  attributeRows.value = JSON.parse(JSON.stringify(baseRows))
  console.info('[OrderProductionUpdate] openAttributeDialog rowsPrepared', {
    code,
    mode: modeInfo.mode,
    useDraft: false,
    rowCount: Array.isArray(attributeRows.value) ? attributeRows.value.length : 0,
    optionCounts: (attributeRows.value || []).map(r => ({
      type: Number(r?.AttributeTypeCodeNumber || 0),
      options: Array.isArray(r?.Options) ? r.Options.length : 0,
      allOptions: Array.isArray(r?.AllOptions) ? r.AllOptions.length : 0,
      selected: String(r?.AttributeCode || '').trim()
    }))
  })
  for (const row of (attributeRows.value || [])) {
    if (!Array.isArray(row.AllOptions)) {
      row.AllOptions = Array.isArray(row.Options) ? [...row.Options] : []
    }
    if (!Array.isArray(row.Options)) {
      row.Options = [...row.AllOptions]
    }
  }
  attributeDialogOpen.value = true
}

function saveAttributeDraft () {
  const code = String(attributeTargetCode.value || '').trim().toUpperCase()
  if (!code) return
  const rows = JSON.parse(JSON.stringify(attributeRows.value || []))
  for (const row of rows) {
    const selected = String(row?.AttributeCode || '').trim()
    if (!selected) {
      $q.notify({ type: 'negative', message: `Urun ozelliklerinde secim zorunlu: ${row?.TypeLabel || ''}` })
      return
    }
  }
  store.setProductAttributeDraft(code, rows)
  console.info('[OrderProductionUpdate] saveAttributeDraft', {
    code,
    rowCount: rows.length,
    selectedCount: rows.length,
    selected: rows.map(r => ({
      type: Number(r?.AttributeTypeCodeNumber || 0),
      code: String(r?.AttributeCode || '').trim()
    }))
  })
  attributeDialogOpen.value = false
  $q.notify({ type: 'positive', message: 'Urun ozellikleri taslagi kaydedildi.' })
}

watch(
  cdItemDraftForm,
  () => {
    if (!cdItemDialogOpen.value) return
    persistCdItemDraft()
  },
  { deep: true }
)

async function collectProductAttributesFromSelectedRows (selectedRows) {
  const codeSet = [...new Set(
    (selectedRows || [])
      .map(r => String(r?.NewItemCode || '').trim().toUpperCase())
      .filter(Boolean)
  )]
  const out = []

  for (const code of codeSet) {
    const modeInfo = store.classifyItemCode(code)
    let rows = store.getProductAttributeDraft(code)
    const dbCurrent = await store.fetchProductItemAttributes(code, 1, true)
    const dbMap = new Map(
      (dbCurrent || []).map(x => [
        Number(x?.attribute_type_code || x?.AttributeTypeCode || 0),
        String(x?.attribute_code || x?.AttributeCode || '').trim()
      ]).filter(x => x[0] > 0)
    )
    const hasDbAttributes = dbMap.size > 0
    const effectiveMode = hasDbAttributes ? 'existing' : modeInfo.mode
    console.info('[OrderProductionUpdate] collectProductAttributes start', {
      code,
      mode: modeInfo.mode,
      effectiveMode,
      hasDbAttributes,
      draftRowCount: Array.isArray(rows) ? rows.length : 0
    })

    if (effectiveMode === 'existing') {
      // Existing kodda kullanıcı değişiklik yaptıysa draftı koru.
      // Draft yoksa DB'den zorunlu/fresh çek.
      if (!Array.isArray(rows) || !rows.length) {
        const lookup = await store.fetchProductAttributes(1)
        const baseRows = buildAttributeRowsFromLookup(lookup)
        console.info('[OrderProductionUpdate] collectProductAttributes existing refetch', {
          code,
          lookupCount: Array.isArray(lookup) ? lookup.length : 0,
          baseRowCount: baseRows.length
        })
        rows = baseRows.map(row => {
          const currentCode = dbMap.get(Number(row.AttributeTypeCodeNumber || 0)) || ''
          const currentOptions = Array.isArray(row.AllOptions)
            ? [...row.AllOptions]
            : (Array.isArray(row.Options) ? [...row.Options] : [])
          if (currentCode && !currentOptions.some(opt => String(opt?.value || '').trim() === currentCode)) {
            currentOptions.unshift({ value: currentCode, label: currentCode })
          }
          return {
            ...row,
            AttributeCode: currentCode,
            OriginalAttributeCode: currentCode,
            AllOptions: currentOptions,
            Options: [...currentOptions]
          }
        })
        store.setProductAttributeDraft(code, JSON.parse(JSON.stringify(rows)))
      }
    } else if (!Array.isArray(rows) || !rows.length) {
      return { errMsg: `${code} icin urun ozellikleri taslagi kaydedilmedi`, productAttributes: [] }
    }

    if (!Array.isArray(rows) || !rows.length) {
      return { errMsg: `${code} icin urun ozellikleri secilmedi`, productAttributes: [] }
    }
    for (const row of rows) {
      const attributeTypeCode = Number(row?.AttributeTypeCodeNumber || 0)
      const attributeCode = String(row?.AttributeCode || '').trim()

      if (!attributeTypeCode) {
        return { errMsg: `${code} icin urun ozellikleri eksik`, productAttributes: [] }
      }

      if (effectiveMode === 'existing') {
        const originalCode =
          dbMap.get(attributeTypeCode) ||
          String(row?.OriginalAttributeCode || '').trim()
        const changed = attributeCode !== originalCode
        if (!changed) continue
        if (!attributeCode) {
          return { errMsg: `${code} icin urun ozellikleri eksik`, productAttributes: [] }
        }
      } else if (!attributeCode) {
        return { errMsg: `${code} icin urun ozellikleri eksik`, productAttributes: [] }
      }

      out.push({
        ItemTypeCode: 1,
        ItemCode: code,
        AttributeTypeCode: attributeTypeCode,
        AttributeCode: attributeCode
      })
    }
    console.info('[OrderProductionUpdate] collectProductAttributes done', {
      code,
      mode: modeInfo.mode,
      effectiveMode,
      outCount: out.filter(x => x.ItemCode === code).length,
      rowCount: rows.length,
      optionCounts: rows.map(r => ({
        type: Number(r?.AttributeTypeCodeNumber || 0),
        options: Array.isArray(r?.Options) ? r.Options.length : 0,
        allOptions: Array.isArray(r?.AllOptions) ? r.AllOptions.length : 0
      }))
    })
  }
  return { errMsg: '', productAttributes: out }
}

async function collectCdItemsFromSelectedRows (selectedRows) {
  const codes = [...new Set(
    (selectedRows || [])
      .filter(r => r?.NewItemMode === 'new' && String(r?.NewItemCode || '').trim())
      .map(r => String(r.NewItemCode).trim().toUpperCase())
  )]
  if (!codes.length) return { errMsg: '', cdItems: [] }

  const out = []
  for (const code of codes) {
    let draft = store.getCdItemDraft(code)
    if (!draft) {
      const existingCdItem = await store.fetchCdItemByCode(code)
      if (existingCdItem) {
        store.markItemCodeKnownExisting(code, true)
        syncRowsForKnownExistingCode(code)
        draft = normalizeCdItemDraftForPayload(existingCdItem)
        store.setCdItemDraft(code, draft)
      }
    }
    if (!draft) {
      return { errMsg: `${code} icin cdItem bilgisi eksik`, cdItems: [] }
    }
    out.push(normalizeCdItemDraftForPayload(draft))
  }
  return { errMsg: '', cdItems: out }
}

function buildMailLineLabelFromRow (row) {
  const item = String(row?.NewItemCode || row?.OldItemCode || '').trim().toUpperCase()
  const color1 = String(row?.NewColor || row?.OldColor || '').trim().toUpperCase()
  const color2 = String(row?.NewDim2 || row?.OldDim2 || '').trim().toUpperCase()
  const desc = String(row?.NewDesc || row?.OldDesc || '').trim()

  if (!item) return ''
  const colorPart = color2 ? `${color1}-${color2}` : color1
  return [item, colorPart, desc].filter(Boolean).join(' ')
}

function buildUpdateMailLineLabelFromRow (row) {
  const newItem = String(row?.NewItemCode || row?.OldItemCode || '').trim().toUpperCase()
  const newColor = String(row?.NewColor || row?.OldColor || '').trim().toUpperCase()
  const newDim2 = String(row?.NewDim2 || row?.OldDim2 || '').trim().toUpperCase()
  const desc = mergeDescWithAutoNote(row, row?.NewDesc || row?.OldDesc || '')

  if (!newItem) return ''
  const colorPart = newDim2 ? `${newColor}-${newDim2}` : newColor
  return [newItem, colorPart, desc].filter(Boolean).join(' ')
}

function buildDueDateChangeRowsFromSelectedRows (selectedRows) {
  const seen = new Set()
  const out = []

  for (const row of (selectedRows || [])) {
    const itemCode = String(row?.NewItemCode || row?.OldItemCode || '').trim().toUpperCase()
    const colorCode = String(row?.NewColor || row?.OldColor || '').trim().toUpperCase()
    const itemDim2Code = String(row?.NewDim2 || row?.OldDim2 || '').trim().toUpperCase()
    const oldDueDate = formatDate(row?.OldDueDate)
    const newDueDate = formatDate(row?.NewDueDate)
    if (!itemCode || !newDueDate || oldDueDate === newDueDate) continue

    const key = [itemCode, colorCode, itemDim2Code, oldDueDate, newDueDate].join('||')
    if (seen.has(key)) continue
    seen.add(key)
    out.push({
      itemCode,
      colorCode,
      itemDim2Code,
      oldDueDate,
      newDueDate
    })
  }

  return out
}

function buildProductionUpdateMailPayload (selectedRows) {
  const updatedItems = [
    ...new Set(
      (selectedRows || [])
        .map(buildUpdateMailLineLabelFromRow)
        .filter(Boolean)
    )
  ]

  return {
    operation: 'update',
    deletedItems: [],
    updatedItems,
    addedItems: [],
    dueDateChanges: buildDueDateChangeRowsFromSelectedRows(selectedRows)
  }
}

function formatBarcodeValidationMessages (validations) {
  return (Array.isArray(validations) ? validations : [])
    .map(v => String(v?.message || '').trim())
    .filter(Boolean)
}

function showBarcodeValidationDialog (validations) {
  const messages = formatBarcodeValidationMessages(validations)
  if (!messages.length) return false
  $q.dialog({
    title: 'Barkod Validasyonlari',
    message: messages.join('<br>'),
    html: true,
    ok: { label: 'Tamam', color: 'negative' }
  })
  return true
}

async function sendUpdateMailAfterApply (selectedRows) {
  const orderId = String(orderHeaderID.value || '').trim()
  if (!orderId) return
  const host = String(window?.location?.hostname || '').trim().toLowerCase()
  const isLocalHost = host === 'localhost' || host === '127.0.0.1'
  if (isLocalHost) {
    console.info('[OrderProductionUpdate] sendUpdateMailAfterApply skipped (localhost)', { orderHeaderID: orderId, host })
    return
  }

  try {
    const t0 = nowMs()
    const payload = buildProductionUpdateMailPayload(selectedRows)
    console.info('[OrderProductionUpdate] sendUpdateMailAfterApply start', {
      orderHeaderID: orderId,
      updatedItems: payload?.updatedItems?.length || 0
    })
    const res = await api.post('/order/send-market-mail', {
      orderHeaderID: orderId,
      operation: payload.operation,
      deletedItems: payload.deletedItems,
      updatedItems: payload.updatedItems,
      addedItems: payload.addedItems,
      dueDateChanges: payload.dueDateChanges,
      extraRecipients: ['urun@baggi.com.tr']
    })

    const sentCount = Number(res?.data?.sentCount || 0)
    console.info('[OrderProductionUpdate] sendUpdateMailAfterApply done', {
      orderHeaderID: orderId,
      sentCount,
      durationMs: Math.round(nowMs() - t0)
    })
    $q.notify({
      type: 'positive',
      message: sentCount > 0
        ? `Guncelleme maili gonderildi (${sentCount} alici)`
        : 'Guncelleme maili gonderildi'
    })
  } catch (err) {
    console.error('[OrderProductionUpdate] sendUpdateMailAfterApply failed', {
      orderHeaderID: orderId,
      status: err?.response?.status,
      data: err?.response?.data,
      message: err?.message
    })
    $q.notify({
      type: 'warning',
      message: 'Guncelleme kaydedildi, mail gonderilemedi.'
    })
  }
}

function buildGroupKey (item) {
  const parts = [
    String(item?.OldItemCode || '').trim(),
    String(item?.OldColor || '').trim(),
    String(item?.OldDim2 || '').trim(),
    String(item?.OldDesc || '').trim(),
    String(item?.OldDim3 || '').trim()
  ]
  return parts.join('||')
}

function formatSizes (sizeMap) {
  const entries = Object.entries(sizeMap || {})
  if (!entries.length) return { list: [], label: '-' }
  entries.sort((a, b) => {
    const left = String(a[0] || '').trim()
    const right = String(b[0] || '').trim()
    if (/^\d+$/.test(left) && /^\d+$/.test(right)) {
      return Number(left) - Number(right)
    }
    return left.localeCompare(right)
  })
  const label = entries.map(([k, v]) => (v > 1 ? `${k}(${v})` : k)).join(', ')
  return { list: entries.map(([k]) => k), label }
}

function formatCodeDescriptionLabel (code, description) {
  const codeText = String(code || '').trim().toUpperCase()
  const descText = String(description || '').trim()
  if (!codeText) return descText
  if (!descText) return codeText
  return `${codeText} - ${descText}`
}

function formatQtyLabel (value) {
  const qty = Number(value || 0)
  if (!Number.isFinite(qty)) return '0'
  return Number.isInteger(qty)
    ? String(qty)
    : qty.toLocaleString('tr-TR', { minimumFractionDigits: 0, maximumFractionDigits: 2 })
}

function groupItems (items, prevRows = []) {
  const prevMap = new Map()
  for (const r of prevRows || []) {
    if (!r?.RowKey) continue
    prevMap.set(r.RowKey, {
      NewDesc: String(r.NewDesc || '').trim(),
      NewItemCode: String(r.NewItemCode || '').trim().toUpperCase(),
      NewColor: String(r.NewColor || '').trim().toUpperCase(),
      NewDim2: String(r.NewDim2 || '').trim().toUpperCase(),
      NewItemMode: String(r.NewItemMode || '').trim(),
      NewItemSource: String(r.NewItemSource || '').trim(),
      NewItemEntryMode: String(r.NewItemEntryMode || '').trim(),
      NewDueDate: String(r.NewDueDate || '').trim()
    })
  }
  const map = new Map()

  for (const it of items) {
    const key = buildGroupKey(it)
    if (!map.has(key)) {
      const prev = prevMap.get(key) || {}
      const prevDesc = prev.NewDesc || ''
      const fallbackDesc = String((it?.NewDesc || it?.OldDesc) || '').trim()
      map.set(key, {
        RowKey: key,
        OrderHeaderID: it.OrderHeaderID,
        OldItemCode: it.OldItemCode,
        OldColor: it.OldColor,
        OldColorDescription: it.OldColorDescription,
        OldColorLabel: formatCodeDescriptionLabel(it.OldColor, it.OldColorDescription),
        OldDim2: it.OldDim2,
        OldDim3: it.OldDim3,
        OldDesc: it.OldDesc,
        OldDueDate: it.OldDueDate || '',
        NewDueDate: (prev.NewDueDate || it.OldDueDate || ''),
        OrderLineIDs: [],
        OrderLines: [],
        OldSizes: [],
        OldSizesLabel: '',
        OldTotalQty: 0,
        OldTotalQtyLabel: '0',
        NewItemCode: prev.NewItemCode || '',
        NewColor: prev.NewColor || '',
        NewDim2: prev.NewDim2 || '',
        NewDesc: prevDesc || fallbackDesc,
        NewItemMode: prev.NewItemMode || 'empty',
        NewItemSource: prev.NewItemSource || '',
        NewItemEntryMode: prev.NewItemEntryMode || '',
        IsVariantMissing: !!it.IsVariantMissing,
        yasPayloadMap: {}
      })
    }

    const g = map.get(key)
    if (it?.OrderLineID) g.OrderLineIDs.push(it.OrderLineID)

    const rawSize = String(it?.OldDim1 || '').trim()
    const size = store.normalizeDim1ForUi(rawSize)
    const rawSizeUpper = rawSize.toUpperCase()
    if (/^(\d+)\s*(Y|YAS|YAŞ)$/.test(rawSizeUpper) && size) {
      g.yasPayloadMap[size] = store.pickPreferredYasPayloadLabel(
        g.yasPayloadMap[size],
        rawSizeUpper
      )
    }
    if (it?.OrderLineID) {
      g.OrderLines.push({
        OrderLineID: it.OrderLineID,
        ItemDim1Code: size
      })
    }
    if (size !== '') {
      g.__sizeMap = g.__sizeMap || {}
      g.__sizeMap[size] = (g.__sizeMap[size] || 0) + 1
    }
    g.__oldQtyTotal = Number(g.__oldQtyTotal || 0) + Number(it?.OldQty || 0)
    if (it?.IsVariantMissing) g.IsVariantMissing = true
  }

  const out = []
  for (const g of map.values()) {
    const sizes = formatSizes(g.__sizeMap || {})
    g.OldSizes = sizes.list
    g.OldSizesLabel = sizes.label
    g.OldTotalQty = Number(g.__oldQtyTotal || 0)
    g.OldTotalQtyLabel = formatQtyLabel(g.OldTotalQty)
    const info = store.classifyItemCode(g.NewItemCode)
    g.NewItemCode = info.normalized
    g.NewItemMode = info.mode
    if (info.mode === 'empty') g.NewItemSource = ''
    if (!g.NewItemEntryMode && g.NewItemCode) {
      g.NewItemEntryMode = g.NewItemSource === 'selected' ? 'selected' : 'typed'
    }
    delete g.__sizeMap
    delete g.__oldQtyTotal
    out.push(g)
  }

  return out
}

async function refreshAll () {
  await store.fetchHeader(orderHeaderID.value)
  await store.fetchItems(orderHeaderID.value)
  await store.fetchProducts()
}

async function onBulkSubmit () {
  if (isBulkSubmitting.value || store.saving) {
    console.info('[OrderProductionUpdate] onBulkSubmit ignored (already running)', {
      orderHeaderID: orderHeaderID.value,
      isBulkSubmitting: isBulkSubmitting.value,
      storeSaving: store.saving
    })
    return
  }

  isBulkSubmitting.value = true
  const flowStart = nowMs()
  try {
    suppressAutoSetupDialogs.value = true
    const selectedRows = rows.value.filter(r => !!selectedMap.value[r.RowKey])
    const headerAverageDueDateValue = normalizeDateInput(headerAverageDueDate.value)
    const headerDateChanged = hasHeaderAverageDueDateChange.value
    if (!selectedRows.length && !headerDateChanged) {
      $q.notify({ type: 'warning', message: 'Lutfen en az bir satir seciniz veya ustteki termin tarihini degistiriniz.' })
      return
    }

    const prepStart = nowMs()
    const { errMsg, lines } = collectLinesFromRows(selectedRows)
    if (errMsg) {
      $q.notify({ type: 'negative', message: errMsg })
      return
    }
    if (!lines.length && !headerDateChanged) {
      $q.notify({ type: 'warning', message: 'Secili satirlarda degisiklik yok.' })
      return
    }

    if (lines.length > 0) {
      const changedRows = selectedRows.filter(hasRowChange)
      const confirmed = await confirmOptionalColorWarnings(changedRows)
      if (!confirmed) return
    }

    let cdItems = []
    let productAttributes = []
    if (lines.length > 0) {
      const { errMsg: cdErrMsg, cdItems: nextCdItems } = await collectCdItemsFromSelectedRows(selectedRows)
      if (cdErrMsg) {
        $q.notify({ type: 'negative', message: cdErrMsg })
        return
      }
      cdItems = nextCdItems

      const { errMsg: attrErrMsg, productAttributes: nextProductAttributes } = await collectProductAttributesFromSelectedRows(selectedRows)
      if (attrErrMsg) {
        $q.notify({ type: 'negative', message: attrErrMsg })
        const firstCode = String(attrErrMsg.split(' ')[0] || '').trim().toUpperCase()
        if (isValidBaggiModelCode(firstCode)) {
          await openAttributeDialog(firstCode)
        }
        return
      }
      productAttributes = nextProductAttributes
    }

    console.info('[OrderProductionUpdate] onBulkSubmit prepared', {
      orderHeaderID: orderHeaderID.value,
      selectedRowCount: selectedRows.length,
      lineCount: lines.length,
      cdItemCount: cdItems.length,
      attributeCount: productAttributes.length,
      headerAverageDueDate: headerAverageDueDateValue,
      headerDateChanged,
      prepDurationMs: Math.round(nowMs() - prepStart)
    })

    const applyChanges = async (insertMissing) => {
      const applyStart = nowMs()
      const applyResult = await store.applyUpdates(
        orderHeaderID.value,
        lines,
        insertMissing,
        cdItems,
        productAttributes,
        headerDateChanged ? headerAverageDueDateValue : null
      )
      console.info('[OrderProductionUpdate] apply finished', {
        orderHeaderID: orderHeaderID.value,
        insertMissing: !!insertMissing,
        lineCount: lines.length,
        barcodeInserted: Number(applyResult?.barcodeInserted || 0),
        headerAverageDueDate: headerAverageDueDateValue,
        headerDateChanged,
        durationMs: Math.round(nowMs() - applyStart)
      })
      await store.fetchHeader(orderHeaderID.value)
      if (lines.length > 0) {
        await store.fetchItems(orderHeaderID.value)
      }
      selectedMap.value = {}
      if (lines.length > 0) {
        await sendUpdateMailAfterApply(selectedRows)
      } else {
        $q.notify({ type: 'positive', message: 'Tahmini termin tarihi guncellendi.' })
      }
    }

    if (lines.length > 0) {
      const validateStart = nowMs()
      const validate = await store.validateUpdates(orderHeaderID.value, lines, cdItems)
      console.info('[OrderProductionUpdate] validate finished', {
        orderHeaderID: orderHeaderID.value,
        lineCount: lines.length,
        missingCount: Number(validate?.missingCount || 0),
        barcodeValidationCount: Number(validate?.barcodeValidationCount || 0),
        durationMs: Math.round(nowMs() - validateStart)
      })
      if (showBarcodeValidationDialog(validate?.barcodeValidations)) {
        return
      }
      const missingCount = validate?.missingCount || 0
      if (missingCount > 0) {
        const missingList = (validate?.missing || []).map(v => (
          `${v.ItemCode} / ${v.ColorCode} / ${v.ItemDim1Code} / ${v.ItemDim2Code}`
        ))
        $q.dialog({
          title: 'Eksik Varyantlar',
          message: `Eksik varyant bulundu: ${missingCount}<br><br>${missingList.join('<br>')}`,
          html: true,
          ok: { label: 'Ekle ve Guncelle', color: 'primary' },
          cancel: { label: 'Vazgec', flat: true }
        }).onOk(async () => {
          await applyChanges(true)
        })
        return
      }
    }

    await applyChanges(false)
  } catch (err) {
    console.error('[OrderProductionUpdate] onBulkSubmit failed', {
      orderHeaderID: orderHeaderID.value,
      selectedRowCount: selectedRows.length,
      lineCount: lines.length,
      headerAverageDueDate: headerAverageDueDateValue,
      headerDateChanged,
      apiError: err?.response?.data,
      message: err?.message
    })
    if (showBarcodeValidationDialog(err?.response?.data?.barcodeValidations)) {
      return
    }
    $q.notify({ type: 'negative', message: store.error || 'Toplu kayit islemi basarisiz.' })
  } finally {
    isBulkSubmitting.value = false
    suppressAutoSetupDialogs.value = false
    console.info('[OrderProductionUpdate] onBulkSubmit total', {
      orderHeaderID: orderHeaderID.value,
      durationMs: Math.round(nowMs() - flowStart)
    })
  }
}
</script>

<style scoped>
.prod-table :deep(th) {
  font-weight: 700;
  letter-spacing: 0.2px;
}

.prod-table :deep(td) {
  vertical-align: middle;
}

.prod-table :deep(.q-table__container) {
  width: 100%;
}

.prod-table :deep(.q-table) {
  font-size: 11px;
}

.order-prod-page {
  --header-height: 56px;
  --filter-bar-height: 72px;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: auto;
}

.page-header {
  position: sticky;
  top: 0;
  z-index: 8;
  background: #fff;
  margin-top: -8px;
  margin-bottom: 8px;
  padding-top: 4px;
  padding-bottom: 6px;
}

.filter-bar {
  position: sticky;
  top: var(--header-height);
  z-index: 7;
  background: #fff;
  padding-top: 4px;
  padding-bottom: 8px;
}

.table-wrap {
  flex: 1;
  min-height: 0;
  min-width: 0;
  display: flex;
  width: 100%;
  overflow: auto;
}

.prod-table :deep(.q-table__middle) {
  max-height: calc(100vh - var(--header-height) - var(--filter-bar-height) - 140px);
  overflow: auto;
}

.prod-table :deep(.q-table__container) {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.prod-table :deep(.q-table thead tr th) {
  position: sticky;
  top: 0;
  z-index: 6;
  background: #fff;
}

.prod-table :deep(th.col-old),
.prod-table :deep(td.col-old) {
  background: #fff0d9;
}

.prod-table :deep(th.col-new),
.prod-table :deep(td.col-new) {
  background: #e3f3ff;
}

.prod-table :deep(th.col-old) {
  color: #8a5a00;
}

.prod-table :deep(th.col-new) {
  color: #0d4f7a;
}

.prod-table :deep(td.col-old) {
  border-left: 4px solid #f0a500;
}

.prod-table :deep(td.col-new) {
  border-left: 4px solid #2d9cdb;
}

.prod-table :deep(th.col-new-first),
.prod-table :deep(td.col-new-first) {
  border-left: 6px solid #1b7cc8;
}

.prod-table :deep(td.cell-new) {
  background: #e3f3ff;
}

.prod-table :deep(.new-item-existing .q-field__control) {
  background: #eaf9ef !important;
  border-left: 3px solid #21ba45;
}

.prod-table :deep(.new-item-new .q-field__control) {
  background: #fff5e9 !important;
  border-left: 3px solid #f2a100;
}

.prod-table :deep(td.col-desc),
.prod-table :deep(th.col-desc),
.prod-table :deep(td.col-wrap),
.prod-table :deep(th.col-wrap) {
  white-space: normal;
  word-break: break-word;
  line-height: 1.2;
}

.prod-table :deep(.q-field),
.prod-table :deep(.q-field__control),
.prod-table :deep(.q-select),
.prod-table :deep(.q-input) {
  width: 100%;
  min-width: 0;
}
</style>
