/**
 * Comms expense intake — expenses-module.
 * Creates expense records from receipt photos/documents sent via WhatsApp or Telegram.
 */
import { createDb, getRoleByName, getTenantById, loadAdapterCredentialRow } from '@zync/db/queries'
import { decryptCredential } from '@zync/auth'
import { createExpense } from '@zync/expenses'
import { emitExpenseWebhook } from '@zync/expenses'
import type { Env } from '@zync/types'
import type { InboundMessage, TenantId } from '@zync/types'
import type { ExpenseProcessJob } from '../queues/expense-process'

const ALLOWED_MIME: Record<string, 'pdf' | 'jpg' | 'png' | 'heic'> = {
  'application/pdf': 'pdf',
  'image/jpeg': 'jpg',
  'image/jpg': 'jpg',
  'image/png': 'png',
  'image/heic': 'heic',
}

interface ReceiptAttachment {
  filename: string
  contentType: string
  data: ArrayBuffer
}

function inferFileType(contentType: string, filename: string): 'pdf' | 'jpg' | 'png' | 'heic' | null {
  const fromMime = ALLOWED_MIME[contentType.toLowerCase()]
  if (fromMime) return fromMime
  const ext = filename.split('.').pop()?.toLowerCase()
  if (ext === 'pdf' || ext === 'jpg' || ext === 'jpeg') return ext === 'jpeg' ? 'jpg' : ext as 'pdf' | 'jpg'
  if (ext === 'png' || ext === 'heic') return ext
  return null
}

async function downloadTelegramFile(token: string, fileId: string): Promise<ReceiptAttachment | null> {
  const fileRes = await fetch(`https://api.telegram.org/bot${token}/getFile?file_id=${encodeURIComponent(fileId)}`)
  if (!fileRes.ok) return null
  const fileJson = (await fileRes.json()) as { ok?: boolean; result?: { file_path?: string } }
  const filePath = fileJson.result?.file_path
  if (!filePath) return null

  const downloadRes = await fetch(`https://api.telegram.org/file/bot${token}/${filePath}`)
  if (!downloadRes.ok) return null
  const data = await downloadRes.arrayBuffer()
  const filename = filePath.split('/').pop() ?? 'receipt.jpg'
  const contentType = downloadRes.headers.get('content-type') ?? 'image/jpeg'
  return { filename, contentType, data }
}

async function downloadWhatsAppMedia(
  accessToken: string,
  mediaId: string,
): Promise<ReceiptAttachment | null> {
  const metaRes = await fetch(`https://graph.facebook.com/v19.0/${mediaId}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  })
  if (!metaRes.ok) return null
  const meta = (await metaRes.json()) as { url?: string; mime_type?: string; id?: string }
  if (!meta.url) return null

  const downloadRes = await fetch(meta.url, {
    headers: { Authorization: `Bearer ${accessToken}` },
  })
  if (!downloadRes.ok) return null
  const data = await downloadRes.arrayBuffer()
  const contentType = meta.mime_type ?? downloadRes.headers.get('content-type') ?? 'image/jpeg'
  const ext = ALLOWED_MIME[contentType] ?? 'jpg'
  return { filename: `whatsapp-receipt.${ext}`, contentType, data }
}

async function resolveCreatedBy(db: ReturnType<typeof createDb>, tenantId: string): Promise<string | null> {
  const ownerRole = await getRoleByName(db, tenantId as TenantId, 'OWNER')
  if (!ownerRole) return null

  const membership = await db.query.tenantMemberships.findFirst({
    where: (m, { eq, and }) =>
      and(
        eq(m.tenantId, tenantId),
        eq(m.roleId, ownerRole.id),
        eq(m.status, 'active'),
      ),
  })

  return membership?.userId ?? null
}

export async function tryCommsExpenseIntake(
  env: Env,
  tenantId: string,
  source: 'whatsapp' | 'telegram',
  message: InboundMessage,
): Promise<boolean> {
  const db = createDb(env)
  const tenant = await getTenantById(db, tenantId as TenantId)
  if (!tenant) return false

  let attachment: ReceiptAttachment | null = null

  if (source === 'telegram') {
    const fileId =
      (message.metadata['documentFileId'] as string | undefined) ??
      (message.metadata['photoFileId'] as string | undefined)
    if (!fileId) return false

    const credRow = await loadAdapterCredentialRow(db, tenantId, 'telegram')
    if (!credRow) return false
    const token = await decryptCredential(
      { ciphertext: credRow.ciphertext, iv: credRow.iv, authTag: credRow.authTag },
      env.INTEGRATION_ENCRYPTION_KEY,
    )
    attachment = await downloadTelegramFile(token, fileId)
  } else {
    const mediaId = message.metadata['waMediaId'] as string | undefined
    if (!mediaId) return false

    const credRow = await loadAdapterCredentialRow(db, tenantId, 'whatsapp')
    if (!credRow) return false
    const credJson = await decryptCredential(
      { ciphertext: credRow.ciphertext, iv: credRow.iv, authTag: credRow.authTag },
      env.INTEGRATION_ENCRYPTION_KEY,
    )
    const cred = JSON.parse(credJson) as { access_token?: string }
    if (!cred.access_token) return false
    attachment = await downloadWhatsAppMedia(cred.access_token, mediaId)
  }

  if (!attachment) return false

  const fileType = inferFileType(attachment.contentType, attachment.filename)
  if (!fileType) return false

  const createdBy = await resolveCreatedBy(db, tenantId)
  if (!createdBy) {
    console.warn(`[comms-expense] No active OWNER membership for tenant ${tenantId}; skipping intake`)
    return false
  }

  const expenseId = crypto.randomUUID()
  const r2Key = `${tenantId}/expenses/${expenseId}/${attachment.filename}`

  await env.STORAGE.put(r2Key, attachment.data, {
    httpMetadata: { contentType: attachment.contentType },
  })

  const expense = await createExpense(db, tenantId, {
    tenantId,
    createdBy,
    r2Key,
    fileName: attachment.filename,
    fileType,
    fileSizeBytes: attachment.data.byteLength,
    source,
    sourceMetadata: {
      sender: message.from,
      chatId: message.chatId,
      ...message.metadata,
    },
    status: 'PENDING',
  })

  await emitExpenseWebhook(env, tenantId, 'expense.uploaded', {
    expenseId: expense.id,
    fileName: attachment.filename,
    source,
  })

  const job: ExpenseProcessJob = {
    type: 'expense.process',
    tenantId,
    expenseId: expense.id,
    tier: tenant.tier ?? 'freelancer',
  }
  await env.EXPENSE_QUEUE.send(job)
  return true
}
