import { verifyAuthentication, decodeUserHandle } from '../../../utils/webauthnHelper'
import { consumeChallenge } from '../../../utils/webauthnChallengeStore'
import { readCredentials, writeCredentials, fetchUserName } from '../../../utils/adUserCredentials'
import { decryptPassword } from '../../../utils/passwordCipher'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const response = body?.response
  if (!response?.response?.clientDataJSON) {
    throw createError({ statusCode: 400, statusMessage: 'Missing authentication response' })
  }

  // 1. Decode + consume challenge (signed cookie issued by login-options)
  const clientDataStr = Buffer.from(response.response.clientDataJSON, 'base64url').toString('utf8')
  const clientData = JSON.parse(clientDataStr)
  const challenge: string = clientData.challenge
  const entry = consumeChallenge(event, 'login', challenge)
  if (!entry) {
    throw createError({ statusCode: 400, statusMessage: 'Challenge expired or invalid' })
  }

  // 2. Resolve user from userHandle (we encoded ad_user.id as the userHandle
  //    during registration, so discoverable-credential responses give it back)
  const userId = decodeUserHandle(response.response?.userHandle)
  if (!userId) {
    throw createError({ statusCode: 400, statusMessage: 'No user handle in response' })
  }

  // 3. Load credentials for this user (uses the iDempiere service token)
  const stored = await readCredentials(event, userId).catch(() => ({ keys: [], password: null }))
  const matching = stored.keys.find(k => k.credentialId === response.id)
  if (!matching) {
    throw createError({ statusCode: 400, statusMessage: 'Credential not registered' })
  }
  if (!stored.password) {
    throw createError({ statusCode: 400, statusMessage: 'No stored credentials — please re-register your security key' })
  }

  // 4. Verify the assertion
  const verification = await verifyAuthentication({
    response,
    expectedChallenge: challenge,
    credential: {
      id: matching.credentialId,
      publicKey: new Uint8Array(Buffer.from(matching.publicKey, 'base64')),
      counter: matching.counter,
      transports: matching.transports as any,
    },
  }).catch((e: any) => {
    throw createError({ statusCode: 400, statusMessage: 'Verification failed: ' + (e?.message ?? 'unknown error') })
  })

  if (!verification.verified) {
    throw createError({ statusCode: 400, statusMessage: 'Verification failed' })
  }

  // 5. Update counter
  matching.counter = verification.authenticationInfo.newCounter
  await writeCredentials(event, userId, stored).catch(() => null)

  // 6. Decrypt the iDempiere password
  let password: string
  try {
    password = decryptPassword(stored.password)
  } catch (e: any) {
    throw createError({ statusCode: 500, statusMessage: 'Could not decrypt stored credentials: ' + (e?.message ?? 'unknown') })
  }

  // 7. Resolve username (the login id iDempiere expects)
  const userName = await fetchUserName(event, userId)
  if (!userName) {
    throw createError({ statusCode: 500, statusMessage: 'Could not resolve username for user ' + userId })
  }

  // 8. Delegate to the EXISTING /api/idempiere-auth/login endpoint via internal
  //    $fetch.raw. login.post.ts is left 100% untouched — it does the auth
  //    call, sets all cookies, runs auto-finalize. We forward its Set-Cookie
  //    headers to our outer response so the browser sees them.
  const isPwa = !!body?.pwa
  const isMobileWorker = !!body?.mobileWorker || getCookie(event, 'logship_mw') === '1'

  const loginRes = await $fetch.raw('/api/idempiere-auth/login', {
    method: 'POST',
    headers: {
      // forward existing cookies so client_id etc. may be available
      cookie: getHeader(event, 'cookie') ?? '',
    },
    body: {
      userName,
      password,
      remember: false,
      selectRole: false,
      mobileWorker: isMobileWorker,
      pwa: isPwa,
    },
  }).catch((e: any) => {
    throw createError({
      statusCode: e?.statusCode ?? e?.status ?? 500,
      statusMessage: e?.statusMessage ?? e?.message ?? 'Login failed',
    })
  })

  // Forward Set-Cookie headers from the internal login response to our caller.
  // Without this, the browser never sees logship_it / logship_user / etc.
  const setCookies: string[] = (loginRes.headers as any).getSetCookie?.()
    ?? (loginRes.headers as any).raw?.()['set-cookie']
    ?? []
  for (const c of setCookies) {
    appendResponseHeader(event, 'set-cookie', c)
  }

  return loginRes._data
})
