/**
 * Notifications store — bell-icon inbox + unread badge.
 *
 * Pulls from /api/notifications (read + unread) and /api/notifications/
 * unread-count. Subscribes to the realtime store so a `message:new` or
 * `announcement:new` event refetches the count immediately. Mutations
 * (mark-as-read / read-all) are optimistic so the badge drops the
 * instant the user clicks.
 */
import { create } from 'zustand';
import api from './api';
import { useRealtimeStore } from './realtime';

export interface NotificationRow {
  id: number;
  uid: string;
  type: 'in_app' | 'email' | 'sms';
  subject: string | null;
  body: string | null;
  related_resource: string | null;
  status: 'pending' | 'sent' | 'failed' | 'read';
  metadata: any;
  created_at: string;
  read_at: string | null;
}

interface NotificationsState {
  items: NotificationRow[];
  unread: number;
  loading: boolean;
  inboxOpen: boolean;
  fetchInbox: () => Promise<void>;
  fetchUnreadCount: () => Promise<void>;
  markRead: (id: number) => Promise<void>;
  markAllRead: () => Promise<void>;
  setInboxOpen: (open: boolean) => void;
  /** Internal — wired by AppLayout to react to realtime message events. */
  _bumpOnRealtime: (event: { type: string }) => void;
}

let realtimeUnsub: (() => void) | null = null;

export const useNotificationsStore = create<NotificationsState>((set, get) => ({
  items: [],
  unread: 0,
  loading: false,
  inboxOpen: false,

  setInboxOpen: (open) => set({ inboxOpen: open }),

  fetchInbox: async () => {
    set({ loading: true });
    try {
      const res = await api.get('/notifications?unread=true');
      const raw: NotificationRow[] = res.data || [];
      const unreadItems = raw.filter((n) => n.status !== 'read');
      set({ items: unreadItems, unread: unreadItems.length });
    } catch (err) {
      console.warn('[notifications] inbox fetch failed', err);
    } finally {
      set({ loading: false });
    }
  },

  fetchUnreadCount: async () => {
    try {
      const res = await api.get('/notifications/unread-count');
      set({ unread: res.data?.count ?? 0 });
    } catch (err) {
      console.warn('[notifications] count fetch failed', err);
    }
  },

  markRead: async (id: number) => {
    const before = get().items;
    const beforeUnread = get().unread;
    const target = before.find((n) => n.id === id);
    // Optimistically remove the read item from active notification bell items
    set({
      items: before.filter((n) => n.id !== id),
      unread: Math.max(0, beforeUnread - (target && target.status !== 'read' ? 1 : 0)),
    });
    try {
      await api.post(`/notifications/${id}/read`);
    } catch (err) {
      // Roll back on failure
      set({ items: before, unread: beforeUnread });
      console.warn('[notifications] mark read failed', err);
    }
  },

  markAllRead: async () => {
    const before = get().items;
    const beforeUnread = get().unread;
    set({
      items: [],
      unread: 0,
    });
    try {
      await api.post('/notifications/read-all');
    } catch (err) {
      set({ items: before, unread: beforeUnread });
      console.warn('[notifications] mark-all-read failed', err);
    }
  },

  _bumpOnRealtime: (event) => {
    if (event.type === 'message:new' || event.type === 'announcement:new' || event.type === 'notification:new') {
      get().fetchUnreadCount();
      // Only refetch the full inbox when it's open so we don't double the
      // network traffic while the dropdown is closed.
      if (get().inboxOpen) get().fetchInbox();
    }
  },
}));

/**
 * Wire once at app startup. Returns an unsubscribe function. Safe to
 * call multiple times — the previous subscription is dropped first.
 */
export function attachNotificationsRealtime() {
  if (realtimeUnsub) {
    realtimeUnsub();
    realtimeUnsub = null;
  }
  const handler = (payload: any) => {
    if (!payload || typeof payload.type !== 'string') return;
    useNotificationsStore.getState()._bumpOnRealtime({ type: payload.type });
  };
  const sub1 = useRealtimeStore.getState().subscribe('message:new', handler);
  const sub2 = useRealtimeStore.getState().subscribe('announcement:new', handler);
  const sub3 = useRealtimeStore.getState().subscribe('notification:new', handler);

  realtimeUnsub = () => {
    sub1();
    sub2();
    sub3();
  };

  return () => {
    realtimeUnsub?.();
    realtimeUnsub = null;
  };
}