/**
 * Invoice payments routes — partial-payment-recording (wave-11).
 * Mounted under the invoice router at /api/invoices/:id/payments.
 *
 * GET    /api/invoices/:id/payments              → list payments + balance
 * POST   /api/invoices/:id/payments              → record a payment
 * DELETE /api/invoices/:id/payments/:paymentId   → reverse a payment
 */
import { Hono } from 'hono'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { bumpFinancialsVersionOnWrite } from '../../middleware/bump-financials-version'
import { requirePermission } from '../../middleware/guards'
import {
  createDb,
  getInvoiceBalance,
  getInvoiceAdapterSettings,
  recordInvoicePayment,
  reverseInvoicePayment,
  recordPaymentUserInputSchema,
  reversePaymentInputSchema,
  ReceiptIssuedError,
} from '@zync/db/queries'

export const invoicePaymentsRoutes = new Hono<AppEnv>()

invoicePaymentsRoutes.use('*', authMiddleware)
invoicePaymentsRoutes.use('*', bumpFinancialsVersionOnWrite)

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

    const invoiceId = c.req.param('id')
    const db = createDb(c.env)
    const balance = await getInvoiceBalance(db, session.tid, invoiceId)
    return c.json(balance, 200)
  },
)

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

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

    const db = createDb(c.env)
    try {
      const result = await recordInvoicePayment(
        db,
        session.tid,
        invoiceId,
        session.sub,
        parsed.data,
      )
      if (result.balance === 0 && c.env.QUEUE) {
        try {
          const settings = await getInvoiceAdapterSettings(db, session.tid)
          if (settings.adapter) {
            await c.env.QUEUE.send({ type: 'invoice.payment_sync', invoiceId, tenantId: session.tid })
          }
        } catch (queueErr) {
          console.error('[invoice-payments] invoice.payment_sync enqueue failed:', queueErr)
        }
      }
      return c.json(result, 201)
    } catch (err) {
      if (err instanceof Error && err.message.includes('not found')) {
        return c.json({ error: 'Invoice not found' }, 404)
      }
      if (err instanceof Error && err.message.startsWith('Cannot record payment')) {
        return c.json({ error: err.message }, 409)
      }
      throw err
    }
  },
)

// DELETE /api/invoices/:id/payments/:paymentId
invoicePaymentsRoutes.delete(
  '/:id/payments/:paymentId',
  requirePermission('invoices:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    // Only OWNER or ADMIN may reverse payments
    const role = session.role
    if (role !== 'OWNER' && role !== 'ADMIN') {
      return c.json({ error: 'Insufficient role — OWNER or ADMIN required' }, 403)
    }

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

    const db = createDb(c.env)
    try {
      const result = await reverseInvoicePayment(
        db,
        session.tid,
        invoiceId,
        paymentId,
        session.sub,
        parsed.data,
      )
      return c.json(result, 200)
    } catch (err) {
      if (err instanceof ReceiptIssuedError) {
        return c.json({ error: err.message }, 422)
      }
      if (err instanceof Error && err.message.includes('not found')) {
        return c.json({ error: 'Not found' }, 404)
      }
      throw err
    }
  },
)
