﻿<template>
  <q-page v-if="canUpdateLanguage" class="q-pa-md translation-page">
    <div class="translation-toolbar sticky-toolbar">
      <div class="row q-col-gutter-sm items-end q-mb-md">
        <div class="col-12 col-md-4">
          <q-input
            v-model="filters.q"
            dense
            outlined
            clearable
            label="Kelime ara"
          />
        </div>
        <div class="col-auto">
          <q-btn color="primary" icon="search" label="Getir" @click="loadRows" />
        </div>
        <div class="col-auto">
          <q-btn
            color="secondary"
            icon="sync"
            label="YENİ KELİMELERİ GETİR"
            :loading="store.saving"
            @click="syncSources"
          />
        </div>
        <div class="col-auto">
          <q-toggle v-model="autoTranslate" dense color="primary" label="Oto Çeviri" />
        </div>
      </div>

      <div class="row q-gutter-sm q-mb-sm">
        <q-btn
          color="accent"
          icon="g_translate"
          label="Seçilenleri Çevir"
          :disable="selectedKeys.length === 0"
          :loading="store.saving"
          @click="translateSelectedRows"
        />
        <q-btn
          color="secondary"
          icon="done_all"
          label="Seçilenleri Onayla"
          :disable="selectedKeys.length === 0"
          :loading="store.saving"
          @click="bulkApproveSelected"
        />
        <q-btn
          color="primary"
          icon="save"
          label="Seçilenleri Toplu Güncelle"
          :disable="selectedKeys.length === 0"
          :loading="store.saving"
          @click="bulkSaveSelected"
        />
      </div>
    </div>

    <q-table
      ref="tableRef"
      class="translation-table"
      flat
      bordered
      virtual-scroll
      :virtual-scroll-sticky-size-start="56"
      row-key="t_key"
      :loading="store.loading || store.saving"
      :rows="pivotRows"
      :columns="columns"
      :rows-per-page-options="[0]"
      v-model:pagination="tablePagination"
      hide-bottom
      @virtual-scroll="onVirtualScroll"
    >
      <template #body-cell-actions="props">
        <q-td :props="props">
          <q-btn
            dense
            color="primary"
            icon="save"
            label="Güncelle"
            :disable="!rowHasChanges(props.row.t_key)"
            :loading="store.saving"
            @click="saveRow(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-select="props">
        <q-td :props="props">
          <q-checkbox
            dense
            :model-value="selectedKeys.includes(props.row.t_key)"
            @update:model-value="(v) => toggleSelected(props.row.t_key, v)"
          />
        </q-td>
      </template>

      <template #body-cell-source_text_tr="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'source_text_tr')">
          <div class="source-text-label" :title="rowDraft(props.row.t_key).source_text_tr">
            {{ rowDraft(props.row.t_key).source_text_tr }}
          </div>
        </q-td>
      </template>

      <template #body-cell-source_type="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'source_type')">
          <q-select
            v-model="rowDraft(props.row.t_key).source_type"
            dense
            outlined
            emit-value
            map-options
            :options="sourceTypeOptions"
            @update:model-value="() => queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-en="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'en')">
          <q-input
            v-model="rowDraft(props.row.t_key).en"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-de="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'de')">
          <q-input
            v-model="rowDraft(props.row.t_key).de"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-es="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'es')">
          <q-input
            v-model="rowDraft(props.row.t_key).es"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-it="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'it')">
          <q-input
            v-model="rowDraft(props.row.t_key).it"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-ru="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'ru')">
          <q-input
            v-model="rowDraft(props.row.t_key).ru"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>

      <template #body-cell-ar="props">
        <q-td :props="props" :class="cellClass(props.row.t_key, 'ar')">
          <q-input
            v-model="rowDraft(props.row.t_key).ar"
            type="textarea"
            autogrow
            :max-rows="8"
            outlined
            @blur="queueAutoSave(props.row.t_key)"
          />
        </q-td>
      </template>
    </q-table>
  </q-page>

  <q-page v-else class="q-pa-md flex flex-center">
    <div class="text-negative text-subtitle1">
      Bu modüle erişim yetkiniz yok.
    </div>
  </q-page>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useQuasar } from 'quasar'
import { usePermission } from 'src/composables/usePermission'
import { useTranslationStore } from 'src/stores/translationStore'

const $q = useQuasar()
const store = useTranslationStore()
const { canUpdate } = usePermission()
const canUpdateLanguage = canUpdate('language')

const filters = ref({
  q: ''
})
const autoTranslate = ref(false)
const tableRef = ref(null)
const FETCH_LIMIT = 1400
const loadedOffset = ref(0)
const hasMoreRows = ref(true)
const loadingMore = ref(false)
const tablePagination = ref({
  page: 1,
  rowsPerPage: 0,
  sortBy: 'source_text_tr',
  descending: false
})
let filterReloadTimer = null

const sourceTypeOptions = [
  { label: 'dummy', value: 'dummy' },
  { label: 'postgre', value: 'postgre' },
  { label: 'mssql', value: 'mssql' }
]

const columns = [
  { name: 'actions', label: 'Güncelle', field: 'actions', align: 'left' },
  { name: 'select', label: 'Seç', field: 'select', align: 'left' },
  { name: 'source_text_tr', label: 'Türkçe Metin', field: 'source_text_tr', align: 'left', style: 'min-width: 340px' },
  { name: 'source_type', label: 'Kaynak', field: 'source_type', align: 'left', style: 'min-width: 140px' },
  { name: 'en', label: 'İngilizce', field: 'en', align: 'left', style: 'min-width: 220px' },
  { name: 'de', label: 'Almanca', field: 'de', align: 'left', style: 'min-width: 220px' },
  { name: 'es', label: 'İspanyolca', field: 'es', align: 'left', style: 'min-width: 220px' },
  { name: 'it', label: 'İtalyanca', field: 'it', align: 'left', style: 'min-width: 220px' },
  { name: 'ru', label: 'Rusça', field: 'ru', align: 'left', style: 'min-width: 220px' },
  { name: 'ar', label: 'Arapça', field: 'ar', align: 'left', style: 'min-width: 220px' }
]

const draftByKey = ref({})
const originalByKey = ref({})
const selectedKeys = ref([])
const autoSaveTimers = new Map()

const pivotRows = computed(() => {
  const byKey = new Map()
  for (const row of store.rows) {
    const key = row.t_key
    if (!byKey.has(key)) {
      byKey.set(key, {
        t_key: key,
        source_text_tr: '',
        source_type: 'dummy',
        en: '',
        de: '',
        es: '',
        it: '',
        ru: '',
        ar: '',
        langs: {}
      })
    }

    const target = byKey.get(key)
    target.langs[row.lang_code] = {
      id: row.id,
      status: row.status,
      is_manual: row.is_manual
    }

    if (row.lang_code === 'tr') {
      target.source_text_tr = row.translated_text || row.source_text_tr || ''
      target.source_type = row.source_type || 'dummy'
    } else if (row.lang_code === 'en') {
      target.en = row.translated_text || ''
    } else if (row.lang_code === 'de') {
      target.de = row.translated_text || ''
    } else if (row.lang_code === 'es') {
      target.es = row.translated_text || ''
    } else if (row.lang_code === 'it') {
      target.it = row.translated_text || ''
    } else if (row.lang_code === 'ru') {
      target.ru = row.translated_text || ''
    } else if (row.lang_code === 'ar') {
      target.ar = row.translated_text || ''
    }
  }

  return Array.from(byKey.values()).sort((a, b) => a.t_key.localeCompare(b.t_key))
})

function snapshotDrafts (options = {}) {
  const preserveDirty = Boolean(options?.preserveDirty)
  const draft = {}
  const original = {}
  for (const row of pivotRows.value) {
    const existingDraft = draftByKey.value[row.t_key]
    const existingOriginal = originalByKey.value[row.t_key]
    const keepExisting = preserveDirty &&
      existingDraft &&
      existingOriginal &&
      (
        existingDraft.source_text_tr !== existingOriginal.source_text_tr ||
        existingDraft.source_type !== existingOriginal.source_type ||
        existingDraft.en !== existingOriginal.en ||
        existingDraft.de !== existingOriginal.de ||
        existingDraft.es !== existingOriginal.es ||
        existingDraft.it !== existingOriginal.it ||
        existingDraft.ru !== existingOriginal.ru ||
        existingDraft.ar !== existingOriginal.ar
      )

    if (keepExisting) {
      draft[row.t_key] = { ...existingDraft }
      original[row.t_key] = { ...existingOriginal }
      continue
    }

    draft[row.t_key] = {
      source_text_tr: row.source_text_tr || '',
      source_type: row.source_type || 'dummy',
      en: row.en || '',
      de: row.de || '',
      es: row.es || '',
      it: row.it || '',
      ru: row.ru || '',
      ar: row.ar || ''
    }
    original[row.t_key] = { ...draft[row.t_key] }
  }
  draftByKey.value = draft
  originalByKey.value = original
  selectedKeys.value = selectedKeys.value.filter(k => draft[k])
}

function rowDraft (key) {
  if (!draftByKey.value[key]) {
    draftByKey.value[key] = {
      source_text_tr: '',
      source_type: 'dummy',
      en: '',
      de: '',
      es: '',
      it: '',
      ru: '',
      ar: ''
    }
  }
  return draftByKey.value[key]
}

function buildFilters () {
  const query = String(filters.value.q || '').trim()
  return {
    q: query || undefined
  }
}

function rowHasChanges (key) {
  const draft = draftByKey.value[key]
  const orig = originalByKey.value[key]
  if (!draft || !orig) return false
  return (
    draft.source_text_tr !== orig.source_text_tr ||
    draft.source_type !== orig.source_type ||
    draft.en !== orig.en ||
    draft.de !== orig.de ||
    draft.es !== orig.es ||
    draft.it !== orig.it ||
    draft.ru !== orig.ru ||
    draft.ar !== orig.ar
  )
}

function isPending (key, lang) {
  const row = pivotRows.value.find(r => r.t_key === key)
  const meta = row?.langs?.[lang]
  return meta?.status === 'pending'
}

function cellClass (key, field) {
  const draft = draftByKey.value[key]
  const orig = originalByKey.value[key]
  if (!draft || !orig) return ''

  if (draft[field] !== orig[field]) return 'cell-dirty'

  if (field === 'en' && isPending(key, 'en')) return 'cell-new'
  if (field === 'de' && isPending(key, 'de')) return 'cell-new'
  if (field === 'es' && isPending(key, 'es')) return 'cell-new'
  if (field === 'it' && isPending(key, 'it')) return 'cell-new'
  if (field === 'ru' && isPending(key, 'ru')) return 'cell-new'
  if (field === 'ar' && isPending(key, 'ar')) return 'cell-new'
  if (field === 'source_text_tr' && isPending(key, 'tr')) return 'cell-new'
  return ''
}

function toggleSelected (key, checked) {
  if (checked) {
    if (!selectedKeys.value.includes(key)) {
      selectedKeys.value = [...selectedKeys.value, key]
    }
    return
  }
  selectedKeys.value = selectedKeys.value.filter(k => k !== key)
}

function queueAutoSave (key) {
  if (!key) return
  const existing = autoSaveTimers.get(key)
  if (existing) {
    clearTimeout(existing)
  }
  const timer = setTimeout(() => {
    autoSaveTimers.delete(key)
    if (rowHasChanges(key)) {
      void saveRow(key)
    }
  }, 250)
  autoSaveTimers.set(key, timer)
}

async function fetchRowsChunk (append = false) {
  const params = {
    ...buildFilters(),
    limit: FETCH_LIMIT,
    offset: append ? loadedOffset.value : 0
  }

  await store.fetchRows(params, { append })
  const incomingCount = Number(store.count) || 0

  if (append) {
    loadedOffset.value += incomingCount
  } else {
    loadedOffset.value = incomingCount
  }
  hasMoreRows.value = incomingCount === FETCH_LIMIT
  snapshotDrafts({ preserveDirty: append })
}

async function loadRows () {
  try {
    loadedOffset.value = 0
    hasMoreRows.value = true
    await fetchRowsChunk(false)
  } catch (err) {
    console.error('[translation-sync][ui] loadRows:error', {
      message: err?.message || 'Çeviri satırları yüklenemedi'
    })
    $q.notify({
      type: 'negative',
      message: err?.message || 'Çeviri satırları yüklenemedi'
    })
  }
}

async function loadMoreRows () {
  if (!hasMoreRows.value || loadingMore.value || store.loading || store.saving) return
  loadingMore.value = true
  try {
    await fetchRowsChunk(true)
  } finally {
    loadingMore.value = false
  }
}

async function ensureEnoughVisibleRows (minRows = 120, maxBatches = 4) {
  let guard = 0
  while (hasMoreRows.value && pivotRows.value.length < minRows && guard < maxBatches) {
    await loadMoreRows()
    guard++
  }
}

function onVirtualScroll (details) {
  const to = Number(details?.to || 0)
  if (!Number.isFinite(to)) return
  if (to >= pivotRows.value.length - 15) {
    void loadMoreRows()
  }
}

function scheduleFilterReload () {
  if (filterReloadTimer) {
    clearTimeout(filterReloadTimer)
  }
  filterReloadTimer = setTimeout(() => {
    filterReloadTimer = null
    void loadRows()
  }, 350)
}

async function ensureMissingLangRows (key, draft, langs) {
  const missingLangs = []
  if (!langs.en && String(draft.en || '').trim() !== '') missingLangs.push('en')
  if (!langs.de && String(draft.de || '').trim() !== '') missingLangs.push('de')
  if (!langs.es && String(draft.es || '').trim() !== '') missingLangs.push('es')
  if (!langs.it && String(draft.it || '').trim() !== '') missingLangs.push('it')
  if (!langs.ru && String(draft.ru || '').trim() !== '') missingLangs.push('ru')
  if (!langs.ar && String(draft.ar || '').trim() !== '') missingLangs.push('ar')
  if (missingLangs.length === 0) return false

  await store.upsertMissing([
    {
      t_key: key,
      source_text_tr: draft.source_text_tr || key
    }
  ], missingLangs)
  return true
}

function buildRowUpdates (row, draft, original, approveStatus = 'approved') {
  const items = []
  const langs = row.langs || {}
  const sourceTypeChanged = draft.source_type !== original.source_type

  if (langs.tr?.id && (draft.source_text_tr !== original.source_text_tr || sourceTypeChanged)) {
    items.push({
      id: langs.tr.id,
      source_text_tr: draft.source_text_tr,
      translated_text: draft.source_text_tr,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.en?.id && (draft.en !== original.en || sourceTypeChanged)) {
    items.push({
      id: langs.en.id,
      translated_text: draft.en,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.de?.id && (draft.de !== original.de || sourceTypeChanged)) {
    items.push({
      id: langs.de.id,
      translated_text: draft.de,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.es?.id && (draft.es !== original.es || sourceTypeChanged)) {
    items.push({
      id: langs.es.id,
      translated_text: draft.es,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.it?.id && (draft.it !== original.it || sourceTypeChanged)) {
    items.push({
      id: langs.it.id,
      translated_text: draft.it,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.ru?.id && (draft.ru !== original.ru || sourceTypeChanged)) {
    items.push({
      id: langs.ru.id,
      translated_text: draft.ru,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  if (langs.ar?.id && (draft.ar !== original.ar || sourceTypeChanged)) {
    items.push({
      id: langs.ar.id,
      translated_text: draft.ar,
      source_type: draft.source_type,
      status: approveStatus,
      is_manual: true
    })
  }
  return items
}

async function saveRow (key) {
  const row = pivotRows.value.find(r => r.t_key === key)
  const draft = draftByKey.value[key]
  const original = originalByKey.value[key]
  if (!row || !draft || !original || !rowHasChanges(key)) return

  try {
    const insertedMissing = await ensureMissingLangRows(key, draft, row.langs || {})
    if (insertedMissing) {
      await loadRows()
    }

    const refreshed = pivotRows.value.find(r => r.t_key === key)
    if (!refreshed) return
    const refreshDraft = draftByKey.value[key]
    const refreshOriginal = originalByKey.value[key]
    const items = buildRowUpdates(refreshed, refreshDraft, refreshOriginal)
    if (items.length > 0) {
      await store.bulkUpdate(items)
    }

    await loadRows()
    $q.notify({ type: 'positive', message: 'Satır güncellendi' })
  } catch (err) {
    $q.notify({ type: 'negative', message: err?.message || 'Güncelleme hatası' })
  }
}

async function bulkApproveSelected () {
  try {
    const ids = []
    for (const key of selectedKeys.value) {
      const row = pivotRows.value.find(r => r.t_key === key)
      if (!row) continue
      for (const lang of ['tr', 'en', 'de', 'es', 'it', 'ru', 'ar']) {
        const meta = row.langs?.[lang]
        if (meta?.id && meta?.status === 'pending') {
          ids.push(meta.id)
        }
      }
    }
    const unique = Array.from(new Set(ids))
    if (unique.length === 0) {
      $q.notify({ type: 'warning', message: 'Onaylanacak pending kayıt bulunamadı' })
      return
    }
    await store.bulkApprove(unique)
    await loadRows()
    $q.notify({ type: 'positive', message: `${unique.length} kayıt onaylandı` })
  } catch (err) {
    $q.notify({ type: 'negative', message: err?.message || 'Toplu onay hatası' })
  }
}

async function translateSelectedRows () {
  try {
    const keys = Array.from(new Set(selectedKeys.value.filter(Boolean)))
    if (keys.length === 0) {
      $q.notify({ type: 'warning', message: 'Çevrilecek seçim bulunamadı' })
      return
    }

    const response = await store.translateSelected({
      t_keys: keys,
      languages: ['en', 'de', 'it', 'es', 'ru', 'ar'],
      limit: Math.min(50000, keys.length * 6)
    })

    const translated = Number(response?.translated_count || 0)
    const traceId = response?.trace_id || null

    await loadRows()
    $q.notify({
      type: 'positive',
      message: `Seçilenler çevrildi: ${translated}${traceId ? ` | Trace: ${traceId}` : ''}`
    })
  } catch (err) {
    $q.notify({ type: 'negative', message: err?.message || 'Seçili çeviri işlemi başarısız' })
  }
}

async function bulkSaveSelected () {
  try {
    const items = []
    for (const key of selectedKeys.value) {
      const row = pivotRows.value.find(r => r.t_key === key)
      const draft = draftByKey.value[key]
      const original = originalByKey.value[key]
      if (!row || !draft || !original) continue
      if (!rowHasChanges(key)) continue

      const insertedMissing = await ensureMissingLangRows(key, draft, row.langs || {})
      if (insertedMissing) {
        await loadRows()
      }

      const refreshed = pivotRows.value.find(r => r.t_key === key)
      if (!refreshed) continue
      const refreshDraft = draftByKey.value[key]
      const refreshOriginal = originalByKey.value[key]
      items.push(...buildRowUpdates(refreshed, refreshDraft, refreshOriginal))
    }

    if (items.length === 0) {
      $q.notify({ type: 'warning', message: 'Toplu güncellenecek değişiklik yok' })
      return
    }

    await store.bulkUpdate(items)
    await loadRows()
    $q.notify({ type: 'positive', message: `${items.length} kayıt toplu güncellendi` })
  } catch (err) {
    $q.notify({ type: 'negative', message: err?.message || 'Toplu güncelleme hatası' })
  }
}

async function syncSources () {
  const startedAt = Date.now()
  const beforeCount = pivotRows.value.length
  console.info('[translation-sync][ui] button:click', {
    at: new Date(startedAt).toISOString(),
    auto_translate: autoTranslate.value,
    only_new: true,
    before_row_count: beforeCount
  })
  try {
    const response = await store.syncSources({
      auto_translate: autoTranslate.value,
      languages: ['en', 'de', 'it', 'es', 'ru', 'ar'],
      limit: 1000,
      only_new: true
    })
    const result = response?.result || response || {}
    const traceId = result?.trace_id || response?.trace_id || null
    console.info('[translation-sync][ui] sync:response', {
      trace_id: traceId,
      seed_count: result.seed_count || 0,
      affected_count: result.affected_count || 0,
      auto_translated: result.auto_translated || 0,
      duration_ms: result.duration_ms || null
    })
    await loadRows()
    const afterCount = pivotRows.value.length
    console.info('[translation-sync][ui] chain:reload-complete', {
      trace_id: traceId,
      duration_ms: Date.now() - startedAt,
      before_row_count: beforeCount,
      after_row_count: afterCount,
      delta_row_count: afterCount - beforeCount
    })
    $q.notify({
      type: 'positive',
      message: `Tarama tamamlandı. Seed: ${result.seed_count || 0}, Oto çeviri: ${result.auto_translated || 0}`
    })
  } catch (err) {
    $q.notify({
      type: 'negative',
      message: err?.message || 'Kaynak tarama hatası'
    })
  }
}

onMounted(() => {
  void loadRows()
})

onBeforeUnmount(() => {
  if (filterReloadTimer) {
    clearTimeout(filterReloadTimer)
    filterReloadTimer = null
  }
})

watch(
  () => filters.value.q,
  () => { scheduleFilterReload() }
)

watch(
  [() => tablePagination.value.sortBy, () => tablePagination.value.descending],
  () => { void ensureEnoughVisibleRows(120, 4) }
)
</script>

<style scoped>
.translation-page {
  height: calc(100vh - 120px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.translation-toolbar {
  background: #fff;
  padding-top: 6px;
}

.sticky-toolbar {
  position: sticky;
  top: 0;
  z-index: 35;
}

.translation-table {
  flex: 1;
  min-height: 0;
}

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

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

.translation-table :deep(.q-table tbody td) {
  vertical-align: top;
  padding: 6px;
}

.translation-table :deep(.q-field__native) {
  line-height: 1.35;
  word-break: break-word;
}

.source-text-label {
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.4;
  max-height: 11.2em;
  overflow: auto;
}

.cell-dirty {
  background: #fff3cd;
}

.cell-new {
  background: #d9f7e8;
}
</style>

