/**
 * API key management routes — white-label-api.
 *
 * Mounted at /api-keys in routes/index.ts.
 *
 * GET    /     — list tenant API keys
 * POST   /     — create API key (OWNER only; key shown once)
 * DELETE /:id  — revoke API key (OWNER only)
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../types'
import { authMiddleware } from '../middleware/auth'
import { requirePermission, requireTier } from '../middleware/guards'
import { createDb, listTenantApiKeys, createTenantApiKey, revokeTenantApiKey } from '@zync/db/queries'
import { hashToken } from '@zync/auth'
import { TenantTier } from '@zync/types'

const ENTERPRISE_ONLY_SCOPES = new Set(['leads:write', 'campaigns:write'])

const createApiKeySchema = z.object({
  name: z.string().trim().min(1).max(64),
  scopes: z.array(z.string().min(1)).min(1),
})

export const apiKeysRoutes = new Hono<AppEnv>()

apiKeysRoutes.use('*', authMiddleware)

function isOwnerRole(role: string | undefined): boolean {
  return role === 'owner' || role === 'OWNER'
}

function generateApiKeySecret(length = 32): string {
  const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'
  const bytes = crypto.getRandomValues(new Uint8Array(length))
  let out = ''
  for (let i = 0; i < length; i++) {
    out += alphabet[bytes[i]! % alphabet.length]
  }
  return out
}

function toView(row: {
  id: string
  name: string
  key_prefix: string
  scopes: string[]
  created_at: string
  last_used_at: string | null
  revoked_at: string | null
  created_by: string
}) {
  return {
    id: row.id,
    name: row.name,
    keyPrefix: row.key_prefix,
    scopes: row.scopes,
    status: row.revoked_at ? 'revoked' : 'active',
    createdAt: row.created_at,
    lastUsedAt: row.last_used_at,
    revokedAt: row.revoked_at,
    createdBy: row.created_by,
  }
}

apiKeysRoutes.get(
  '/',
  requireTier(TenantTier.BUSINESS),
  requirePermission('settings:read'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const db = createDb(c.env)
    const keys = await listTenantApiKeys(db, session.tid)
    return c.json(keys.map(toView), 200)
  },
)

apiKeysRoutes.post(
  '/',
  requireTier(TenantTier.BUSINESS),
  requirePermission('settings:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }
    if (!isOwnerRole((session as { role?: string }).role)) {
      return c.json({ error: 'Owner role required' }, 403)
    }

    const body = await c.req.json().catch(() => null)
    const parsed = createApiKeySchema.safeParse(body)
    if (!parsed.success) {
      return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
    }

    const tier = (session as { tier?: string }).tier
    if (tier === TenantTier.BUSINESS && parsed.data.scopes.some((scope) => ENTERPRISE_ONLY_SCOPES.has(scope))) {
      return c.json({ error: 'enterprise_scope_required' }, 403)
    }

    const secret = generateApiKeySecret()
    const key = `zyk_live_${secret}`
    const keyHash = await hashToken(key)

    const db = createDb(c.env)
    const created = await createTenantApiKey(db, session.tid, session.sub, {
      name: parsed.data.name,
      keyPrefix: key.slice(0, 8),
      keyHash,
      scopes: parsed.data.scopes,
    })

    return c.json(
      {
        key,
        id: created.id,
        name: created.name,
        keyPrefix: created.key_prefix,
        scopes: created.scopes,
        createdAt: created.created_at,
      },
      201,
    )
  },
)

apiKeysRoutes.delete(
  '/:id',
  requireTier(TenantTier.BUSINESS),
  requirePermission('settings:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }
    if (!isOwnerRole((session as { role?: string }).role)) {
      return c.json({ error: 'Owner role required' }, 403)
    }

    const { id } = c.req.param()
    const db = createDb(c.env)
    const revoked = await revokeTenantApiKey(db, session.tid, id, session.sub)
    if (!revoked) return c.json({ error: 'Not found' }, 404)

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