import refreshTokenHelper from "../../../utils/refreshTokenHelper"
import forceLogoutHelper from "../../../utils/forceLogoutHelper"
import errorHandlingHelper from "../../../utils/errorHandlingHelper"
import { resolveOrderSourceAmazonConnection, getListingsItem, patchListingsItemPrice, updateCachedListingPrice, withPricePushQueue } from "../../../utils/amazonSpApi"

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

// SIS — push one corrected price to Amazon. Body: { orderSourceId, sku,
// newPrice }. No persisted audit trail (explicitly out of scope) — every
// push is console.log'd (greppable in the PM2 out-log, same traceability
// convention as commissionTable.ts) and the confirmed new state is returned
// so the frontend can update the row + its own in-session "recent changes"
// list.
//
// IMPORTANT (verified against real production data — the read-after-write
// used to show the stale price even on a genuinely successful push): the
// Listings Items API PATCH is NOT synchronous. It returns `status:
// "ACCEPTED"` — a submission that Amazon processes asynchronously, same
// shape as the Feeds API, just normally fast. An immediate GET right after
// can still show the old price. So: poll briefly for the read to catch up,
// but once Amazon has ACCEPTED the submission with no blocking issues,
// treat the push as successful regardless of whether the poll converged in
// time, and report the requested price rather than a stale read.
const handleFunc = async (event: any, authToken: any = null) => {
  const token = authToken ?? await getTokenHelper(event)
  const body = await readBody(event)
  const orderSourceId = Number(body?.orderSourceId)
  const sku = String(body?.sku || '').trim()
  const newPrice = Number(body?.newPrice)

  if (!orderSourceId) return { status: 400, message: 'orderSourceId is required' }
  if (!sku) return { status: 400, message: 'sku is required' }
  if (!Number.isFinite(newPrice) || newPrice <= 0) return { status: 400, message: 'newPrice must be a positive number' }

  const { sellerId, marketplaceId, accessToken } = await resolveOrderSourceAmazonConnection(event, token, orderSourceId)

  let userLabel = 'unknown'
  try {
    const raw = getCookie(event, 'logship_user')
    const user = raw ? JSON.parse(raw) : null
    userLabel = user?.Name || user?.email || getCookie(event, 'logship_user_id') || 'unknown'
  } catch {
    userLabel = getCookie(event, 'logship_user_id') || 'unknown'
  }

  // Amazon's Listings Items operations are tightly rate-limited (~1 req/sec,
  // no bulk equivalent — see throttledBatch's comment in amazonSpApi.ts) and
  // the frontend's per-field push is deliberately non-blocking (the user can
  // tab to the next row before this resolves), so several pushes to the same
  // seller can otherwise fire concurrently. Serialize + pace them here
  // instead of changing that UX.
  return withPricePushQueue(orderSourceId, async () => {
    const before = await getListingsItem(accessToken, sellerId, sku, marketplaceId).catch(() => null)

    let patchResult: any = null
    try {
      // Pass `before` through so patchListingsItemPrice can skip its own
      // redundant GET of the same SKU just to read productType.
      patchResult = await patchListingsItemPrice(accessToken, sellerId, sku, marketplaceId, newPrice, 'EUR', before)
    } catch (e: any) {
      console.error('[sis] price push FAILED', { user: userLabel, orderSourceId, sku, oldPrice: before?.price ?? null, newPrice, error: e?.message || String(e) })
      throw e
    }

    const blockingIssues = (patchResult?.issues || []).filter((i: any) => (i?.severity || '').toUpperCase() !== 'WARNING')
    if (patchResult?.status !== 'ACCEPTED' || blockingIssues.length > 0) {
      const msg = blockingIssues.map((i: any) => i.message).join('; ') || `Amazon rejected the submission (status: ${patchResult?.status})`
      console.error('[sis] price push REJECTED', { user: userLabel, orderSourceId, sku, newPrice, issues: patchResult?.issues })
      return { status: 502, message: `Amazon rejected the price update: ${msg}` }
    }

    // Poll briefly for the read to catch up (Amazon applies ACCEPTED submissions
    // asynchronously) — not required for success, just for an accurate confirmed
    // read when possible instead of always falling back to the requested price.
    let after: any = null
    for (let attempt = 0; attempt < 4; attempt++) {
      await sleep(1500)
      after = await getListingsItem(accessToken, sellerId, sku, marketplaceId).catch(() => null)
      if (after?.price != null && Math.abs(after.price - newPrice) < 0.005) break
    }
    const confirmedPrice = (after?.price != null && Math.abs(after.price - newPrice) < 0.005) ? after.price : newPrice
    const pending = confirmedPrice === newPrice && after?.price !== newPrice

    // Keep the cached listings report (up to 30 min old) from serving the
    // stale pre-push price on the next reload/refresh — trust the ACCEPTED
    // submission even if the poll above didn't converge in time.
    updateCachedListingPrice(orderSourceId, sku, confirmedPrice)

    console.log('[sis] price push', {
      user: userLabel,
      orderSourceId,
      sku,
      oldPrice: before?.price ?? null,
      requestedPrice: newPrice,
      confirmedPrice,
      pending
    })

    return {
      status: 200,
      sku,
      oldPrice: before?.price ?? null,
      requestedPrice: newPrice,
      pending,
      confirmed: { ...(after || {}), price: confirmedPrice }
    }
  })
}

export default defineEventHandler(async (event) => {
  let data: any = {}

  try {
    data = await handleFunc(event)
  } catch (err: any) {
    // An Amazon-side failure (429, rejected submission, timeout, ...) is not
    // an auth problem — retrying would silently re-run the whole push (up to
    // ~7 Amazon calls) a second time, doubling load exactly when Amazon is
    // already unhappy. Fail straight to the client instead.
    if (err?.isAmazonError) {
      data = errorHandlingHelper(err?.data ?? err, err?.data ?? err)
      return data
    }
    try {
      let authToken: any = await refreshTokenHelper(event)
      data = await handleFunc(event, authToken)
    } catch (error: any) {
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
      forceLogoutHelper(event, data)
    }
  }

  return data
})
