/**
 * Expense approval routes — expense-approval-workflow (wave-11 leaf-E).
 * Mounted at /api/expenses/approvals (BEFORE /api/expenses/:id).
 *
 * GET  /           → paginated approval queue (expenses:approve)
 * POST /:id/approve → approve expense (expenses:approve)
 * POST /:id/reject  → reject expense (expenses:approve)
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import {
  listPendingExpenseApprovals as listPendingApprovals,
  approveExpense,
  rejectExpense,
  isExpenseApprover,
} from '@zync/db/queries'

const approvalStatusSchema = z.enum(['pending', 'approved', 'rejected', 'not_required'])

const rejectSchema = z.object({
  reason: z.string().min(1).max(1000),
})

const approveSchema = z.object({
  note: z.string().max(1000).optional(),
})

export const expenseApprovalRoutes = new Hono<AppEnv>()

expenseApprovalRoutes.use('*', authMiddleware)

// ── GET /api/expenses/approvals ───────────────────────────────────────────────

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

  const approver = await isExpenseApprover({
    tenantId: session.tid,
    userId: session.sub,
    db: c.get('db'),
  })
  if (!approver) {
    return c.json({ error: 'Not an authorized approver' }, 403)
  }

  const statusParam = c.req.query('status') ?? 'pending'
  const statusResult = approvalStatusSchema.safeParse(statusParam)
  if (!statusResult.success) {
    return c.json({ error: 'Invalid status' }, 400)
  }

  const db = c.get('db')
  const result = await listPendingApprovals({
    tenantId: session.tid,
    status: statusResult.data,
    limit: 50,
    db,
  })

  return c.json({ items: result.items, total: result.total }, 200)
})

// ── POST /api/expenses/approvals/:id/approve ──────────────────────────────────

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

  const approver = await isExpenseApprover({
    tenantId: session.tid,
    userId: session.sub,
    db: c.get('db'),
  })
  if (!approver) {
    return c.json({ error: 'Not an authorized approver' }, 403)
  }

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

  const db = c.get('db')
  try {
    const updated = await approveExpense({
      tenantId: session.tid,
      expenseId: c.req.param('id'),
      approverUserId: session.sub,
      note: parsed.data.note,
      db,
    })
    return c.json(updated, 200)
  } catch (err) {
    return c.json({ error: (err as Error).message }, 409)
  }
})

// ── POST /api/expenses/approvals/:id/reject ───────────────────────────────────

expenseApprovalRoutes.post('/:id/reject', requirePermission('expenses:approve'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const approver = await isExpenseApprover({
    tenantId: session.tid,
    userId: session.sub,
    db: c.get('db'),
  })
  if (!approver) {
    return c.json({ error: 'Not an authorized approver' }, 403)
  }

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

  const db = c.get('db')
  try {
    const updated = await rejectExpense({
      tenantId: session.tid,
      expenseId: c.req.param('id'),
      approverUserId: session.sub,
      reason: parsed.data.reason,
      db,
    })
    return c.json(updated, 200)
  } catch (err) {
    return c.json({ error: (err as Error).message }, 409)
  }
})
