/**
 * Payment link invoice routes — payment-gateway-adapters (wave-10 leaf 5)
 *   + invoice-payment-link-generation (wave-12).
 * Mounted at /api/invoices (sub-routes of the invoices router).
 *
 * POST /:id/payment-link       → generate gateway-hosted checkout link (wave-10)
 * GET  /:id/payment-link       → get stateless HMAC token link status (wave-12)
 * POST /:id/payment-link/send  → email payment link to customer (wave-12)
 *
 * Guarded by authMiddleware + invoices:read|write permission.
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { requirePermission } from '../../middleware/guards'
import {
  createDb,
  getInvoiceWithLines,
  getPaymentGatewayConfig,
  logAuditEvent,
} from '@zync/db/queries'
import { generatePaymentLink as cardcomLink } from '../../integrations/payment-gateways/cardcom'
import { generatePaymentLink as payplusLink } from '../../integrations/payment-gateways/payplus'
import { generatePaymentLink as stripeLink } from '../../integrations/payment-gateways/stripe'
import { signInvoicePaymentToken, buildPaymentLinkUrl } from '@zync/payments'
import {
  paymentLinkStatusForInvoice,
  tenantHasActiveGateway,
  resolveCustomerPrimaryEmail,
} from '../../services/payment-link'
import { sendEmail } from '@zync/notifications'
import { stampPaymentLinkSentAt, getPaymentLinkEmailTemplate } from '@zync/db/queries'

// ── Zod schema for send body ───────────────────────────────────────────────────

export const sendPaymentLinkSchema = z.object({
  to: z.array(z.string().email()).min(1).optional(),
  subject: z.string().max(255).optional(),
  message: z.string().max(5000).optional(),
})

export type SendPaymentLinkInput = z.infer<typeof sendPaymentLinkSchema>

// ── Response type ──────────────────────────────────────────────────────────────

export interface PaymentLinkResponse {
  url: string
  token: string
  status: 'active' | 'inactive'
  reason?: string
}

// ── Router ─────────────────────────────────────────────────────────────────────

export const paymentLinkInvoiceRoutes = new Hono<AppEnv>()

// POST /api/invoices/:id/payment-link
paymentLinkInvoiceRoutes.post(
  '/:id/payment-link',
  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 = c.get('db') ?? createDb(c.env)

    // Check gateway configured
    const gatewayConfig = await getPaymentGatewayConfig(db, session.tid)
    if (!gatewayConfig) {
      return c.json(
        { error: 'Payment gateway not configured. Go to Settings → Payment Gateway.' },
        422,
      )
    }
    if (!gatewayConfig.isActive) {
      return c.json({ error: 'Payment gateway is not active.' }, 422)
    }

    // Load invoice
    const invoice = await getInvoiceWithLines(db, session.tid, invoiceId)
    if (!invoice) {
      return c.json({ error: 'Invoice not found' }, 404)
    }

    // Build common invoice payload
    const invoicePayload = {
      id: invoice.id,
      invoiceNumber: invoice.invoiceNumber,
      amount: invoice.total,
      currency: invoice.currency,
      customerName: invoice.customerId ?? '', // customer UUID — gateways use this as reference
      description: `Invoice ${invoice.invoiceNumber ?? invoice.id}`,
    }

    let result: { url: string; expiresAt: string | null }

    try {
      switch (gatewayConfig.gateway) {
        case 'cardcom': {
          const cfg = gatewayConfig.config as { terminalNumber: string; apiName: string; apiPassword: string }
          result = await cardcomLink(
            { terminalNumber: cfg.terminalNumber, apiName: cfg.apiName, apiPassword: cfg.apiPassword },
            invoicePayload,
          )
          break
        }
        case 'payplus': {
          const cfg = gatewayConfig.config as { apiKey: string; secretKey: string }
          result = await payplusLink(
            { apiKey: cfg.apiKey, secretKey: cfg.secretKey },
            invoicePayload,
          )
          break
        }
        case 'stripe': {
          const cfg = gatewayConfig.config as { secretKey: string }
          result = await stripeLink(
            { secretKey: cfg.secretKey },
            invoicePayload,
          )
          break
        }
        default:
          return c.json({ error: 'Unknown gateway' }, 422)
      }
    } catch (err) {
      return c.json({ error: err instanceof Error ? err.message : 'Gateway error' }, 502)
    }

    await logAuditEvent(c, {
      tenantId: session.tid,
      userId: session.sub,
      entityType: 'invoice',
      entityId: invoiceId,
      eventType: 'payment_link.generated',
      metadata: { gateway: gatewayConfig.gateway },
    })

    return c.json({ url: result.url, expiresAt: result.expiresAt }, 200)
  },
)

// ── GET /api/invoices/:id/payment-link ─────────────────────────────────────────
// Returns stateless HMAC token + link status. Does NOT call a gateway.

paymentLinkInvoiceRoutes.get(
  '/:id/payment-link',
  requirePermission('invoices:read'),
  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 = c.get('db') ?? createDb(c.env)

    const invoice = await getInvoiceWithLines(db, session.tid, invoiceId)
    if (!invoice) {
      return c.json({ error: 'Not found' }, 404)
    }

    // Derive status from invoice state
    let statusResult = paymentLinkStatusForInvoice(invoice.status)

    // Override if no active gateway
    const hasGateway = await tenantHasActiveGateway(db, session.tid)
    if (!hasGateway) {
      statusResult = { state: 'inactive', reason: 'No payment gateway configured' }
    }

    const secret = c.env.INVOICE_PAYMENT_LINK_KEY
    const token = await signInvoicePaymentToken(invoice.id, session.tid, secret)
    const baseUrl = c.env.APP_BASE_URL ?? 'https://app.zync.is'
    const url = buildPaymentLinkUrl(token, baseUrl)

    const response: PaymentLinkResponse = {
      url,
      token,
      status: statusResult.state,
      ...(statusResult.reason ? { reason: statusResult.reason } : {}),
    }

    return c.json(response, 200)
  },
)

// ── POST /api/invoices/:id/payment-link/send ───────────────────────────────────
// Emails the payment link to the customer. Stamps payment_link_sent_at.

paymentLinkInvoiceRoutes.post(
  '/:id/payment-link/send',
  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 = c.get('db') ?? createDb(c.env)

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

    // Load invoice (tenant-scoped)
    const invoice = await getInvoiceWithLines(db, session.tid, invoiceId)
    if (!invoice) {
      return c.json({ error: 'Not found' }, 404)
    }

    // Reject if link is not active
    const statusResult = paymentLinkStatusForInvoice(invoice.status)
    if (statusResult.state !== 'active') {
      return c.json(
        { error: statusResult.reason ?? 'Payment link is not active for this invoice' },
        409,
      )
    }

    // Reject if no active gateway
    const hasGateway = await tenantHasActiveGateway(db, session.tid)
    if (!hasGateway) {
      return c.json({ error: 'No payment gateway configured' }, 409)
    }

    // Resolve recipients
    let recipients = body.to && body.to.length > 0 ? body.to : undefined
    if (!recipients && invoice.customerId) {
      const primaryEmail = await resolveCustomerPrimaryEmail(db, session.tid, invoice.customerId)
      if (primaryEmail) {
        recipients = [primaryEmail]
      }
    }
    if (!recipients || recipients.length === 0) {
      return c.json({ error: 'No recipient email could be resolved' }, 422)
    }

    // Sign token + build URL
    const secret = c.env.INVOICE_PAYMENT_LINK_KEY
    const token = await signInvoicePaymentToken(invoice.id, session.tid, secret)
    const baseUrl = c.env.APP_BASE_URL ?? 'https://app.zync.is'
    const paymentUrl = buildPaymentLinkUrl(token, baseUrl)

    // Load email template (fall back to defaults if not configured)
    const tpl = await getPaymentLinkEmailTemplate(db, session.tid)

    const invoiceRef = invoice.invoiceNumber ?? invoice.proformaNumber ?? invoice.id
    const defaultSubject = `Invoice ${invoiceRef} — payment link`
    const defaultMessage = `Your invoice ${invoiceRef} is ready for payment.\n\nPay securely here: {{payment_link}}\n\nThank you.`

    const emailSubject = body.subject ?? tpl?.subject ?? defaultSubject
    const rawMessage = body.message ?? tpl?.bodyText ?? defaultMessage

    // Interpolate {{payment_link}} and {{business_name}}
    const emailBody = rawMessage
      .replace(/\{\{payment_link\}\}/g, paymentUrl)
      .replace(/\{business_name\}/g, session.tid)

    // Send to each recipient
    for (const to of recipients) {
      await sendEmail(
        {
          to,
          templateKey: 'payment_link',
          locale: 'he-IL' as const,
          vars: {
            subject: emailSubject,
            title: emailSubject,
            body: emailBody,
            payment_link: paymentUrl,
          },
        },
        c.env,
      )
    }

    // Stamp payment_link_sent_at in the DB
    await stampPaymentLinkSentAt(db, session.tid, invoiceId)

    await logAuditEvent(c, {
      tenantId: session.tid,
      userId: session.sub,
      entityType: 'invoice',
      entityId: invoiceId,
      eventType: 'payment_link.sent',
      metadata: { recipientCount: recipients.length },
    })

    return c.json({ sent: true }, 200)
  },
)
