import { Response } from 'express';
import { prisma } from '../config/database';
import { AuthRequest, ApproverAuthRequest, VisitorCheckpointAuthRequest } from '../middleware/auth';
import type { RecipientType } from '../lib/events';

type Recipient = { type: RecipientType; id: string };

// `getRecipients` returns one or more (type,id) pairs that should match the
// current session. Admin-as-approver gets both OWNER+ownerId (shared workspace
// feed) AND APPROVER+adminId (their personal approval queue), so the bell
// surfaces every notification they're entitled to in a single list.
function buildHandlers(getRecipients: (req: any) => Recipient[]) {
  const buildWhere = (recipients: Recipient[]) =>
    recipients.length === 1
      ? { recipientType: recipients[0].type, recipientId: recipients[0].id }
      : { OR: recipients.map((r) => ({ recipientType: r.type, recipientId: r.id })) };

  async function list(req: any, res: Response) {
    try {
      const recipients = getRecipients(req);
      if (recipients.length === 0) { res.status(401).json({ error: 'Unauthorized' }); return; }
      // Pagination: cursor by createdAt (ISO). `before` excludes the cursor
      // itself so Load-more never re-renders the last row of the previous
      // page. `limit` is clamped 1..100; default 50 matches the bell popup.
      const before = typeof req.query.before === 'string' ? new Date(req.query.before) : null;
      const limitRaw = typeof req.query.limit === 'string' ? Number(req.query.limit) : 50;
      const limit = Math.min(100, Math.max(1, Number.isFinite(limitRaw) ? limitRaw : 50));
      const baseWhere = buildWhere(recipients);
      const where = before && !isNaN(before.getTime())
        ? { ...baseWhere, createdAt: { lt: before } }
        : baseWhere;
      const items = await prisma.notification.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        take: limit + 1, // peek one beyond to know if there's more
      });
      const hasMore = items.length > limit;
      if (hasMore) items.pop();
      const nextCursor = hasMore && items.length > 0 ? items[items.length - 1].createdAt : null;
      // Unread count is global — independent of the page being viewed — so
      // the bell badge stays accurate even when the user is deep-paged.
      const unread = await prisma.notification.count({ where: { ...baseWhere, readAt: null } });
      res.json({ notifications: items, unread, nextCursor, hasMore });
    } catch (e) {
      console.error('list notifications', e);
      res.status(500).json({ error: 'Failed to fetch notifications' });
    }
  }

  async function markRead(req: any, res: Response) {
    try {
      const recipients = getRecipients(req);
      if (recipients.length === 0) { res.status(401).json({ error: 'Unauthorized' }); return; }
      const n = await prisma.notification.findFirst({
        where: { id: req.params.id, ...buildWhere(recipients) },
      });
      if (!n) { res.status(404).json({ error: 'Not found' }); return; }
      if (!n.readAt) await prisma.notification.update({ where: { id: n.id }, data: { readAt: new Date() } });
      res.json({ ok: true });
    } catch (e) {
      console.error('markRead', e);
      res.status(500).json({ error: 'Failed to mark read' });
    }
  }

  async function markAllRead(req: any, res: Response) {
    try {
      const recipients = getRecipients(req);
      if (recipients.length === 0) { res.status(401).json({ error: 'Unauthorized' }); return; }
      await prisma.notification.updateMany({
        where: { ...buildWhere(recipients), readAt: null },
        data: { readAt: new Date() },
      });
      res.json({ ok: true });
    } catch (e) {
      console.error('markAllRead', e);
      res.status(500).json({ error: 'Failed to mark all read' });
    }
  }

  return { list, markRead, markAllRead };
}

export const ownerNotifications = buildHandlers((req: AuthRequest) => {
  // Admin sessions see their personal APPROVER queue only — workspace-
  // owner notifications stay private. Receptionists (sub-admins) ALSO
  // pull their parent admin's APPROVER queue, since the receptionist's
  // job is to act on the parent's behalf.
  if (req.adminId) {
    const out: Recipient[] = [{ type: 'APPROVER', id: req.adminId }];
    if (req.parentAdminId) out.push({ type: 'APPROVER', id: req.parentAdminId });
    return out;
  }
  if (req.ownerId) {
    return [{ type: 'OWNER', id: req.ownerId }];
  }
  return [];
});
export const approverNotifications = buildHandlers((req: ApproverAuthRequest) =>
  req.approverId ? [{ type: 'APPROVER', id: req.approverId }] : [],
);
export const checkpointNotifications = buildHandlers((req: VisitorCheckpointAuthRequest) =>
  req.visitorCheckpointId ? [{ type: 'CHECKPOINT', id: req.visitorCheckpointId }] : [],
);
