/**
 * Receipts routes — invoice-receipt-document (wave-12, spec 179).
 * Mounted at /api/receipts in apps/zync-api/src/routes/index.ts.
 *
 * Routes:
 *   GET    /                → list receipts (paginated)
 *   GET    /:id             → get receipt with payment lines
 *   GET    /:id/pdf         → render receipt HTML (print-to-PDF)
 *   POST   /:id/void        → void an issued receipt
 */
import { Hono } from 'hono'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requireModuleEnabled } from '../../middleware/require-module-enabled'
import { requirePermission } from '../../middleware/guards'
import {
  listReceipts,
  getReceiptWithLines,
  voidReceipt,
  voidReceiptSchema,
  listReceiptsSchema,
  ReceiptNotFoundError,
  ReceiptConflictError,
} from '@zync/db/queries'
import { renderReceiptHtml } from '../../receipts/render'
import {
  fetchReceiptHtmlSnapshot,
  generateAndStoreReceiptSnapshot,
  loadReceiptRenderContext,
} from '../../lib/receipt-snapshot'

export const receiptRoutes = new Hono<AppEnv>()

receiptRoutes.use('*', authMiddleware)
receiptRoutes.use('*', requireModuleEnabled('invoices'))

// ── GET /api/receipts ─────────────────────────────────────────────────────────

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

  const query = c.req.query()
  const parsed = listReceiptsSchema.safeParse({
    cursor: query.cursor,
    limit: query.limit,
    status: query.status,
    docType: query.docType,
    customer: query.customer,
    dateFrom: query.dateFrom,
    dateTo: query.dateTo,
    invoiceId: query.invoiceId,
  })
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const db = c.get('db')
  const result = await listReceipts(db, session.tid, parsed.data)
  return c.json(result, 200)
})

// ── GET /api/receipts/:id ─────────────────────────────────────────────────────

receiptRoutes.get('/:id', requirePermission('invoices:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')
  const db = c.get('db')
  const receipt = await getReceiptWithLines(db, session.tid, id)
  if (!receipt) {
    return c.json({ error: 'Not found' }, 404)
  }
  return c.json(receipt, 200)
})

// ── GET /api/receipts/:id/pdf ─────────────────────────────────────────────────

receiptRoutes.get('/:id/pdf', requirePermission('invoices:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')
  const db = c.get('db')
  const receipt = await getReceiptWithLines(db, session.tid, id)
  if (!receipt) {
    return c.json({ error: 'Not found' }, 404)
  }

  const htmlResponseHeaders = {
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Disposition': `inline; filename="receipt-${receipt.receiptNumber ?? id}.html"`,
    'Content-Security-Policy':
      "default-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:",
    'X-Content-Type-Options': 'nosniff',
  } as const

  // ISSUED: serve immutable R2 snapshot when available (key stored in pdfR2Key)
  if (receipt.status === 'ISSUED') {
    const r2 = c.env.INVOICE_SNAPSHOTS_BUCKET
    let snapshotKey = receipt.pdfR2Key

    if (snapshotKey) {
      const snapshotHtml = await fetchReceiptHtmlSnapshot(r2, snapshotKey)
      if (snapshotHtml) {
        return new Response(snapshotHtml, { status: 200, headers: htmlResponseHeaders })
      }
    }

    // Backfill missing snapshot (e.g. issued before R2 storage fix)
    if (r2) {
      try {
        const snapshotted = await generateAndStoreReceiptSnapshot(
          { db, env: c.env },
          session.tid,
          id,
        )
        snapshotKey = snapshotted?.pdfR2Key ?? snapshotKey
        if (snapshotKey) {
          const snapshotHtml = await fetchReceiptHtmlSnapshot(r2, snapshotKey)
          if (snapshotHtml) {
            return new Response(snapshotHtml, { status: 200, headers: htmlResponseHeaders })
          }
        }
      } catch (backfillErr) {
        console.error('[receipts/pdf] snapshot backfill failed:', backfillErr)
      }
    }
  }

  // Live render (non-ISSUED, or snapshot unavailable)
  const ctx = await loadReceiptRenderContext(db, c.env, session.tid, receipt)
  const html = renderReceiptHtml(receipt, ctx)

  return new Response(html, {
    status: 200,
    headers: htmlResponseHeaders,
  })
})

// ── POST /api/receipts/:id/void ───────────────────────────────────────────────

receiptRoutes.post('/:id/void', requirePermission('invoices:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

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

  const db = c.get('db')
  try {
    const receipt = await voidReceipt(db, session.tid, session.sub, id, parsed.data.reason)
    return c.json(receipt, 200)
  } catch (err) {
    if (err instanceof ReceiptNotFoundError) {
      return c.json({ error: 'Not found' }, 404)
    }
    if (err instanceof ReceiptConflictError) {
      return c.json({ error: err.message }, 409)
    }
    throw err
  }
})
