/**
 * Customer Statement routes — customer-statement (wave-16, spec 183).
 *
 * Mounted in customers/router.ts at /:id/statement (BEFORE /:id broad route).
 *
 * GET  /:id/statement       → JSON CustomerStatement
 * GET  /:id/statement/pdf   → HTML (print-fallback pattern) or application/pdf
 * POST /:id/statement/send  → sends PDF email to customer primary contact
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { verifyJwt } from '@zync/auth'
import type { AppEnv } from '../../types'
import { requirePermission } from '../../middleware/guards'
import { buildCustomerStatement, getCustomerBillingEmail } from '@zync/db/queries'
import { renderStatementPdf, validateStatementPdf } from '../../lib/statement-pdf'
import { formatStatementEmailSubject, formatStatementEmailBody } from '../../lib/statement-email'
import { sendEmail } from '@zync/notifications'

// ── Portal JWT helper ─────────────────────────────────────────────────────────

interface PortalAuth {
  tenantId: string
  customerId: string
}

async function tryPortalAuth(
  c: { req: { header: (k: string) => string | undefined }; env: { JWT_SECRET: string } },
): Promise<PortalAuth | null> {
  const cookie = c.req.header('Cookie') ?? ''
  const match = /portal_session=([^;]+)/.exec(cookie)
  if (!match) return null
  try {
    const payload = await verifyJwt(match[1]!, c.env.JWT_SECRET)
    if (!payload || payload['type'] !== 'portal') return null
    if (typeof payload['tid'] !== 'string' || typeof payload['customerId'] !== 'string') return null
    return { tenantId: payload['tid'] as string, customerId: payload['customerId'] as string }
  } catch {
    return null
  }
}

export const customerStatementRoute = new Hono<AppEnv>()

// ── Query schema ──────────────────────────────────────────────────────────────

const statementQuerySchema = z.object({
  from: z.string().date().optional(),
  to: z.string().date().optional(),
  currency: z.enum(['ILS', 'USD', 'EUR']).optional(),
})

const sendStatementBodySchema = z.object({
  from: z.string().date(),
  to: z.string().date(),
  currency: z.enum(['ILS', 'USD', 'EUR']).optional(),
  message: z.string().max(2000).optional(),
})

function todayIso(): string {
  return new Date().toISOString().slice(0, 10)
}

function oneYearAgoIso(): string {
  const d = new Date()
  d.setFullYear(d.getFullYear() - 1)
  return d.toISOString().slice(0, 10)
}

// ── GET /:id/statement ────────────────────────────────────────────────────────

customerStatementRoute.get(
  '/:id/statement',
  requirePermission('customers:read'),
  requirePermission('invoices:read'),
  async (c) => {
    const session = c.get('session')
    let tenantId: string
    let customerId: string

    if (session && session.type === 'user' && session.tid) {
      tenantId = session.tid
      customerId = c.req.param('id')
    } else {
      const portalAuth = await tryPortalAuth(c)
      if (!portalAuth) {
        return c.json({ error: 'Unauthorized' }, 401)
      }
      tenantId = portalAuth.tenantId
      customerId = portalAuth.customerId
    }

    const parsed = statementQuerySchema.safeParse(c.req.query())
    if (!parsed.success) {
      return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
    }

    const { from = oneYearAgoIso(), to = todayIso(), currency } = parsed.data
    const db = c.get('db')

    const statement = await buildCustomerStatement(db, {
      tenantId,
      customerId,
      from,
      to,
      currency,
    })

    if (!statement) {
      return c.json({ error: 'Customer not found' }, 404)
    }

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

// ── GET /:id/statement/pdf ─────────────────────────────────────────────────────

customerStatementRoute.get(
  '/:id/statement/pdf',
  requirePermission('customers:read'),
  requirePermission('invoices:read'),
  async (c) => {
    const session = c.get('session')
    let tenantId: string
    let customerId: string

    if (session && session.type === 'user' && session.tid) {
      tenantId = session.tid
      customerId = c.req.param('id')
    } else {
      const portalAuth = await tryPortalAuth(c)
      if (!portalAuth) {
        return c.json({ error: 'Unauthorized' }, 401)
      }
      tenantId = portalAuth.tenantId
      customerId = portalAuth.customerId
    }

    const parsed = statementQuerySchema.safeParse(c.req.query())
    if (!parsed.success) {
      return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
    }

    const { from = oneYearAgoIso(), to = todayIso(), currency } = parsed.data
    const db = c.get('db')

    const statement = await buildCustomerStatement(db, {
      tenantId,
      customerId,
      from,
      to,
      currency,
    })

    if (!statement) {
      return c.json({ error: 'Customer not found' }, 404)
    }

    let pdf: Uint8Array
    try {
      pdf = await renderStatementPdf(statement)
      validateStatementPdf(pdf)
    } catch (error) {
      const typedError = error && typeof error === 'object'
        ? error as { code?: unknown; upstreamStatus?: unknown }
        : {}
      console.error('statement_pdf_generation_failed', {
        customerId,
        tenantId,
        reason: typeof typedError.code === 'string' ? typedError.code : 'STATEMENT_PDF_UNKNOWN_ERROR',
        upstreamStatus: typeof typedError.upstreamStatus === 'number' ? typedError.upstreamStatus : undefined,
      })
      return c.json({ error: 'PDF generation failed', code: 'STATEMENT_PDF_GENERATION_FAILED' }, 502)
    }
    return new Response(pdf, {
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': `attachment; filename="statement-${customerId}.pdf"`,
        'Cache-Control': 'no-store',
      },
    })
  },
)

// ── POST /:id/statement/send ──────────────────────────────────────────────────

customerStatementRoute.post(
  '/:id/statement/send',
  requirePermission('customers:read'),
  requirePermission('customers:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      // Check if portal session — portal users cannot send statements
      const portalAuth = await tryPortalAuth(c)
      if (portalAuth) {
        return c.json({ error: 'Forbidden: portal users cannot send statements' }, 403)
      }
      return c.json({ error: 'Unauthorized' }, 401)
    }
    const tenantId = session.tid
    const customerId = c.req.param('id')

    const body = await c.req.json().catch(() => null)
    const parsed = sendStatementBodySchema.safeParse(body)
    if (!parsed.success) {
      return c.json({ error: 'Invalid request body', details: parsed.error.flatten() }, 400)
    }

    const { from, to, currency, message } = parsed.data
    const db = c.get('db')

    // Build statement (also verifies customer belongs to tenant)
    const statement = await buildCustomerStatement(db, {
      tenantId,
      customerId,
      from,
      to,
      currency,
    })

    if (!statement) {
      return c.json({ error: 'Customer not found' }, 404)
    }

    // Resolve billing email: primary contact first, then customers.email
    const billingEmail = await getCustomerBillingEmail(db, tenantId, customerId)

    if (!billingEmail) {
      return c.json({ error: 'No billing email for this customer', code: 'NO_BILLING_EMAIL' }, 422)
    }

    const locale: 'he' | 'en' = 'he'
    const subject = formatStatementEmailSubject(statement.customerName, from, to, locale)
    const emailBody = formatStatementEmailBody(statement.customerName, from, to, message, locale)

    await sendEmail(
      {
        to: billingEmail,
        templateKey: 'invoice-sent',
        vars: {
          subject,
          title: subject,
          body: emailBody,
          actionButtons: '',
        },
        locale: locale === 'he' ? 'he-IL' : 'en-US',
      },
      c.env,
    )

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