<script setup>
import { toast } from 'bulma-toast'

const tabs = useTabs()
const router = useRouter()

definePageMeta({
  middleware: ['auth','org'],
  keepalive: true
})

const forcingLogout = async () => {
  await $fetch('/api/idempiere-auth/logout', { method: 'POST', headers: useRequestHeaders(['cookie']) })
  tabs.value = [{link: '/', text: 'Home', active: true}]
  localStorage.setItem('tabs', JSON.stringify(tabs.value))
  location.href = '/signin'
}

const alert = ref({ status: 200, message: '' })

// Load Order Sources and filter to JTL-FFN
const { data: orderSourcesRes } = await useFetch('/api/settings/order-sources', { headers: useRequestHeaders(['cookie']) })
const orderSources = computed(() => (orderSourcesRes.value?.records || []).filter((r) => r?.Marketplace?.identifier === 'jtl-ffn'))

const selectedOrderSourceId = ref('')

// Stocks
const loading = ref(false)
const items = ref([])
const originalMap = ref({})
const page = ref({ total: 0, limit: 50, offset: 0 })

const fetchStocks = async (updateInPlace = false) => {
  if(!selectedOrderSourceId.value) return
  loading.value = true
  if(!updateInPlace) {
    items.value = []
    originalMap.value = {}
  }
  try {
    const res = await $fetch('/api/ffn/stocks', {
      params: { orderSourceId: selectedOrderSourceId.value, '$top': page.value.limit, '$skip': page.value.offset },
      headers: useRequestHeaders(['cookie'])
    })
    if(Number(res?.status || 200) === 401) return forcingLogout()
    if(res?.items) {
      if(updateInPlace && items.value.length > 0) {
        // Update existing items in place by matching jfsku
        for(const newData of res.items) {
          const existingItem = items.value.find(it => it.jfsku === newData.jfsku)
          if(existingItem) {
            // Update individual properties to trigger Vue reactivity properly
            existingItem.stockLevel = newData.stockLevel
            existingItem.stockLevelReserved = newData.stockLevelReserved
            existingItem.stockLevelBlocked = newData.stockLevelBlocked
            existingItem.stockLevelAnnounced = newData.stockLevelAnnounced
            existingItem.warehouses = newData.warehouses
            existingItem.stockLevelDetails = newData.stockLevelDetails
            existingItem.merchantSku = newData.merchantSku
            existingItem._targetQty = Number(newData.stockLevel ?? 0)
            existingItem._adjust = 0

            originalMap.value[existingItem._rowId] = JSON.parse(JSON.stringify(existingItem))
          }
        }
      } else {
        // Replace all items
        items.value = res.items.map((x, idx)=>({ _rowId: `${x.jfsku}-${idx}`, _targetQty: Number(x.stockLevel ?? 0), _adjust: 0, ...x }))
        // snapshot originals
        for(const it of items.value) {
          originalMap.value[it._rowId] = JSON.parse(JSON.stringify(it))
        }
      }
      if(res?._page) page.value = { total: res._page.total ?? 0, limit: res._page.limit ?? 50, offset: res._page.offset ?? 0 }

      // Enrich with product names
      await enrichWithProductNames()
      // Enrich with local qty on hand from iDempiere
      await enrichWithLocalQty()
    } else if(res?.records) {
      // in case of different response, pass-through
      items.value = res.records
    }
    alert.value = { status: res?.status || 200, message: res?.message || '' }
  } catch (err) {
    alert.value = { status: err?.status || 500, message: err?.message || 'Failed to fetch stocks' }
  } finally {
    loading.value = false
  }
}

const editedMap = computed(() => {
  const map = {}
  for(const it of items.value) {
    const orig = originalMap.value?.[it._rowId]?.stockLevel ?? it.stockLevel ?? 0
    const delta = Number((it._targetQty ?? orig)) - Number(orig)
    // keep the computed delta on the row for submission
    it._adjust = isNaN(delta) ? 0 : delta
    if(!isNaN(delta) && Number(delta) !== 0) {
      map[it._rowId] = { delta: Number(delta) }
    }
  }
  return map
})

const submitAdjustments = async () => {
  const changed = Object.keys(editedMap.value)
  if(changed.length === 0) {
    toast({ message: 'No changes to submit', type: 'is-info', duration: 2000 })
    return
  }
  const payloads = []
  for(const key of changed) {
    const it = items.value.find(x => x._rowId === key)
    if(!it) continue
    // Try to extract optional fields from details if available
    const wh = (it.warehouses && it.warehouses[0]) ? it.warehouses[0] : null
    const det = (it.stockLevelDetails && it.stockLevelDetails[0]) ? it.stockLevelDetails[0] : null
    const bb = det?.bestBefore
    const nowIso = new Date().toISOString()
    const qtyDelta = Number(it._adjust || 0)

    const p = {
      fulfillerStockChangeId: `${Date.now()}-${Math.random().toString(36).slice(2,8)}`,
      jfsku: it.jfsku,
      warehouseId: wh?.warehouseId,
      quantity: qtyDelta,
      // leave blocked unchanged unless you specifically want to adjust it too
      batch: det?.batch,
      bestBefore: bb ? { year: bb.year, month: bb.month, day: bb.day } : undefined,
      note: 'Adjusted via ERP UI',
      fulfillerTimestamp: nowIso
    }
    payloads.push(p)
  }

  try {
    const res = await $fetch('/api/ffn/stocks/adjustments', {
      method: 'POST',
      headers: useRequestHeaders(['cookie']),
      body: { orderSourceId: selectedOrderSourceId.value, items: payloads }
    })
    if(Number(res?.status || 200) === 401) return forcingLogout()
    if(Number(res?.status || 200) >= 400) {
      alert.value = { status: res.status, message: res.message || 'Failed to submit adjustments' }
      toast({ message: alert.value.message, type: 'is-danger', duration: 3000 })
    } else {
      alert.value = { status: 200, message: res.message || 'Adjustments sent' }
      toast({ message: alert.value.message, type: 'is-success', duration: 2000 })
      // Apply the adjustments to stockLevel and reset the form
      for(const it of items.value) {
        it.stockLevel = it._targetQty
        originalMap.value[it._rowId] = JSON.parse(JSON.stringify(it))
      }
      await fetchStocks(true)
    }
  } catch (err) {
    alert.value = { status: err?.status || 500, message: err?.message || 'Failed to submit adjustments' }
    toast({ message: alert.value.message, type: 'is-danger', duration: 3000 })
  }
}

const breadCrumb = computed(() => ([
  { link: '/settings/order-sources', text: 'Order Sources' },
  { link: '#', text: 'JTL-FFN Stocks', active: true }
]))

const toInt = (v) => {
  if(v === '' || v === null || v === undefined) return 0
  const n = parseInt(String(v), 10)
  return isNaN(n) ? 0 : n
}

const toNum = (v) => {
  if(v === '' || v === null || v === undefined) return 0
  const n = Number(v)
  return isNaN(n) ? 0 : n
}

const computeAdjust = (it) => {
  const orig = originalMap.value?.[it._rowId]?.stockLevel ?? it.stockLevel ?? 0
  const target = toNum(it._targetQty)
  it._adjust = Number(target) - Number(orig)
}

// Fetch product details for each jfsku and attach a name
const enrichWithProductNames = async () => {
  const orderSourceId = selectedOrderSourceId.value
  if(!orderSourceId || !Array.isArray(items.value) || items.value.length === 0) return

  // simple concurrency control
  const concurrency = 5
  const queue = [...items.value]
  const workers = new Array(concurrency).fill(0).map(async () => {
    while(queue.length) {
      const it = queue.shift()
      try {
        const prod = await $fetch(`/api/ffn/products/${encodeURIComponent(it.jfsku)}`, {
          params: { orderSourceId },
          headers: useRequestHeaders(['cookie'])
        })
        // try common name fields
        const name = prod?.name || prod?.productName || prod?.title || prod?.identifier || ''
        it.productName = name
      } catch (e) {
        // ignore per-item failures; keep table usable
        it.productName = it.productName || ''
      }
    }
  })
  await Promise.all(workers)
}

// Fetch local qty on hand per product by matching JTL jfsku to iDempiere m_product.jsfsku
const enrichWithLocalQty = async () => {
  if(!Array.isArray(items.value) || items.value.length === 0) return
  const concurrency = 5
  const queue = [...items.value]
  const workers = new Array(concurrency).fill(0).map(async () => {
    while(queue.length) {
      const it = queue.shift()
      const jfsku = it.jfsku || ''
      if(!jfsku) { it.localQtyOnHand = undefined; it.productId = undefined; continue }
      try {
        const res = await $fetch('/api/local/qty-on-hand', {
          params: { jfsku, merchantSku: it.merchantSku },
          headers: useRequestHeaders(['cookie'])
        })
        it.localQtyOnHand = Number(res?.qtyOnHand || 0)
        it.productId = res?.productId || undefined
      } catch (e) {
        it.localQtyOnHand = undefined
        it.productId = undefined
      }
    }
  })
  await Promise.all(workers)
}

const openProductInNewTab = (productId) => {
  if(!productId) return
  const link = `/materials/products/${productId}/edit`
  tabs.value.push({ link, text: `Product ${productId}`, active: false })
  localStorage.setItem('tabs', JSON.stringify(tabs.value))
  router.push(link)
}
</script>

<template>
  <Head>
    <Title>JTL-FFN Stocks</Title>
  </Head>

  <NuxtLayout name="new-dash">
    <NewBreadCrumb :items="breadCrumb" />

    <div class="banner datatable-wrapper">
      <div class="columns is-desktop mb-3">
        <div class="column is-half">
          <label class="label d-block">Order Source (JTL-FFN only)</label>
          <div class="group">
            <select v-model="selectedOrderSourceId" class="select">
              <option value="">-- Select --</option>
              <option v-for="os in orderSources" :key="os.id" :value="String(os.id)">{{ os.Name }} ({{ os.Value }})</option>
            </select>
            <button type="button" class="button" :class="!selectedOrderSourceId ? 'pointerEvents-none opacity-0.6' : ''" @click="fetchStocks">Load Stocks</button>
          </div>
        </div>
        <div class="column is-half">
          <div class="group flexEnd">
            <input type="number" min="1" class="input w-120px" v-model.number="page.limit" />
            <button class="button" :disabled="page.offset<=0 || loading" @click="page.offset = Math.max(0, page.offset - page.limit); fetchStocks()">Prev</button>
            <button class="button" :disabled="loading" @click="page.offset = page.offset + page.limit; fetchStocks()">Next</button>
            <button class="button is-primary" :disabled="Object.keys(editedMap).length===0 || loading" @click="submitAdjustments">Submit Changes ({{ Object.keys(editedMap).length }})</button>
          </div>
        </div>
      </div>

      <AlertBox :alert="alert" />

      <div v-if="loading && items.length === 0">Loading...</div>
      <div v-if="items.length > 0">
        <div v-if="loading" class="loading-overlay">Updating...</div>
        <div class="table-responsive">
          <table class="table is-fullwidth">
            <thead>
              <tr>
                <th>jfsku</th>
                <th>merchantSku</th>
                <th>name</th>
                <th>stockLevel</th>
                <th>target qty</th>
                <th>adjustment (+/-)</th>
                <th>reserved</th>
                <th>blocked</th>
                <th>announced</th>
                <th>warehouseId</th>
                <th>local OnHand</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="it in items" :key="it._rowId">
                <td>{{ it.jfsku }}</td>
                <td>{{ it.merchantSku }}</td>
                <td class="product-name-cell">
                  <a v-if="it.productId" href="#" @click.prevent="openProductInNewTab(it.productId)" class="product-link">{{ it.productName || '' }}</a>
                  <span v-else>{{ it.productName || '' }}</span>
                </td>
                <td class="compact-cell">{{ it.stockLevel }}</td>
                <td class="compact-cell">
                  <input type="number" step="1" class="input compact-input" v-model.number="it._targetQty" @input="computeAdjust(it)" @change="computeAdjust(it)" />
                </td>
                <td class="compact-cell">{{ it._adjust }}</td>
                <td class="compact-cell">{{ it.stockLevelReserved }}</td>
                <td class="compact-cell">{{ it.stockLevelBlocked }}</td>
                <td class="compact-cell">{{ it.stockLevelAnnounced }}</td>
                <td>{{ (it.warehouses && it.warehouses[0]?.warehouseId) || '' }}</td>
                <td>{{ it.localQtyOnHand ?? '' }}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </NuxtLayout>
</template>

<style scoped>
.table-responsive { overflow:auto; position: relative; }
.group { display:flex; gap:8px; align-items:center; }
.flexEnd { justify-content:flex-end; }
.w-120px{ width:120px; }
.loading-overlay {
  position: absolute;
  top: 0;
  right: 0;
  background: rgba(255, 255, 255, 0.8);
  padding: 8px 16px;
  border-radius: 4px;
  font-weight: bold;
  z-index: 10;
}
.table tbody tr {
  height: 40px;
  max-height: 40px;
}
.table tbody td {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 4px 8px;
}
.product-name-cell {
  max-width: 300px;
}
.compact-cell {
  width: 80px;
  max-width: 80px;
}
.compact-input {
  width: 70px;
  min-width: 70px;
  padding: 4px 6px;
  height: 30px;
  font-size: 0.9rem;
}
.product-link {
  color: #3273dc;
  text-decoration: none;
}
.product-link:hover {
  text-decoration: underline;
}
</style>
