/**
 * Billing API routes — billing-module (wave-8 leaf 1).
 *
 * Mounted at /api/billing in apps/zync-api/src/routes/index.ts.
 *
 * GET    /billing/plan             — current plan + usage stats
 * GET    /billing/payment-methods  — list saved payment methods
 * GET    /billing/history          — past invoices / charges
 * PATCH  /billing/email            — update billing contact email
 *
 * All routes: authMiddleware + OWNER or ADMIN role check.
 * Zod validation on all request bodies.
 * No raw Drizzle — all DB ops via @zync/db/queries.
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { SessionPayload } from '@zync/types'
import { authMiddleware } from '../../middleware/auth'
import { blockDuringImpersonation } from '../../middleware/block-impersonation-ops'
import {
  createDb,
  getTenantBilling,
  getPaymentMethods,
  getBillingHistory,
  updateBillingEmail,
  logAuditEvent,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'

export const billingRoutes = new Hono<AppEnv>()

billingRoutes.use('*', authMiddleware)

// ── Auth guard ────────────────────────────────────────────────────────────────

const OWNER_ADMIN = new Set(['OWNER', 'ADMIN'])

function requireOwnerOrAdmin(session: SessionPayload | undefined): boolean {
  if (!session || session.type !== 'user') return false
  return OWNER_ADMIN.has(session.role)
}

// ── GET /billing/plan ─────────────────────────────────────────────────────────

billingRoutes.get('/plan', async (c) => {
  const session = c.get('session') as SessionPayload | undefined
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireOwnerOrAdmin(session)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const db = createDb(c.env)
  const billing = await getTenantBilling(db, session.tid)
  if (!billing) {
    return c.json({ error: 'Tenant not found' }, 404)
  }

  return c.json(billing, 200)
})

// ── GET /billing/payment-methods ──────────────────────────────────────────────

billingRoutes.get('/payment-methods', async (c) => {
  const session = c.get('session') as SessionPayload | undefined
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireOwnerOrAdmin(session)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const db = createDb(c.env)
  const methods = await getPaymentMethods(db, session.tid)

  return c.json({ paymentMethods: methods }, 200)
})

// ── GET /billing/history ──────────────────────────────────────────────────────

const historyQuerySchema = z.object({
  limit: z.coerce.number().int().min(1).max(100).default(20),
})

billingRoutes.get('/history', async (c) => {
  const session = c.get('session') as SessionPayload | undefined
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireOwnerOrAdmin(session)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const parsed = historyQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  const history = await getBillingHistory(db, session.tid, parsed.data.limit)

  return c.json(history, 200)
})

// ── PATCH /billing/email ──────────────────────────────────────────────────────

const patchEmailSchema = z.object({
  email: z.string().email().max(254),
})

billingRoutes.patch('/email', blockDuringImpersonation(), async (c) => {
  const session = c.get('session') as SessionPayload | undefined
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireOwnerOrAdmin(session)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const parsed = patchEmailSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  await updateBillingEmail(db, session.tid, parsed.data.email)

  await logAuditEvent(c, {
    tenantId: session.tid,
    userId: session.sub,
    entityType: 'billing',
    entityId: session.tid,
    eventType: 'settings.updated',
    metadata: { field: 'billing_email', new_value: parsed.data.email },
  })

  return c.json({ email: parsed.data.email }, 200)
})
