/**
 * WhatsApp inbound webhook receiver — system-communications-notifications (Task 14).
 *
 * GET  /api/webhooks/whatsapp/:tenantId — Meta webhook verification challenge
 * POST /api/webhooks/whatsapp/:tenantId — Inbound message events
 *
 * Tier gate: WhatsApp requires Enterprise tier (requireTier('enterprise')).
 * Signature: X-Hub-Signature-256 HMAC-SHA256 (timing-safe — no early-return).
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { timingSafeEqual, decryptCredential, meetsMinimumTier } from '@zync/auth'
import { createDb, loadAdapterCredentialRow, getTenantById } from '@zync/db/queries'
import type { AppEnv } from '../../types'
import type { InboundMessage, TenantId } from '@zync/types'
import { TenantTier } from '@zync/types'
import { claimInboundWebhookEvent } from '../../lib/webhook-idempotency'

const whatsappRouter = new Hono<AppEnv>()

const tenantParamSchema = z.object({
  tenantId: z.string().uuid(),
})

/**
 * HMAC-SHA256 of `data` using `secret`. Returns hex digest.
 */
async function hmacSha256Hex(secret: string, data: string): Promise<string> {
  const keyBytes = new TextEncoder().encode(secret)
  const dataBytes = new TextEncoder().encode(data)
  const key = await crypto.subtle.importKey(
    'raw',
    keyBytes,
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign'],
  )
  const sigBuf = await crypto.subtle.sign('HMAC', key, dataBytes)
  return Array.from(new Uint8Array(sigBuf))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('')
}

// GET /api/webhooks/whatsapp/:tenantId — Meta verification challenge
whatsappRouter.get('/:tenantId', async (c) => {
  const paramParsed = tenantParamSchema.safeParse({ tenantId: c.req.param('tenantId') })
  if (!paramParsed.success) return c.json({ error: 'Invalid tenant ID' }, 400)

  const mode = c.req.query('hub.mode')
  const token = c.req.query('hub.verify_token')
  const challenge = c.req.query('hub.challenge')

  if (mode !== 'subscribe') return c.json({ error: 'Invalid mode' }, 403)
  if (!challenge) return c.json({ error: 'Missing challenge' }, 400)

  const db = createDb(c.env)
  const credRow = await loadAdapterCredentialRow(db, paramParsed.data.tenantId, 'whatsapp')
  if (!credRow) return c.json({ error: 'Not configured' }, 403)

  const credJson = await decryptCredential(
    { ciphertext: credRow.ciphertext, iv: credRow.iv, authTag: credRow.authTag },
    c.env.INTEGRATION_ENCRYPTION_KEY,
  )
  const cred = JSON.parse(credJson) as { verify_token: string }

  // Timing-safe token check
  if (!timingSafeEqual(token ?? '', cred.verify_token)) {
    return c.json({ error: 'Invalid verify token' }, 403)
  }

  return c.text(challenge)
})

// POST /api/webhooks/whatsapp/:tenantId — Inbound events
whatsappRouter.post('/:tenantId', async (c) => {
  const paramParsed = tenantParamSchema.safeParse({ tenantId: c.req.param('tenantId') })
  if (!paramParsed.success) return c.json({ error: 'Invalid tenant ID' }, 400)
  const { tenantId } = paramParsed.data

  // Tier gate: WhatsApp requires Enterprise tier.
  // Since this is an inbound webhook (no session), we check the tenant tier via DB.
  const db = createDb(c.env)
  const tenant = await getTenantById(db, tenantId as TenantId)
  if (!tenant || !meetsMinimumTier(tenant.tier as TenantTier, TenantTier.ENTERPRISE)) {
    return c.json({ error: 'Enterprise tier required for WhatsApp' }, 402)
  }

  const rawBody = await c.req.text()
  const hubSignature = c.req.header('X-Hub-Signature-256')

  const credRow = await loadAdapterCredentialRow(db, tenantId, 'whatsapp')
  if (!credRow) {
    return c.json({ ok: true }) // No integration configured
  }

  const credJson = await decryptCredential(
    { ciphertext: credRow.ciphertext, iv: credRow.iv, authTag: credRow.authTag },
    c.env.INTEGRATION_ENCRYPTION_KEY,
  )
  const cred = JSON.parse(credJson) as { app_secret: string }

  // Active integration → signature verification is mandatory (fail closed).
  if (!cred.app_secret?.trim()) {
    console.error('[whatsapp-webhook] active integration missing app_secret', { tenantId })
    return c.json({ error: 'Integration misconfigured' }, 503)
  }
  if (!hubSignature) {
    return c.json({ error: 'Missing signature' }, 401)
  }
  const computedHex = await hmacSha256Hex(cred.app_secret, rawBody)
  const computed = `sha256=${computedHex}`
  if (!timingSafeEqual(hubSignature, computed)) {
    return c.json({ error: 'Invalid signature' }, 401)
  }

  // Parse payload
  let body: Record<string, unknown>
  try {
    body = JSON.parse(rawBody)
  } catch {
    return c.json({ error: 'Invalid JSON' }, 400)
  }

  const inboundMessage = parseWhatsAppEvent(body)
  if (!inboundMessage) {
    return c.json({ ok: true })
  }

  // Dedup targets inbound messages only: parseWhatsAppEvent returns null for
  // statuses/read receipts (no `messages` array), so those never enqueue.
  // waMessageId is Meta's message.id; when absent on a malformed payload we skip
  // dedup — no stable provider event id and non-message events are already filtered.
  const waMessageId = inboundMessage.metadata?.waMessageId as string | undefined
  if (waMessageId) {
    const claimed = await claimInboundWebhookEvent(c.env.KV, 'whatsapp', waMessageId)
    if (!claimed) {
      return c.json({ ok: true })
    }
  }

  // Enqueue for async processing
  await c.env.QUEUE.send({
    type: 'comms.inbound',
    source: 'whatsapp',
    tenantId,
    message: inboundMessage,
    receivedAt: new Date().toISOString(),
  })

  return c.json({ ok: true })
})

function parseWhatsAppEvent(body: Record<string, unknown>): InboundMessage | null {
  try {
    const entry = (body['entry'] as Array<Record<string, unknown>> | undefined)?.[0]
    if (!entry) return null
    const changes = (entry['changes'] as Array<Record<string, unknown>> | undefined)?.[0]
    if (!changes) return null
    const value = changes['value'] as Record<string, unknown> | undefined
    if (!value) return null
    const messages = value['messages'] as Array<Record<string, unknown>> | undefined
    const msg = messages?.[0]
    if (!msg) return null

    const from = msg['from'] as string | undefined
    const msgType = msg['type'] as string | undefined
    const textObj = msg['text'] as { body?: string } | undefined
    const text = textObj?.body ?? ''
    const image = msg['image'] as { id?: string; mime_type?: string; caption?: string } | undefined
    const document = msg['document'] as { id?: string; mime_type?: string; filename?: string; caption?: string } | undefined
    const caption = image?.caption ?? document?.caption ?? ''
    const phoneNumberId = value['metadata']
      ? (value['metadata'] as { phone_number_id?: string })['phone_number_id'] ?? ''
      : ''

    if (!from) return null

    const waMediaId = image?.id ?? document?.id
    const waMimeType = image?.mime_type ?? document?.mime_type

    return {
      from,
      text: text || caption,
      chatId: phoneNumberId || from,
      metadata: {
        waMessageId: msg['id'],
        waTimestamp: msg['timestamp'],
        waType: msgType,
        waPhoneNumberId: phoneNumberId,
        waMediaId,
        waMimeType,
        waDocumentFilename: document?.filename,
      },
    }
  } catch {
    return null
  }
}

export { whatsappRouter }
