/**
 * Invoice email history route — invoice-email-history (wave-13, spec 159).
 *
 * GET /:id/email-history → grouped email event history for a single invoice.
 *
 * Groups events by send occurrence: one InvoiceEmailSendGroup per 'sent' event
 * (ordered newest first). Within each group, per-recipient event rows sorted
 * chronologically.
 */
import { Hono } from 'hono'
import type { AppEnv } from '../../types'
import { requirePermission } from '../../middleware/guards'
import { getInvoice, listEmailEventsByInvoice, getLatestRecipientStatus } from '@zync/db/queries'
import type { InvoiceEmailEventType, InvoiceEmailEventWithSender } from '@zync/db/queries'

// ── Response types ─────────────────────────────────────────────────────────────

export interface InvoiceEmailEventDetail {
  id: string
  eventType: InvoiceEmailEventType
  occurredAt: string
  metadata: Record<string, unknown> | null
}

export interface InvoiceEmailRecipientGroup {
  toAddress: string
  events: InvoiceEmailEventDetail[]
}

export interface InvoiceEmailSendGroup {
  sentEventId: string
  occurredAt: string // the 'sent' event occurredAt
  sentByName: string | null
  recipients: InvoiceEmailRecipientGroup[]
}

export interface EmailHistoryResponse {
  groups: InvoiceEmailSendGroup[]
}

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

export const emailHistoryRoutes = new Hono<AppEnv>()

emailHistoryRoutes.get(
  '/:id/email-history',
  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')

    // Verify invoice belongs to tenant (404 on wrong tenant)
    const invoice = await getInvoice(db, session.tid, invoiceId)
    if (!invoice) {
      return c.json({ error: 'Not found' }, 404)
    }

    const events = await listEmailEventsByInvoice(db, {
      tenantId: session.tid,
      invoiceId,
    })

    const groups = buildSendGroups(events)

    return c.json<EmailHistoryResponse>({ groups }, 200)
  },
)

// ── Bulk email status ──────────────────────────────────────────────────────────

/**
 * GET /api/invoices/email-status?ids=uuid1,uuid2,...
 *
 * Returns the best email delivery status per invoice id.
 * Used by the invoice list page to display a bounce badge.
 * Response: { statuses: Record<string, InvoiceEmailEventType> }
 */
emailHistoryRoutes.get(
  '/email-status',
  requirePermission('invoices:read'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const idsParam = c.req.query('ids') ?? ''
    const invoiceIds = idsParam
      .split(',')
      .map((s) => s.trim())
      .filter(Boolean)
      .slice(0, 100) // cap to prevent abuse

    if (invoiceIds.length === 0) {
      return c.json({ statuses: {} }, 200)
    }

    const db = c.get('db')
    const statusMap = await getLatestRecipientStatus(db, {
      tenantId: session.tid,
      invoiceIds,
    })

    // Convert Map to plain object for JSON serialization
    const statuses: Record<string, InvoiceEmailEventType> = {}
    for (const [id, status] of statusMap.entries()) {
      statuses[id] = status
    }

    return c.json({ statuses }, 200)
  },
)

// ── Group builder ──────────────────────────────────────────────────────────────

function buildSendGroups(events: InvoiceEmailEventWithSender[]): InvoiceEmailSendGroup[] {
  // Identify all 'sent' events — each is a send occurrence
  const sentEvents = events.filter((e) => e.eventType === 'sent')

  if (sentEvents.length === 0) return []

  // Sort sent events newest first for the outer array
  const sortedSentEvents = [...sentEvents].sort(
    (a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime(),
  )

  // For each sent event, collect related non-sent events by recipient
  // Strategy: non-sent events belong to the nearest preceding sent event
  // for the same (invoice, address). Fall back to most recent sent batch.
  const groups: InvoiceEmailSendGroup[] = sortedSentEvents.map((sentEvent, groupIdx) => {
    // All 'sent' events for this occurrence (same timestamp cluster — within 2s)
    const sentBatchMs = new Date(sentEvent.occurredAt).getTime()

    // Collect recipients for this send occurrence (all 'sent' events in the same batch)
    // "same batch" = within 2 seconds of this sent event (or same sentBy + within same second)
    const batchAddresses = sentEvents
      .filter((e) => Math.abs(new Date(e.occurredAt).getTime() - sentBatchMs) < 2000)
      .map((e) => e.toAddress)

    // For each address in this batch, gather its subsequent events up to the next sent batch
    const nextSentBatchMs =
      groupIdx < sortedSentEvents.length - 1
        ? new Date(sortedSentEvents[groupIdx + 1]!.occurredAt).getTime()
        : 0

    const recipients: InvoiceEmailRecipientGroup[] = batchAddresses.map((addr) => {
      // Non-sent events for this address after this sent and before the next sent batch
      const addrEvents = events
        .filter((e) => {
          if (e.toAddress !== addr) return false
          const t = new Date(e.occurredAt).getTime()
          if (t < sentBatchMs - 2000) return false
          if (nextSentBatchMs > 0 && t < nextSentBatchMs + 2000) {
            // belongs to a later batch (skip for this group)
            if (e.eventType !== 'sent' && t > sentBatchMs + 2000) return false
          }
          return true
        })
        .sort((a, b) => new Date(a.occurredAt).getTime() - new Date(b.occurredAt).getTime())

      const eventDetails: InvoiceEmailEventDetail[] = addrEvents.map((e) => ({
        id: e.id,
        eventType: e.eventType as InvoiceEmailEventType,
        occurredAt: e.occurredAt,
        metadata: e.metadata,
      }))

      return { toAddress: addr, events: eventDetails }
    })

    return {
      sentEventId: sentEvent.id,
      occurredAt: sentEvent.occurredAt,
      sentByName: sentEvent.sentByName ?? null,
      recipients,
    }
  })

  return groups
}
