/**
 * zync-public-api — Cloudflare Worker entry point.
 * Tenant public REST API at api.zync.is/v1/*
 * tenant-public-api (wave-11 leaf-D)
 */
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import type { Env } from './env'
import { securityHeadersMiddleware } from './middleware/security-headers'
import { apiKeyAuth } from './middleware/auth'
import { perKeyRateLimit } from './middleware/rate-limit'
import { customersRouter } from './routes/customers'
import { invoicesRouter } from './routes/invoices'
import { tasksRouter } from './routes/tasks'
import { eventsRouter } from './routes/events'
import { buildOpenApiDocument } from './openapi'

const app = new Hono<{ Bindings: Env }>()

app.use('*', securityHeadersMiddleware)

// CORS — allow docs.zync.is for browser-based API key testing
// OPTIONS preflight must return before auth middleware
app.use('*', cors({
  origin: (origin, c) => {
    if (origin === 'https://docs.zync.is') return origin
    if ((c.env as Env & { ENVIRONMENT?: string }).ENVIRONMENT !== 'production' && origin === 'http://localhost:4321') return origin
    return ''
  },
  allowHeaders: ['Authorization', 'Content-Type', 'Accept'],
  allowMethods: ['GET', 'POST', 'PATCH', 'OPTIONS'],
  exposeHeaders: ['X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset', 'Retry-After'],
  maxAge: 86400,
}))

// Public endpoints — no auth
app.get('/v1/health', (c) => c.json({ ok: true }))

// OpenAPI schema — public, no auth, cached 5 minutes
app.get('/api/openapi.json', (_c) => {
  const doc = buildOpenApiDocument()
  return new Response(JSON.stringify(doc), {
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=300',
    },
  })
})

// Auth + rate-limit middleware for all /v1/* except health
app.use('/v1/*', apiKeyAuth)
app.use('/v1/*', perKeyRateLimit)

// Resource routers
app.route('/v1/customers', customersRouter)
app.route('/v1/invoices', invoicesRouter)
app.route('/v1/tasks', tasksRouter)
app.route('/v1/events', eventsRouter)

// wave-12: OAuth token introspection — GET /api/auth/me
// Returns OAuth token context when called with an OAuth Bearer token (spec requirement).
app.use('/api/auth/me', apiKeyAuth)
app.get('/api/auth/me', (c) => {
  const auth = c.get('apiKey' as never) as import('./middleware/auth').ResolvedAuth | undefined
  if (!auth) return c.json({ error: 'Unauthorized' }, 401)
  if (auth.authKind === 'oauth') {
    return c.json({
      authKind: 'oauth',
      clientId: auth.clientId,
      tenantId: auth.tenantId,
      userId: auth.userId,
      scope: auth.scopes,
    })
  }
  return c.json({
    authKind: 'api_key',
    tenantId: auth.tenantId,
    userId: auth.userId,
    keyId: auth.keyId,
    scope: auth.scopes,
  })
})

// 404 fallback
app.notFound((c) => c.json({ error: 'not_found', message: 'Endpoint not found' }, 404))

export default app
