import { Router, Request, Response } from 'express'
import { syncEntitlement } from '../services/entitlement'
import { RevenueCatEvent } from '../types'
import { getEnvValue } from '../config/env'
import * as crypto from 'crypto'

const router = Router()

/**
 * Loads a RevenueCat Ed25519 public key from its base64 form.
 * RevenueCat typically distributes the raw 32-byte public key (base64).
 * We also accept DER SPKI form for forward compatibility.
 */
function loadRevenueCatPublicKey(base64Key: string): crypto.KeyObject {
  const keyBytes = Buffer.from(base64Key, 'base64')

  if (keyBytes.length === 32) {
    // Raw Ed25519 public key -> encode as JWK
    return crypto.createPublicKey({
      key: {
        kty: 'OKP',
        crv: 'Ed25519',
        x: keyBytes.toString('base64url'),
      },
      format: 'jwk',
    })
  }

  // Otherwise assume DER-encoded SubjectPublicKeyInfo
  return crypto.createPublicKey({
    key: keyBytes,
    format: 'der',
    type: 'spki',
  })
}

/**
 * Verifies RevenueCat webhook signature using Ed25519.
 * RevenueCat signs the raw request body with their signing key.
 * Returns false for invalid signature/header; throws only for misconfiguration
 * so the caller can return a 500 (fail-closed).
 */
function verifyRevenueCatSignature(req: Request): boolean {
  const signingKey = process.env.REVENUECAT_SIGNING_KEY

  // If signing key is not configured, fail closed (not dev mode)
  if (!signingKey || signingKey.trim() === '') {
    console.error('[webhook] REVENUECAT_SIGNING_KEY not set — rejecting all requests (fail-closed)')
    throw new Error('REVENUECAT_SIGNING_KEY must be set in production')
  }

  const signatureHeader = req.headers['x-revenuecat-signature']
  if (!signatureHeader) {
    console.error('[webhook] Missing X-RevenueCat-Signature header')
    return false
  }

  // The raw body should have been captured by middleware
  const rawBody = (req as any).rawBody
  if (!rawBody || !Buffer.isBuffer(rawBody)) {
    console.error('[webhook] Raw body not available for signature verification')
    return false
  }

  try {
    const signature = Buffer.from(
      Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader,
      'base64'
    )
    const publicKey = loadRevenueCatPublicKey(signingKey)

    // Ed25519 uses no digest algorithm in Node.js
    return crypto.verify(null, rawBody, publicKey, signature)
  } catch (err) {
    console.error('[webhook] Signature verification failed:', err)
    return false
  }
}

/**
 * Verifies RevenueCat webhook secret using constant-time comparison.
 * Kept as a fallback when signature verification is not configured.
 * A missing secret causes verification to fail (fail-closed).
 */
function verifyRevenueCatSecret(req: Request): boolean {
  try {
    const secret = getEnvValue('REVENUECAT_WEBHOOK_SECRET')
    const authHeader = req.headers['authorization']
    const auth = (Array.isArray(authHeader) ? authHeader[0] : authHeader) ?? ''

    // Missing secret = fail closed
    if (!secret) {
      console.error('[webhook] REVENUECAT_WEBHOOK_SECRET not set — rejecting all requests (fail-closed)')
      return false
    }

    // Constant-time comparison to prevent timing attacks
    const authBuffer = Buffer.from(auth)
    const secretBuffer = Buffer.from(secret)

    if (authBuffer.length !== secretBuffer.length) {
      return false
    }

    return crypto.timingSafeEqual(authBuffer, secretBuffer)
  } catch (err) {
    console.error('[webhook] Secret verification error:', err)
    return false
  }
}

router.post('/revenuecat', async (req: Request, res: Response) => {
  const signatureKeyConfigured = !!process.env.REVENUECAT_SIGNING_KEY?.trim()
  const secretConfigured = !!process.env.REVENUECAT_WEBHOOK_SECRET?.trim()

  // Fail closed: at least one auth method must be configured
  if (!signatureKeyConfigured && !secretConfigured) {
    console.error('[webhook] No webhook auth configured — rejecting all requests (fail-closed)')
    res.status(500).json({ error: 'webhook_auth_not_configured' })
    return
  }

  let verified = false
  try {
    verified = signatureKeyConfigured
      ? verifyRevenueCatSignature(req)
      : verifyRevenueCatSecret(req)
  } catch (err: any) {
    // Configuration/internal errors are surfaced as 500 (fail-closed)
    console.error('[webhook] Auth verification error:', err)
    res.status(500).json({ error: 'auth_verification_failed', message: err.message })
    return
  }

  if (!verified) {
    res.status(401).json({ error: 'unauthorized' })
    return
  }

  const body = req.body as RevenueCatEvent
  if (!body?.event?.type || !body?.event?.app_user_id) {
    res.status(400).json({ error: 'malformed payload' })
    return
  }

  // Acknowledge immediately — RC retries if we don't respond within 10s
  res.status(200).json({ received: true })

  try {
    await syncEntitlement(body)
  } catch (err) {
    console.error('[webhook] entitlement sync failed:', err)
  }
})

export default router
