<template>
  <q-page v-if="canReadOrder" class="pcmm-page q-pa-md">
    <div class="pcmm-top sticky-top">
      <div class="pcmm-header row items-center q-col-gutter-md">
        <div class="col">
          <div class="text-h6">Maliyet Parca Eslestirme</div>
          <div class="text-caption text-grey-7">
            NEBIM_V3 urun gruplari + TEDARIK URETIM PROGRAMI parca bolum + hammadde turleri eslestirmesi
          </div>
        </div>

      <div class="col-auto">
        <q-btn
          color="primary"
          icon="refresh"
          label="Yenile"
          :loading="loading"
          @click="hardResetAndRefresh"
        />
      </div>
    </div>

      <q-separator class="q-my-sm" />
    </div>

    <div class="pcmm-table-wrap">
      <q-table
      class="ol-table pcmm-table"
      flat
      bordered
      dense
      separator="cell"
      row-key="__key"
      :rows="rows"
      :columns="columns"
      :loading="loading"
      no-data-label="Kayit bulunamadi"
      :rows-per-page-options="[0]"
      hide-bottom
      sticky-header
    >
      <template #header-cell="props">
        <q-th :props="props">
          <div class="pcmm-header-cell">
            <div class="pcmm-head-wrap-2">{{ props.col.label }}</div>
            <q-btn
              v-if="props.col.name !== 'copy_select' && props.col.name !== 'save_select'"
              dense
              flat
              round
              size="sm"
              icon="filter_alt"
              :color="isColumnFilterActive(props.col.name) ? 'primary' : 'grey-6'"
            >
              <q-menu class="pcmm-filter-menu" fit>
                <div class="pcmm-filter-menu-content">
                  <div class="text-caption text-weight-bold q-mb-sm">{{ props.col.label }}</div>
                  <q-input
                    v-model="getColumnFilter(props.col.name).text"
                    dense
                    outlined
                    clearable
                    label="Icerir"
                  />
                  <q-select
                    v-model="getColumnFilter(props.col.name).selected"
                    class="q-mt-sm"
                    dense
                    outlined
                    multiple
                    use-chips
                    use-input
                    emit-value
                    map-options
                    :options="getColumnDistinctOptions(props.col.name)"
                    label="Deger Sec"
                  />
                  <div class="row justify-end q-gutter-sm q-mt-sm">
                    <q-btn dense flat color="grey-7" label="Temizle" @click="clearColumnFilter(props.col.name)" />
                  </div>
                </div>
              </q-menu>
            </q-btn>
          </div>
        </q-th>
      </template>

      <template #top-left>
        <div class="row items-center q-gutter-sm">
          <q-select
            v-model="commonHammaddeSelected"
            dense
            filled
            multiple
            use-chips
            clearable
            emit-value
            map-options
            option-label="label"
            option-value="value"
            :options="commonHammaddeOptions"
            :disable="loading || saving || commonHammaddeOptions.length === 0"
            class="pcmm-multi-select"
            style="min-width: 420px"
          >
            <template #selected-item="scope">
              <q-chip
                class="q-mr-xs"
                dense
                removable
                @remove="scope.removeAtIndex(scope.index)"
              >
                {{ scope.opt.label }}
              </q-chip>
            </template>
            <template #hint>
              Ekrandaki satirlarda bulunan hammadde turleri (distinct)
            </template>
          </q-select>
          <q-btn
            color="negative"
            icon="delete_sweep"
            label="Butun Parcalardan Kaldir"
            :disable="loading || saving || (commonHammaddeSelected || []).length === 0"
            @click="confirmRemoveCommonHammadde"
          />
          <q-btn
            label="Kolon Filtreleri"
            icon="filter_alt_off"
            color="grey-7"
            flat
            :disable="loading || saving"
            @click="clearAllColumnFilters"
          />
        </div>
      </template>

      <template #top-right>
        <div class="row items-center q-gutter-sm">
          <q-btn
            color="secondary"
            icon="content_copy"
            label="Kopyala"
            :disable="loading || saving || !canCopySelected"
            @click="copySelectedToSelected"
          />
          <q-btn
            color="secondary"
            icon="save"
            label="Secilenleri Kaydet"
            :loading="saving"
            :disable="loading || saving || !canSaveSelected"
            @click="saveSelected"
          />
          <q-btn
            color="primary"
            icon="save"
            label="Degisenleri Kaydet"
            :loading="saving"
            :disable="loading || saving || dirtyCount === 0"
            @click="saveAll"
          />
          <div class="text-caption text-grey-7 q-pl-sm">
            Satir: {{ rows.length }} | Degisen: {{ dirtyCount }} | Kopya: {{ copySelectedCount }} | Secili: {{ saveSelectedCount }}
          </div>
        </div>
      </template>

      <template #body-cell-copy_select="props">
        <q-td :props="props" style="width: 54px">
          <div class="row items-center no-wrap">
            <q-checkbox
              :model-value="isCopySelected(props.row.__key)"
              @update:model-value="(v) => toggleCopySelected(props.row.__key, v)"
            />
            <q-badge
              v-if="copyRoleLabel(props.row.__key)"
              color="grey-8"
              class="q-ml-xs"
              style="max-width: 110px"
            >
              {{ copyRoleLabel(props.row.__key) }}
              <q-tooltip anchor="center left" self="center right">
                {{ copyRoleLabel(props.row.__key) }}
              </q-tooltip>
            </q-badge>
          </div>
        </q-td>
      </template>

      <template #body-cell-save_select="props">
        <q-td :props="props" style="width: 54px">
          <q-checkbox
            :model-value="isSaveSelected(props.row.__key)"
            @update:model-value="(v) => toggleSaveSelected(props.row.__key, v)"
          />
        </q-td>
      </template>

      <template #body-cell-parcaBolumAdi="props">
        <q-td :props="props">
          <q-select
            :model-value="bolumByKey[props.row.__key] || []"
            dense
            filled
            multiple
            use-chips
            clearable
            use-input
            input-debounce="0"
            :options="mtBolumOptions"
            option-label="label"
            option-value="value"
            emit-value
            map-options
            class="pcmm-multi-select"
            behavior="menu"
            @filter="onFilterMTBolum"
            @update:model-value="(val) => { updateBolumSelection(props.row.__key, val); markDirty(props.row) }"
            style="min-width: 260px"
          >
            <template #before-options>
              <q-item clickable @click="selectAllMTBolum(props.row.__key)">
                <q-item-section>Tumunu Sec</q-item-section>
              </q-item>
              <q-item clickable @click="clearMTBolum(props.row.__key)">
                <q-item-section>Temizle</q-item-section>
              </q-item>
              <q-separator />
            </template>

            <template #selected-item="scope">
              <q-chip
                class="q-mr-xs"
                dense
                removable
                @remove="scope.removeAtIndex(scope.index)"
              >
                {{ scope.opt.label }}
              </q-chip>
            </template>

            <template #option="scope">
              <q-item v-bind="scope.itemProps">
                <q-item-section avatar>
                  <q-checkbox
                    :model-value="scope.selected"
                    tabindex="-1"
                    @update:model-value="() => scope.toggleOption(scope.opt)"
                    @click.stop
                  />
                </q-item-section>
                <q-item-section>
                  <q-item-label>{{ scope.opt.label }}</q-item-label>
                </q-item-section>
              </q-item>
            </template>
          </q-select>
        </q-td>
      </template>

      <template #body-cell-nHammaddeTurleri="props">
        <q-td :props="props">
          <q-select
            :model-value="hammaddeByKey[props.row.__key] || []"
            dense
            filled
            multiple
            use-chips
            clearable
            use-input
            input-debounce="0"
            :options="hammaddeOptions"
            option-label="label"
            option-value="value"
            emit-value
            map-options
            class="pcmm-multi-select"
            behavior="menu"
            :disable="(bolumByKey[props.row.__key] || []).length === 0"
            @filter="onFilterHammadde"
            @update:model-value="(val) => { updateHammaddeSelection(props.row.__key, pruneHammaddeSelection(props.row.__key, val)); markDirty(props.row) }"
            style="min-width: 320px"
          >
            <template #before-options>
              <q-item clickable @click="selectAllHammadde(props.row.__key)">
                <q-item-section>Tumunu Sec</q-item-section>
              </q-item>
              <q-item clickable @click="clearHammadde(props.row.__key)">
                <q-item-section>Temizle</q-item-section>
              </q-item>
              <q-separator />
            </template>

            <template #selected-item="scope">
              <q-chip
                class="q-mr-xs"
                dense
                removable
                @remove="scope.removeAtIndex(scope.index)"
              >
                {{ scope.opt.label }}
              </q-chip>
            </template>

            <template #option="scope">
              <q-item v-bind="scope.itemProps" :disable="isHammaddeOptionDisabled(props.row.__key, scope.opt)">
                <q-item-section avatar>
                  <q-checkbox
                    :model-value="scope.selected"
                    :disable="isHammaddeOptionDisabled(props.row.__key, scope.opt)"
                    tabindex="-1"
                    @update:model-value="() => scope.toggleOption(scope.opt)"
                    @click.stop
                  />
                </q-item-section>
                <q-item-section>
                  <q-item-label>{{ scope.opt.label }}</q-item-label>
                </q-item-section>
              </q-item>
            </template>
          </q-select>
        </q-td>
      </template>
      </q-table>
    </div>
  </q-page>

  <q-page v-else class="q-pa-md flex flex-center">
    <div class="text-negative text-subtitle1">
      Bu module erisim yetkiniz yok.
    </div>
  </q-page>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useQuasar } from 'quasar'
import { get, post, del, extractApiErrorDetail } from 'src/services/api'
import { usePermission } from 'src/composables/usePermission'
import { slog } from 'src/utils/slog'

const $q = useQuasar()
const { canRead } = usePermission()
const canReadOrder = canRead('order')

const traceId = `pcd-mtbolum-map-${crypto?.randomUUID?.() || String(Date.now())}`
const prevBodyOverflow = ref(null)
const COLUMN_FILTERS_KEY = 'pcmm.mtbolum.columnFilters.v1'

const loading = ref(false)
const saving = ref(false)

const mappings = ref([])
const copySelectedKeys = ref([]) // ordered
const saveSelectedKeyMap = ref({}) // key -> true

const allCombos = ref([])

const mtBolumOptions = ref([])
const hammaddeOptions = ref([])
const mtBolumLoading = ref(false)
const hammaddeLoading = ref(false)

const bolumByKey = ref({})
const hammaddeByKey = ref({})
const commonHammaddeSelected = ref([])
const hammaddeLabelCache = ref({}) // no -> "NO - ACIKLAMA"

const columns = [
  { name: 'copy_select', label: '', field: 'copy_select', align: 'center' },
  { name: 'save_select', label: '', field: 'save_select', align: 'center' },
  { name: 'urunIlkGrubu', label: 'Urun Ilk Grubu', field: 'urunIlkGrubu', align: 'left', sortable: true },
  { name: 'urunAnaGrubu', label: 'Urun Ana Grubu', field: 'urunAnaGrubu', align: 'left', sortable: true },
  { name: 'urunAltGrubu', label: 'Urun Alt Grubu', field: 'urunAltGrubu', align: 'left', sortable: true },
  { name: 'parcaBolumAdi', label: 'Parca Bolum', field: row => (Array.isArray(row?.nUrtMTBolumIDs) ? row.nUrtMTBolumIDs.join(', ') : ''), align: 'left' },
  { name: 'nHammaddeTurleri', label: 'Hammadde Turleri', field: row => (Array.isArray(row?.nHammaddeTurleri) ? row.nHammaddeTurleri.join(', ') : ''), align: 'left' }
]

const dirtyMap = ref({})
const dirtyCount = computed(() => Object.keys(dirtyMap.value || {}).length)
const copySelectedCount = computed(() => (Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value.length : 0))
const canCopySelected = computed(() => copySelectedCount.value >= 2)
const saveSelectedCount = computed(() => Object.keys(saveSelectedKeyMap.value || {}).length)
const canSaveSelected = computed(() => saveSelectedCount.value > 0)

function findHammaddeLabel (no) {
  const n = Number(no || 0)
  if (!(n > 0)) return ''
  const cached = String(hammaddeLabelCache.value?.[String(n)] || '').trim()
  if (cached) return cached
  const opt = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).find(o => Number(o?.value) === n)
  const raw = String(opt?.label || '').trim()
  if (raw) {
    // Ensure "NO - ACIKLAMA" format. Some option sources may only provide the description.
    if (/^\d+\s*-\s*/.test(raw)) return raw
    if (/^\d+\s*$/.test(raw)) return `${n}`
    return `${n} - ${raw}`
  }
  return `${n}`
}

function normalizeSearch (value) {
  const s = String(value ?? '').trim()
  if (!s) return ''

  // Case-insensitive + Turkish/English character folding.
  // Goal: treat "İ/i/I/ı" as the same, and fold Turkish letters to their ASCII counterparts.
  // This is intentionally simple and predictable for server-side LIKE/search endpoints.
  return s
    .toLowerCase()
    .replace(/ı/g, 'i')
    .replace(/İ/g, 'i')
    .replace(/i̇/g, 'i') // defensive: dotted i from some lowercasing paths
    .replace(/ğ/g, 'g')
    .replace(/ü/g, 'u')
    .replace(/ş/g, 's')
    .replace(/ö/g, 'o')
    .replace(/ç/g, 'c')
}

const columnFilters = reactive({})

function loadSavedColumnFilters () {
  try {
    const raw = localStorage.getItem(COLUMN_FILTERS_KEY)
    if (!raw) return
    const parsed = JSON.parse(raw)
    if (!parsed || typeof parsed !== 'object') return

    // Replace-in-place for Vue reactivity.
    for (const k of Object.keys(columnFilters)) delete columnFilters[k]
    for (const [k, v] of Object.entries(parsed)) {
      const text = String(v?.text ?? '')
      const selected = Array.isArray(v?.selected) ? v.selected.map(x => String(x ?? '')) : []
      columnFilters[k] = { text, selected }
    }
  } catch {}
}

function persistColumnFilters () {
  try {
    localStorage.setItem(COLUMN_FILTERS_KEY, JSON.stringify(columnFilters))
  } catch {}
}

function getColumnFilter (name) {
  if (!columnFilters[name]) {
    columnFilters[name] = { text: '', selected: [] }
  }
  return columnFilters[name]
}

function isColumnFilterActive (name) {
  const cf = getColumnFilter(name)
  return !!String(cf.text || '').trim() || (Array.isArray(cf.selected) && cf.selected.length > 0)
}

function clearColumnFilter (name) {
  const cf = getColumnFilter(name)
  cf.text = ''
  cf.selected = []
  persistColumnFilters()
}

function clearAllColumnFilters () {
  for (const col of columns) {
    if (col.name === 'copy_select' || col.name === 'save_select') continue
    clearColumnFilter(col.name)
  }
  persistColumnFilters()
}

function getColumnComparableValue (row, colName) {
  if (colName === 'parcaBolumAdi') {
    return Array.isArray(row?.nUrtMTBolumIDs) ? row.nUrtMTBolumIDs.join(', ') : ''
  }
  if (colName === 'nHammaddeTurleri') {
    return Array.isArray(row?.nHammaddeTurleri) ? row.nHammaddeTurleri.join(', ') : ''
  }
  return String(row?.[colName] ?? '').trim()
}

function getColumnDistinctOptions (colName) {
  const set = new Set()
  for (const row of mappings.value) {
    const val = getColumnComparableValue(row, colName)
    if (val) set.add(val)
  }
  return Array.from(set)
    .sort((a, b) => a.localeCompare(b, 'tr'))
    .map(v => ({ label: v, value: v }))
}

const rows = computed(() => {
  let result = mappings.value

  for (const col of columns) {
    if (col.name === 'copy_select' || col.name === 'save_select') continue
    const cf = getColumnFilter(col.name)
    const text = normalizeSearch(cf.text)
    const selected = Array.isArray(cf.selected) ? cf.selected : []
    if (!text && selected.length === 0) continue

    result = result.filter((row) => {
      const value = getColumnComparableValue(row, col.name)
      const valueNorm = normalizeSearch(value)
      if (text && !valueNorm.includes(text)) return false
      if (selected.length > 0 && !selected.includes(value)) return false
      return true
    })
  }

  return result
})

const commonHammaddeOptions = computed(() => {
  const listRows = Array.isArray(rows.value) ? rows.value : []
  if (listRows.length === 0) return []
  const keys = listRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
  if (keys.length === 0) return []

  // DISTINCT across visible rows (union).
  const set = new Set()
  for (const k of keys) {
    const ham = normalizeIntList(hammaddeByKey.value?.[k] || [])
    ham.forEach(n => { if (n > 0) set.add(n) })
  }
  return Array.from(set)
    .sort((a, b) => a - b)
    .map(n => ({ value: n, label: findHammaddeLabel(n) }))
})

watch(commonHammaddeOptions, (opts) => {
  // Ensure labels for large/rare nos that won't be in the first 200 option fetch.
  try {
    const nos = (Array.isArray(opts) ? opts : []).map(o => Number(o?.value || 0)).filter(n => n > 0)
    ensureHammaddeLabelsForNos(nos)
  } catch {
    // ignore
  }
}, { immediate: true })

function hardResetAndRefresh () {
  // reset view state (filters + selections + dirty)
  clearAllColumnFilters()
  copySelectedKeys.value = []
  saveSelectedKeyMap.value = {}
  clearDirty()
  refreshAll()
}

function markDirty (row) {
  const key = String(row?.__key || '').trim()
  if (!key) return
  dirtyMap.value = { ...(dirtyMap.value || {}), [key]: true }
}

function clearDirty () {
  dirtyMap.value = {}
}

function isCopySelected (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return false
  return (Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []).includes(key)
}

function toggleCopySelected (rowKey, enabled) {
  const key = String(rowKey || '').trim()
  if (!key) return
  const current = Array.isArray(copySelectedKeys.value) ? [...copySelectedKeys.value] : []
  const idx = current.indexOf(key)
  if (enabled) {
    if (idx === -1) current.push(key)
  } else {
    if (idx >= 0) current.splice(idx, 1)
  }
  copySelectedKeys.value = current
}

function copyRoleLabel (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return ''
  const keys = Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []
  const idx = keys.indexOf(key)
  if (idx === 0) return 'Kopyalanacak'
  if (idx > 0) return 'Yapistirilacak'
  return ''
}

function isSaveSelected (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return false
  return !!saveSelectedKeyMap.value?.[key]
}

function toggleSaveSelected (rowKey, enabled) {
  const key = String(rowKey || '').trim()
  if (!key) return
  const next = { ...(saveSelectedKeyMap.value || {}) }
  if (enabled) next[key] = true
  else delete next[key]
  saveSelectedKeyMap.value = next
}

function copySelectedToSelected () {
  const keys = Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []
  if (keys.length < 2) return
  const srcKey = String(keys[0] || '').trim()
  if (!srcKey) return

  const srcBolum = bolumByKey.value?.[srcKey] || []
  const srcHam = hammaddeByKey.value?.[srcKey] || []

  for (let i = 1; i < keys.length; i++) {
    const key = String(keys[i] || '').trim()
    if (!key) continue
    updateBolumSelection(key, srcBolum)
    updateHammaddeSelection(key, srcHam)
    const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
    if (row) markDirty(row)
  }

  $q.notify({ type: 'positive', message: 'Kopyala / Yapistir tamamlandi' })
}

async function saveSelected () {
  const keys = Object.keys(saveSelectedKeyMap.value || {})
  await saveKeys(keys)
}

function selectAllMTBolum (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return
  const all = (Array.isArray(mtBolumOptions.value) ? mtBolumOptions.value : [])
    .map(o => Number(o?.value))
    .filter(n => Number.isFinite(n) && n > 0)
  updateBolumSelection(key, all)
  const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
  if (row) markDirty(row)
}

function clearMTBolum (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return
  updateBolumSelection(key, [])
  const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
  if (row) markDirty(row)
}

function selectAllHammadde (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return
  const all = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : [])
    .filter(opt => !isHammaddeOptionDisabled(key, opt))
    .map(o => Number(o?.value))
    .filter(n => Number.isFinite(n) && n > 0)
  updateHammaddeSelection(key, all)
  const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
  if (row) markDirty(row)
}

function clearHammadde (rowKey) {
  const key = String(rowKey || '').trim()
  if (!key) return
  updateHammaddeSelection(key, [])
  const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
  if (row) markDirty(row)
}

function normalizeIntList (list) {
  const arr = Array.isArray(list) ? list : []
  const nums = arr
    .map(x => Number(String(x).trim()))
    .filter(n => Number.isFinite(n) && n > 0)
  return Array.from(new Set(nums))
}

function initEditableStateFromRows (rows) {
  const bolum = {}
  const ham = {}
  ;(Array.isArray(rows) ? rows : []).forEach(r => {
    const key = String(r?.__key || '').trim()
    if (!key) return
    bolum[key] = normalizeIntList(r?.nUrtMTBolumIDs || [])
    ham[key] = normalizeIntList(r?.nHammaddeTurleri || [])
  })
  bolumByKey.value = bolum
  hammaddeByKey.value = ham
}

function updateBolumSelection (key, newValue) {
  const k = String(key || '').trim()
  if (!k) return
  bolumByKey.value = {
    ...(bolumByKey.value || {}),
    [k]: normalizeIntList(newValue)
  }
  // When part selection changes, prune hammadde selection to what is valid for the selected parts.
  const currentHam = hammaddeByKey.value?.[k] || []
  const nextHam = pruneHammaddeSelection(k, currentHam)
  if (String(nextHam) !== String(currentHam)) {
    hammaddeByKey.value = { ...(hammaddeByKey.value || {}), [k]: nextHam }
  }
}

function updateHammaddeSelection (key, newValue) {
  const k = String(key || '').trim()
  if (!k) return
  const nextList = normalizeIntList(newValue)
  hammaddeByKey.value = { ...(hammaddeByKey.value || {}), [k]: nextList }
  // Keep table field in sync for column filtering/sorting/export behavior.
  const idx = (Array.isArray(mappings.value) ? mappings.value : []).findIndex(r => String(r?.__key || '') === k)
  if (idx >= 0) {
    const copy = [...mappings.value]
    copy[idx] = { ...(copy[idx] || {}), nHammaddeTurleri: [...nextList] }
    mappings.value = copy
  }
}

function isHammaddeOptionDisabled (rowKey, opt) {
  const mtIds = normalizeIntList(bolumByKey.value?.[rowKey] || [])
  if (mtIds.length === 0) return true
  const mtBolumID = Number(opt?.mtBolumID || 0)
  // If lookup doesn't specify a part, allow everywhere (mtBolumID=0).
  if (!Number.isFinite(mtBolumID) || mtBolumID <= 0) return false
  return !mtIds.includes(mtBolumID)
}

function pruneHammaddeSelection (rowKey, list) {
  const values = normalizeIntList(list || [])
  if (values.length === 0) return []
  const allowed = values.filter(v => {
    const opt = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).find(o => Number(o?.value) === Number(v))
    // If option is not currently in the option list (search/paging), keep it; backend will still validate on save.
    if (!opt) return true
    return !isHammaddeOptionDisabled(rowKey, opt)
  })
  return allowed
}

function confirmRemoveCommonHammadde () {
  const selected = normalizeIntList(commonHammaddeSelected.value || [])
  if (selected.length === 0) return

  const listRows = Array.isArray(rows.value) ? rows.value : []
  const keys = listRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
  if (keys.length === 0) return

  // Affected "parcalar": only rows that currently contain at least one selected hammadde.
  const affectedRows = listRows.filter(r => {
    const k = String(r?.__key || '').trim()
    if (!k) return false
    const current = normalizeIntList(hammaddeByKey.value?.[k] || [])
    return current.some(v => selected.includes(v))
  })
  if (affectedRows.length === 0) {
    $q.notify({ type: 'info', message: 'Secilen hammadde turleri bu ekrandaki satirlarda bulunmuyor.', position: 'top-right' })
    return
  }
  const affected = affectedRows
    .map(r => `${String(r?.urunIlkGrubu || '').trim() || '-'} | ${String(r?.urunAnaGrubu || '').trim() || '-'} | ${String(r?.urunAltGrubu || '').trim() || '-'}`)
    .filter(Boolean)

  const removedLabels = selected.map(n => findHammaddeLabel(n))
  const htmlList = affected.slice(0, 30).map(x => `<div>${x}</div>`).join('')
  const more = affected.length > 30 ? `<div class="text-grey-7 q-mt-sm">(+${affected.length - 30} satir daha)</div>` : ''

  $q.dialog({
    title: 'Butun Parcalardan Kaldir',
    message: `
      <div class="q-mb-sm"><b>Kaldirilacak hammadde turleri:</b></div>
      <div class="q-mb-sm">${removedLabels.map(x => `<div>${x}</div>`).join('')}</div>
      <div class="q-mb-sm"><b>Etkilenecek parcalar:</b></div>
      <div style="max-height: 240px; overflow:auto; border: 1px solid #eee; padding: 8px;">${htmlList}${more}</div>
      <div class="q-mt-sm text-grey-7">Onaylarsaniz secilen hammadde turleri sadece bu satirlardan kaldirilacak ve Degisenleri Kaydet ile kaydedilebilecek.</div>
    `,
    html: true,
    cancel: true,
    persistent: true,
    ok: { label: 'Onayla', color: 'negative' },
    cancelLabel: 'Geri Don'
  }).onOk(() => {
    const affectedKeys = affectedRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
    for (const k of affectedKeys) {
      const current = normalizeIntList(hammaddeByKey.value?.[k] || [])
      const next = current.filter(v => !selected.includes(v))
      if (String(next) === String(current)) continue
      updateHammaddeSelection(k, next)
      const row = listRows.find(r => String(r?.__key || '') === k) || (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === k)
      if (row) markDirty(row)
    }
    commonHammaddeSelected.value = []
    $q.notify({ type: 'positive', message: 'Secilen hammadde turleri etkilenen satirlardan kaldirildi (taslak).', position: 'top-right' })
  })
}

// label resolution now handled by options' `label` field + selected-item slot (see UserDetail.vue "Piyasalar").
async function fetchMappings () {
  loading.value = true
  try {
    const data = await get('/pricing/production-product-costing/maliyet-parca-eslestirme', {
      trace_id: traceId,
      only_active: 1
    })
    return Array.isArray(data) ? data : []
  } catch (e) {
    const detail = await extractApiErrorDetail(e)
    $q.notify({ type: 'negative', message: detail || 'Liste okunamadi' })
    return []
  } finally {
    loading.value = false
  }
}

async function fetchSheet () {
  loading.value = true
  try {
    // Search changes should not carry over row-level selections from a previous sheet.
    copySelectedKeys.value = []
    saveSelectedKeyMap.value = {}

    const existing = await fetchMappings()

    const existingByKey = new Map()
    ;(Array.isArray(existing) ? existing : []).forEach(x => {
      const k = `${String(x?.urunIlkGrubu || '').trim()}|${String(x?.urunAltGrubu || '').trim()}|${String(x?.urunAnaGrubu || '').trim()}`
      const prev = existingByKey.get(k)
      const nextList = Array.isArray(prev) ? prev : (prev ? [prev] : [])
      nextList.push(x)
      existingByKey.set(k, nextList)
    })

    const combos = Array.isArray(allCombos.value) ? allCombos.value : []
    const rows = combos.map((c, idx) => {
      const ilk = String(c?.urunIlkGrubu || '').trim()
      const ana = String(c?.urunAnaGrubu || '').trim()
      const alt = String(c?.urunAltGrubu || '').trim()
      const k = `${ilk}|${alt}|${ana}`
      const hitList = existingByKey.get(k)
      const list = Array.isArray(hitList) ? hitList : []
      const mtIds = list
        .map(x => String(x?.nUrtMTBolumID || '').trim())
        .filter(Boolean)
      const uniqMt = Array.from(new Set(mtIds))

      // If multiple mappings exist, we merge hammadde types (union) for editing convenience.
      const hSet = new Set()
      list.forEach(x => {
        if (Array.isArray(x?.nHammaddeTurleri)) {
          x.nHammaddeTurleri.forEach(v => {
            v = String(v || '').trim()
            if (v) hSet.add(v)
          })
        }
      })
      return {
        __key: k,
        __rowIndex: idx,
        __existingIDs: list.map(x => Number(x?.id || 0)).filter(n => n > 0),
        urunIlkGrubu: ilk,
        urunAnaGrubu: ana,
        urunAltGrubu: alt,
        nUrtMTBolumIDs: uniqMt,
        nHammaddeTurleri: Array.from(hSet)
      }
    })
    mappings.value = rows
    initEditableStateFromRows(rows)
    clearDirty()
  } catch (e) {
    const detail = await extractApiErrorDetail(e)
    $q.notify({ type: 'negative', message: detail || 'Carsaf yuklenemedi' })
  } finally {
    loading.value = false
  }
}

// Column filters handle "search" now. Use clearAllColumnFilters() from the toolbar.

async function refreshAll () {
  await Promise.all([
    fetchMTBolumOptions(''),
    fetchHammaddeOptions(''),
    fetchCombos()
  ])
  await fetchSheet()
}

async function fetchCombos () {
  try {
    const data = await get('/pricing/production-product-costing/options/urun-ana-alt-combos', {
      trace_id: traceId,
      search: '',
      limit: 5000
    })
    allCombos.value = Array.isArray(data) ? data : []
  } catch (e) {
    const detail = await extractApiErrorDetail(e)
    allCombos.value = []
    $q.notify({ type: 'negative', message: detail || 'Urun combo listesi okunamadi' })
    slog.error('production-product-costing.mtbolum-map', 'fetchCombos:error', {
      trace_id: traceId,
      detail: detail || String(e?.message || e || '')
    })
  }
}

async function fetchMTBolumOptions (search) {
  mtBolumLoading.value = true
  try {
    const data = await get('/pricing/production-product-costing/options/mtbolum', {
      trace_id: traceId,
      search: normalizeSearch(search),
      limit: 200
    })
    mtBolumOptions.value = Array.isArray(data)
      ? data.map(x => ({
        value: Number(x?.value ?? x?.nUrtMTBolumID ?? x?.id ?? 0),
        label: (() => {
          const v = Number(x?.value ?? x?.nUrtMTBolumID ?? x?.id ?? 0)
          const name = String(x?.label || x?.sAdi || '').trim()
          if (Number.isFinite(v) && v > 0 && name) return `${v} - ${name}`
          if (Number.isFinite(v) && v > 0) return String(v)
          return name
        })()
      }))
        .filter(x => Number.isFinite(x.value) && x.value > 0)
        .sort((a, b) => a.value - b.value)
      : []
  } finally {
    mtBolumLoading.value = false
  }
}

async function fetchHammaddeOptions (search) {
  hammaddeLoading.value = true
  try {
    const data = await get('/pricing/production-product-costing/detail-editor-options', {
      trace_id: traceId,
      kind: 'hammadde',
      search: normalizeSearch(search),
      limit: 200
    })
    hammaddeOptions.value = Array.isArray(data)
      ? data.map(x => ({
        value: Number(String(x?.nHammaddeTuruNo ?? x?.value ?? '').trim()),
        // Part ownership: spUrtOnMLHammaddeTuru.MTnUrtMTBolumID (backend exposes as mtUrtMTBolumID / mtUrtMTBolumID).
        mtBolumID: Number(x?.mtUrtMTBolumID ?? x?.MTUrtMTBolumID ?? 0),
        label: (() => {
          const v = Number(String(x?.nHammaddeTuruNo ?? x?.value ?? '').trim())
          const name = String(x?.sHammaddeTuruAdi || x?.label || '').trim()
          if (Number.isFinite(v) && v > 0 && name) return `${v} - ${name}`
          if (Number.isFinite(v) && v > 0) return String(v)
          return name
        })()
      }))
        .filter(x => Number.isFinite(x.value) && x.value > 0)
        .sort((a, b) => a.value - b.value)
      : []

    // Prime cache with currently loaded options.
    try {
      const next = { ...(hammaddeLabelCache.value || {}) }
      ;(Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).forEach(opt => {
        const no = Number(opt?.value || 0)
        const label = String(opt?.label || '').trim()
        if (no > 0 && label) next[String(no)] = label
      })
      hammaddeLabelCache.value = next
    } catch {
      // ignore
    }
  } finally {
    hammaddeLoading.value = false
  }
}

async function ensureHammaddeLabelsForNos (nos) {
  const list = normalizeIntList(nos || [])
  if (list.length === 0) return
  const missing = list.filter(n => {
    const cached = String(hammaddeLabelCache.value?.[String(n)] || '').trim()
    if (cached) return false
    const opt = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).find(o => Number(o?.value) === n)
    return !String(opt?.label || '').trim()
  })
  if (missing.length === 0) return
  try {
    const data = await post('/pricing/production-product-costing/options/hammadde-by-nos', {
      nHammaddeTuruNos: missing
    }, { trace_id: traceId })
    const rows = Array.isArray(data) ? data : []
    const next = { ...(hammaddeLabelCache.value || {}) }
    rows.forEach(r => {
      const no = Number(r?.nHammaddeTuruNo || 0)
      const name = String(r?.sAciklama || '').trim()
      if (no > 0 && name) next[String(no)] = `${no} - ${name}`
    })
    hammaddeLabelCache.value = next
  } catch (e) {
    // Non-blocking: fallback will show only the number
    slog.error('production-product-costing.mtbolum-map', 'hammadde-by-nos:error', {
      trace_id: traceId,
      detail: await extractApiErrorDetail(e)
    })
  }
}

function onFilterMTBolum (val, update) {
  update(async () => {
    await fetchMTBolumOptions(val)
  })
}

function onFilterHammadde (val, update) {
  update(async () => {
    await fetchHammaddeOptions(val)
  })
}

async function saveAll () {
  const dirtyKeys = Object.keys(dirtyMap.value || {})
  await saveKeys(dirtyKeys)
}

async function saveKeys (keys) {
  const list = Array.isArray(keys) ? keys.map(k => String(k || '').trim()).filter(Boolean) : []
  if (list.length === 0) return

  saving.value = true
  try {
    const rowsToSave = mappings.value.filter(r => list.includes(String(r.__key || '').trim()))
    for (const row of rowsToSave) {
      const key = String(row.__key || '').trim()
      const mtIds = normalizeIntList(bolumByKey.value?.[key] || [])
      const hList = normalizeIntList(hammaddeByKey.value?.[key] || [])

      // If hammadde cleared OR no bölüm selected: delete all existing mappings for this combo
      const existingIDs = Array.isArray(row.__existingIDs) ? row.__existingIDs : []
      if (existingIDs.length > 0 && (mtIds.length === 0 || hList.length === 0)) {
        for (const id of existingIDs) {
          await del('/pricing/production-product-costing/maliyet-parca-eslestirme', { trace_id: traceId, id })
        }
        row.__existingIDs = []
        bolumByKey.value = { ...(bolumByKey.value || {}), [key]: [] }
        hammaddeByKey.value = { ...(hammaddeByKey.value || {}), [key]: [] }
        continue
      }

      if (mtIds.length > 0 && hList.length > 0) {
        const existing = await fetchMappings()
        const ilk = String(row.urunIlkGrubu || '').trim()
        const ana = String(row.urunAnaGrubu || '').trim()
        const alt = String(row.urunAltGrubu || '').trim()
        const existingForCombo = (Array.isArray(existing) ? existing : []).filter(x =>
          String(x?.urunIlkGrubu || '').trim() === ilk &&
          String(x?.urunAnaGrubu || '').trim() === ana &&
          String(x?.urunAltGrubu || '').trim() === alt
        )

        const selectedSet = new Set(mtIds.map(String))
        for (const ex of existingForCombo) {
          const exMt = String(ex?.nUrtMTBolumID || '').trim()
          if (exMt && !selectedSet.has(exMt) && Number(ex?.id || 0) > 0) {
            await del('/pricing/production-product-costing/maliyet-parca-eslestirme', { trace_id: traceId, id: Number(ex.id) })
          }
        }

        const idsAfter = []
        for (const mtId of mtIds) {
          const payload = {
            urunIlkGrubu: ilk,
            urunAnaGrubu: ana,
            urunAltGrubu: alt,
            nUrtMTBolumID: mtId,
            nHammaddeTurleri: hList,
            bAktif: true
          }
          const resp = await post('/pricing/production-product-costing/maliyet-parca-eslestirme/upsert', payload, { trace_id: traceId })
          if (resp?.id) idsAfter.push(Number(resp.id))
        }
        row.__existingIDs = idsAfter
      }
    }

    $q.notify({ type: 'positive', message: 'Degisiklikler kaydedildi' })
    clearDirty()
    // after saving, clear save selection to avoid accidental re-save
    saveSelectedKeyMap.value = {}
    await refreshAll()
  } catch (e) {
    const detail = await extractApiErrorDetail(e)
    $q.notify({ type: 'negative', message: detail || 'Kaydetme basarisiz' })
  } finally {
    saving.value = false
  }
}

onMounted(async () => {
  // This page is designed to scroll only inside the q-table body.
  // Prevent global/body scrolling while the page is mounted.
  try {
    prevBodyOverflow.value = document?.body?.style?.overflow ?? ''
    document.body.style.overflow = 'hidden'
  } catch {}

  loadSavedColumnFilters()
  // Persist on changes (typing/selecting) with a small debounce.
  let persistTimer = null
  watch(
    () => columnFilters,
    () => {
      if (persistTimer) clearTimeout(persistTimer)
      persistTimer = setTimeout(() => persistColumnFilters(), 250)
    },
    { deep: true }
  )

  try {
    await Promise.all([
      fetchMTBolumOptions(''),
      fetchHammaddeOptions(''),
      fetchCombos()
    ])
    await fetchSheet()
  } catch (e) {
    const detail = await extractApiErrorDetail(e)
    $q.notify({ type: 'negative', message: detail || 'Sayfa yuklenemedi' })
    slog.error('production-product-costing.mtbolum-map', 'mounted:error', {
      trace_id: traceId,
      detail: detail || String(e?.message || e || '')
    })
  }
})

onBeforeUnmount(() => {
  try {
    if (prevBodyOverflow.value !== null) {
      document.body.style.overflow = prevBodyOverflow.value || ''
    }
  } catch {}
})
</script>

<style scoped>
.pcmm-page {
  background: #fafafa;
  display: flex;
  flex-direction: column;
  /* Constrain to viewport so the table body can scroll. Quasar header is 56px in this app. */
  height: calc(100vh - 56px);
  min-height: 0;
  overflow: hidden;
  /* Small safe gap under q-header */
  padding-top: 10px;
}

.sticky-top {
  flex: 0 0 auto;
  z-index: 10;
  background: #fafafa;
}

.pcmm-header {
  max-width: 1200px;
  margin: 0 auto;
}

.pcmm-top {
  flex: 0 0 auto;
  padding-bottom: 0;
  margin-top: 0;
}

.pcmm-table-wrap {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.pcmm-form {
  max-width: 1200px;
  margin: 0 auto;
}

.pcmm-table {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.pcmm-table :deep(.q-table__container) {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.pcmm-table :deep(.q-table__middle) {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto !important;
}

.pcmm-table :deep(.q-table) {
  table-layout: auto;
}

/* Allow multi-select chips to wrap and grow vertically (PowerBI-like) */
.pcmm-table :deep(.pcmm-multi-select .q-field__control) {
  height: auto !important;
  min-height: 38px;
}

.pcmm-table :deep(.pcmm-multi-select .q-field__native) {
  align-items: flex-start;
}

.pcmm-table :deep(.pcmm-multi-select .q-select__chips) {
  flex-wrap: wrap;
}

.pcmm-table :deep(.pcmm-multi-select .q-chip) {
  max-width: 100%;
}

.pcmm-table :deep(.q-table__top) {
  background: #fafafa;
  position: sticky;
  top: 0;
  z-index: 20; /* Increased to be above thead th */
  padding-top: 4px;
  padding-bottom: 4px;
  margin-bottom: 0;
}

.pcmm-table :deep(.q-table thead th) {
  font-size: 11px;
  padding: 3px 4px;
  white-space: normal !important;
  vertical-align: top !important;
  line-height: 1.15;
  background: #f0f0f0;
}

/* Keep header fixed to the top of the scrollable middle area (directly under the top bar). */
.pcmm-table :deep(.q-table__middle thead tr th) {
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Make sure body rows never paint over the sticky header while scrolling. */
.pcmm-table :deep(.q-table__middle tbody td) {
  background-clip: padding-box;
}

.pcmm-header-cell {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 4px;
  width: 100%;
}

.pcmm-head-wrap-2 {
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  line-height: 1.15;
  white-space: normal;
  word-break: break-word;
}

.pcmm-filter-menu {
  min-width: 300px;
}

.pcmm-filter-menu-content {
  padding: 10px;
}
</style>
