/**
 * Bulk invoice generation routes — bulk-invoice-generation (P048).
 * Mounted at /api/invoices/bulk-generate in apps/zync-api/src/index.ts.
 *
 * Routes:
 *   POST /preview        → compute what would be generated (dry run)
 *   POST /               → generate invoices (≤20 inline, >20 via Queue)
 *   GET  /jobs/:jobId    → poll status of a background billing run
 *
 * Auth: invoices:write + admin role (OWNER | ADMIN)
 *
 * Batch rules:
 *   ≤ 20 customers → inline generation, returns { invoices: [...] }
 *   > 20 customers → dispatches bulk_action job, returns { job_id: string }
 */
import { Hono } from 'hono'
import {
  bulkGeneratePreviewSchema,
  bulkGenerateSchema,
  getBulkGeneratePreview,
  createBulkGenerationJob,
  getBulkGenerationJobStatus,
  generateInvoicesForCustomers,
  listScopedCustomers,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'

const INLINE_BATCH_LIMIT = 20

const invoiceGenerateRoutes = new Hono<AppEnv>()

// All routes require auth + invoices:write
invoiceGenerateRoutes.use('*', authMiddleware)
invoiceGenerateRoutes.use('*', requirePermission('invoices:write'))

// Admin-only guard helper
function requireAdminRole(session: { role?: string }) {
  const role = session.role ?? ''
  return ['OWNER', 'ADMIN'].includes(role.toUpperCase())
}

// ── POST /preview — dry run ───────────────────────────────────────────────────

invoiceGenerateRoutes.post('/preview', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireAdminRole(session)) {
    return c.json({ error: 'Forbidden: admin role required' }, 403)
  }

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

  if (parsed.data.periodStart > parsed.data.periodEnd) {
    return c.json({ error: 'period_start must be before period_end' }, 400)
  }

  const db = c.get('db')
  const preview = await getBulkGeneratePreview(db, session.tid, parsed.data)

  return c.json(preview)
})

// ── POST / — generate ─────────────────────────────────────────────────────────

invoiceGenerateRoutes.post('/', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireAdminRole(session)) {
    return c.json({ error: 'Forbidden: admin role required' }, 403)
  }

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

  if (parsed.data.periodStart > parsed.data.periodEnd) {
    return c.json({ error: 'period_start must be before period_end' }, 400)
  }

  const db = c.get('db')
  const { customerIds, periodStart, periodEnd, includeTime, includeExpenses, includeMilestones } = parsed.data

  const scopedCustomers = await listScopedCustomers(db, session.tid, customerIds)
  if (scopedCustomers.length !== customerIds.length) {
    return c.json({ error: 'One or more customer_ids do not belong to the current tenant' }, 400)
  }

  // Large batch path: > 20 customers → Queue dispatch
  if (customerIds.length > INLINE_BATCH_LIMIT) {
    const jobId = await createBulkGenerationJob(db, {
      tenantId: session.tid,
      actorId: session.sub,
      customerIds,
      periodStart,
      periodEnd,
      includeTime,
      includeExpenses,
      includeMilestones,
    })

    // Dispatch to Queue for processing (bulk-operations consumer)
    await c.env.QUEUE.send({
      type: 'bulk_action',
      jobId,
      tenantId: session.tid,
      actorId: session.sub,
      action: 'invoice_generate',
      params: { customerIds, periodStart, periodEnd, includeTime, includeExpenses, includeMilestones },
    })

    return c.json({ job_id: jobId }, 202)
  }

  const generated = await generateInvoicesForCustomers(db, {
    tenantId: session.tid,
    actorId: session.sub,
    customerIds,
    periodStart,
    periodEnd,
    includeTime,
    includeExpenses,
    includeMilestones,
  })

  return c.json({
    invoices: generated.invoices.map(({ id, customerName, number }) => ({ id, customerName, number })),
    skipped: generated.skipped.length,
    skippedDetails: generated.skipped,
    failed: generated.failed,
    created: generated.invoices.length,
  })
})

// ── GET /jobs/:jobId — poll job status ────────────────────────────────────────

invoiceGenerateRoutes.get('/jobs/:jobId', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!requireAdminRole(session)) {
    return c.json({ error: 'Forbidden: admin role required' }, 403)
  }

  const jobId = c.req.param('jobId')
  const db = c.get('db')

  const status = await getBulkGenerationJobStatus(db, session.tid, jobId)
  if (!status) {
    return c.json({ error: 'Job not found' }, 404)
  }

  return c.json(status)
})

export { invoiceGenerateRoutes }
