/**
 * Financial report routes — reports-analytics (wave B).
 *
 * GET /api/reports/revenue          → Revenue Ledger (פנקס הכנסות)
 * GET /api/reports/revenue/xlsx     → Revenue Ledger Excel
 * GET /api/reports/invoices         → Invoice Report
 * GET /api/reports/invoices/xlsx    → Invoice Report Excel
 * GET /api/reports/payments         → Payment Report
 * GET /api/reports/payments/xlsx    → Payment Report Excel
 *
 * Guards: authMiddleware; JSON routes require reports:read; xlsx routes require reports:export
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import {
  getRevenueLedger,
  getInvoiceReport,
  getPaymentReport,
  logAuditEvent,
} from '@zync/db/queries'
import {
  buildRevenueLedgerXlsx,
  buildInvoiceReportXlsx,
  buildPaymentReportXlsx,
} from '../../lib/financial-reports-xlsx'
import { withReportCache } from '../../lib/report-cache'

const dateRangeSchema = z.object({
  from: z.string().date(),
  to: z.string().date(),
})

function xlsxResponse(buffer: Uint8Array, filename: string): Response {
  return new Response(buffer, {
    status: 200,
    headers: {
      'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'Content-Disposition': `attachment; filename="${filename}"`,
    },
  })
}

export const financialReportRoutes = new Hono<AppEnv>()

financialReportRoutes.use('*', authMiddleware)

// ── Revenue Ledger ─────────────────────────────────────────────────────────────

const invoiceStatusValues = [
  'DRAFT', 'SENT', 'APPROVED', 'REJECTED', 'TAX_ISSUED', 'PAID', 'PARTIALLY_PAID',
  'VOID', 'WRITTEN_OFF', 'BAD_DEBT',
] as const

const revenueQuerySchema = dateRangeSchema.extend({
  customer: z.string().uuid().optional(),
  status: z.enum(invoiceStatusValues).optional(),
})

financialReportRoutes.get('/revenue/xlsx', requirePermission('reports:export'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = revenueQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, status } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const report = await getRevenueLedger(db, session.tid, {
    from,
    to,
    customerId: customer,
    status,
  })

  void logAuditEvent(c, {
    tenantId: session.tid,
    userId: session.sub,
    eventType: 'report.export',
    entityType: 'revenue_ledger',
    entityLabel: `${from}_${to}`,
    metadata: { from, to },
  })

  const buffer = await buildRevenueLedgerXlsx(report)
  return xlsxResponse(buffer, `revenue-ledger-${from}_${to}.xlsx`)
})

financialReportRoutes.get('/revenue', requirePermission('reports:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = revenueQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, status } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const tenantId = session.tid
  const period = `${from}_${to}_${customer ?? ''}_${status ?? ''}`
  return withReportCache(c.env, tenantId, 'revenue_ledger', period, () =>
    getRevenueLedger(db, tenantId, {
      from,
      to,
      customerId: customer,
      status,
    }),
  )
})

// ── Invoice Report ─────────────────────────────────────────────────────────────

const invoiceQuerySchema = dateRangeSchema.extend({
  customer: z.string().uuid().optional(),
  status: z.enum(invoiceStatusValues).optional(),
  overdue: z
    .enum(['true', 'false'])
    .optional()
    .transform((v) => (v === 'true' ? true : v === 'false' ? false : undefined)),
})

financialReportRoutes.get('/invoices/xlsx', requirePermission('reports:export'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = invoiceQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, status, overdue } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const report = await getInvoiceReport(db, session.tid, {
    from,
    to,
    customerId: customer,
    status,
    overdue,
  })

  void logAuditEvent(c, {
    tenantId: session.tid,
    userId: session.sub,
    eventType: 'report.export',
    entityType: 'invoice_report',
    entityLabel: `${from}_${to}`,
    metadata: { from, to },
  })

  const buffer = await buildInvoiceReportXlsx(report)
  return xlsxResponse(buffer, `invoice-report-${from}_${to}.xlsx`)
})

financialReportRoutes.get('/invoices', requirePermission('reports:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = invoiceQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, status, overdue } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const tenantId = session.tid
  const period = `${from}_${to}_${customer ?? ''}_${status ?? ''}_${overdue ?? ''}`
  return withReportCache(c.env, tenantId, 'invoice_report', period, () =>
    getInvoiceReport(db, tenantId, {
      from,
      to,
      customerId: customer,
      status,
      overdue,
    }),
  )
})

// ── Payment Report ─────────────────────────────────────────────────────────────

const paymentSourceValues = ['manual', 'gateway', 'bank_transfer', 'auto_billing'] as const

const paymentQuerySchema = dateRangeSchema.extend({
  customer: z.string().uuid().optional(),
  source: z.enum(paymentSourceValues).optional(),
  receipt: z
    .enum(['yes', 'no'])
    .optional()
    .transform((v) =>
      v === 'yes' ? true : v === 'no' ? false : undefined,
    ),
})

financialReportRoutes.get('/payments/xlsx', requirePermission('reports:export'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = paymentQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, source, receipt } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const report = await getPaymentReport(db, session.tid, {
    from,
    to,
    customerId: customer,
    source,
    receiptIssued: receipt,
  })

  void logAuditEvent(c, {
    tenantId: session.tid,
    userId: session.sub,
    eventType: 'report.export',
    entityType: 'payment_report',
    entityLabel: `${from}_${to}`,
    metadata: { from, to },
  })

  const buffer = await buildPaymentReportXlsx(report)
  return xlsxResponse(buffer, `payment-report-${from}_${to}.xlsx`)
})

financialReportRoutes.get('/payments', requirePermission('reports:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'No tenant context' }, 403)
  }

  const parsed = paymentQuerySchema.safeParse(c.req.query())
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }
  const { from, to, customer, source, receipt } = parsed.data
  if (to < from) {
    return c.json({ error: "'to' must be >= 'from'" }, 400)
  }

  const db = c.get('db')
  const tenantId = session.tid
  const period = `${from}_${to}_${customer ?? ''}_${source ?? ''}_${receipt ?? ''}`
  return withReportCache(c.env, tenantId, 'payment_report', period, () =>
    getPaymentReport(db, tenantId, {
      from,
      to,
      customerId: customer,
      source,
      receiptIssued: receipt,
    }),
  )
})
