/**
 * In-app notifications routes — system-communications-notifications (Task 12)
 *                               + notification-center (Task 2).
 *
 * GET   /api/notifications           → { unread, read } (read capped at 20)
 * GET   /api/notifications/all       → paginated full history with filters
 * POST  /api/notifications/read-all  → 200
 * PATCH /api/notifications/:id/read  → 200
 *
 * All routes require session auth and are tenant-isolated.
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../types'
import { authMiddleware } from '../middleware/auth'
import {
  createDb,
  getNotificationsForUser,
  markAllNotificationsRead,
  markNotificationRead,
  getNotificationsAll,
} from '@zync/db/queries'
import { typeGroupMap, renderNotificationText } from './notifications.render'

// ── Zod schemas ────────────────────────────────────────────────────────────────

const notificationsAllQuerySchema = z.object({
  cursor: z.string().optional(),
  limit: z.coerce.number().int().min(1).max(50).default(50),
  type: z.enum(['tasks', 'invoices', 'tickets', 'mentions', 'system']).optional(),
  status: z.literal('unread').optional(),
})

const notificationsRouter = new Hono<AppEnv>()

// All notifications routes require auth
notificationsRouter.use('*', authMiddleware)

// GET /api/notifications/all — paginated full history (notification-center)
notificationsRouter.get('/all', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user') {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  if (!session.tid) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  // Validate query params
  const rawQuery = {
    cursor: c.req.query('cursor'),
    limit: c.req.query('limit'),
    type: c.req.query('type'),
    status: c.req.query('status'),
  }
  const parsed = notificationsAllQuerySchema.safeParse(rawQuery)
  if (!parsed.success) {
    return c.json({ error: 'Bad Request', details: parsed.error.flatten() }, 400)
  }

  const { cursor, limit, type, status } = parsed.data

  // Resolve type-group filter to concrete NotificationType values
  const typeFilter: string[] | null = type ? (typeGroupMap[type] ?? null) : null

  // Decode cursor: base64(created_at_iso + '|' + id)
  let cursorTs: Date | null = null
  let cursorId: string | null = null
  if (cursor) {
    try {
      const decoded = atob(cursor)
      const sepIdx = decoded.lastIndexOf('|')
      if (sepIdx === -1) throw new Error('no separator')
      const tsStr = decoded.slice(0, sepIdx)
      const idStr = decoded.slice(sepIdx + 1)
      const ts = new Date(tsStr)
      if (isNaN(ts.getTime())) throw new Error('bad date')
      // Basic UUID shape check
      if (!/^[0-9a-f-]{36}$/i.test(idStr)) throw new Error('bad uuid')
      cursorTs = ts
      cursorId = idStr
    } catch {
      return c.json({ error: 'Invalid cursor' }, 400)
    }
  }

  const db = createDb(c.env)
  const result = await getNotificationsAll(db, {
    tenantId: session.tid,
    userId: session.sub,
    typeFilter,
    unreadOnly: status === 'unread',
    cursorTs,
    cursorId,
    limit,
  })

  // Locale: always 'he-IL' (session has no locale field; per-user locale not yet in JWT)
  const locale: 'he-IL' | 'en-US' = 'he-IL'

  // Encode nextCursor from last kept row
  let nextCursor: string | null = null
  if (result.hasNextPage && result.rows.length > 0) {
    const last = result.rows[result.rows.length - 1]!
    const cursorPayload = `${last.createdAt.toISOString()}|${last.id}`
    nextCursor = btoa(cursorPayload)
  }

  // Render i18n keys to display strings; never expose titleKey/bodyKey/params
  const data = result.rows.map((row) => {
    const { title, body } = renderNotificationText(
      row.titleKey,
      row.bodyKey,
      row.params as Record<string, string>,
      locale,
    )
    return {
      id: row.id,
      type: row.type,
      title,
      body,
      entity_type: row.entityType,
      entity_id: row.entityId,
      read_at: row.readAt ? row.readAt.toISOString() : null,
      created_at: row.createdAt.toISOString(),
    }
  })

  return c.json({
    data,
    meta: {
      nextCursor,
      total: result.total,
      unreadCount: result.unreadCount,
    },
  })
})

// GET /api/notifications
notificationsRouter.get('/', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const { unread, read } = await getNotificationsForUser(db, session.tid, session.sub)

  return c.json({ unread, read })
})

// POST /api/notifications/read-all
notificationsRouter.post('/read-all', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  await markAllNotificationsRead(db, session.tid, session.sub)

  return c.json({ status: 'ok' })
})

const readParamSchema = z.object({
  id: z.string().uuid(),
})

// PATCH /api/notifications/:id/read
notificationsRouter.patch('/:id/read', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const parsed = readParamSchema.safeParse({ id: c.req.param('id') })
  if (!parsed.success) {
    return c.json({ error: 'Invalid notification ID' }, 400)
  }

  const db = createDb(c.env)
  await markNotificationRead(db, session.tid, session.sub, parsed.data.id)

  return c.json({ status: 'ok' })
})

export { notificationsRouter }
