import crypto from 'node:crypto'
import getTokenHelper from "../../utils/getTokenHelper"
import refreshTokenHelper from "../../utils/refreshTokenHelper"
import errorHandlingHelper from "../../utils/errorHandlingHelper"
import fetchHelper from "../../utils/fetchHelper"
import { resolveMerchantPartner, resolveOwnUserId, readPartnerFieldCI, MERCHANT_PARTNER_FIELDS } from "../../utils/merchantPartner"
import { EMAIL_FIELDS, isValidEmailSyntax, sendVerificationEmail } from "../../utils/emailVerification"
import { createPendingChange, cancelActivePending, countRecentRequests } from "../../utils/emailVerifyDb"

/**
 * Merchant self-service e-mail change — body { fieldKey, email }.
 *
 * fieldKey ∈ EMAIL_FIELDS: the whitelisted c_bpartner addresses plus the
 * caller's own ad_user e-mail (`userEmail`). Everything else is rejected.
 * The target records are resolved server-side from the caller's own
 * identity; the client cannot pick a partner or user id.
 *
 * A new NON-EMPTY address is never written directly: it is parked in the
 * pending-changes store and a confirmation link is mailed to the NEW
 * address (proof of control). Only the confirm endpoint applies the write —
 * until then the old address stays active and the UI shows the pending one.
 *
 * Two cases apply immediately (there is no new address to prove control of):
 *  - clearing an optional field to '' (required fields cannot be cleared)
 *  - re-entering the currently active address (reverting = cancels pending)
 */
const VERIFY_TTL_MS = 48 * 60 * 60 * 1000
const RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000
const RATE_LIMIT_MAX = 10

const handleFunc = async (event: any, authToken: string | null = null) => {
  const config = useRuntimeConfig()
  const serviceToken = (config.api as any)?.idempieretoken
  const token = serviceToken || authToken || await getTokenHelper(event)
  const body = await readBody(event)

  const fieldKey = String(body?.fieldKey || '')
  const def = EMAIL_FIELDS[fieldKey]
  if (!def) return { status: 400, message: 'Unknown e-mail field' }

  const email = String(body?.email ?? '').trim()
  if (email !== '' && !isValidEmailSyntax(email)) {
    return { status: 422, message: 'Invalid email address' }
  }
  if (email === '' && def.required) {
    return { status: 422, message: 'This address cannot be empty' }
  }

  const userId = resolveOwnUserId(event)
  if (!userId) return { status: 401, message: 'No user session' }

  // Resolve target record + its currently active value.
  let targetId: number
  let currentEmail = ''
  if (def.kind === 'aduser') {
    targetId = userId
    const userRes: any = await fetchHelper(event, `models/ad_user/${userId}?$select=EMail`, 'GET', token, null)
    currentEmail = String(userRes?.EMail || '').trim()
  } else {
    const partner = await resolveMerchantPartner(event, token)
    if (!partner) return { status: 404, message: 'No business partner is linked to this account' }
    targetId = partner.id
    currentEmail = String(readPartnerFieldCI(partner.record, MERCHANT_PARTNER_FIELDS[fieldKey].column) || '').trim()
  }

  // Re-entering the active address = revert: drop any pending change, done.
  if (email !== '' && email.toLowerCase() === currentEmail.toLowerCase()) {
    cancelActivePending(def.kind, targetId, fieldKey)
    return { status: 200, applied: true }
  }

  // Clearing an optional field: nothing to verify — apply immediately.
  if (email === '') {
    const payload: Record<string, any> = { [def.writeKey]: '' }
    if (def.kind === 'aduser') payload.tableName = 'AD_User'
    await fetchHelper(event, `models/${def.kind === 'aduser' ? 'ad_user' : 'c_bpartner'}/${targetId}`, 'PUT', token, payload)
    cancelActivePending(def.kind, targetId, fieldKey)
    return { status: 200, applied: true }
  }

  // New address → verification flow.
  if (countRecentRequests(userId, RATE_LIMIT_WINDOW_MS) >= RATE_LIMIT_MAX) {
    return { status: 429, message: 'Too many change requests — please try again later' }
  }

  const language = getCookie(event, 'logship_language') || 'de_DE'
  const verifyToken = crypto.randomBytes(32).toString('hex')
  const pending = createPendingChange({
    token: verifyToken,
    kind: def.kind,
    fieldKey,
    targetId,
    requestedBy: userId,
    oldEmail: currentEmail,
    newEmail: email,
    language,
    ttlMs: VERIFY_TTL_MS
  })
  if (!pending) {
    return { status: 500, message: 'Verification store unavailable — please contact support' }
  }

  try {
    await sendVerificationEmail({ fieldKey, oldEmail: currentEmail, newEmail: email, token: verifyToken, language })
  } catch (e: any) {
    // Undo the pending row — a confirmation that can never arrive must not
    // block the field in the UI.
    cancelActivePending(def.kind, targetId, fieldKey)
    console.error('[EmailVerification] send failed:', e?.message)
    return { status: 502, message: 'Confirmation e-mail could not be sent — please try again later' }
  }

  return { status: 200, applied: false, verificationSentTo: email }
}

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

  try {
    data = await handleFunc(event)
  } catch (err: any) {
    try {
      const authToken: any = await refreshTokenHelper(event)
      data = await handleFunc(event, authToken)
    } catch (error: any) {
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
    }
  }

  return data
})
