import { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react';
import toast from 'react-hot-toast';
import { useAuth } from './AuthContext';
import api from '../services/api';

// Lazily created Audio instance — reused across calls to avoid memory churn
let _audio = null;
let _audioUnlocked = false;

function getAudio() {
  if (!_audio) {
    _audio = new Audio('/sounds/notification.mp3');
    _audio.volume = 0.5;
  }
  return _audio;
}

/** Called once on the first user gesture to silently play→pause the element,
 *  which "unlocks" it so later timer-based .play() calls are not blocked. */
function unlockAudio() {
  if (_audioUnlocked) return;
  _audioUnlocked = true;
  const audio = getAudio();
  audio.muted = true;
  audio.play().then(() => {
    audio.pause();
    audio.muted = false;
    audio.currentTime = 0;
  }).catch(() => {
    audio.muted = false;
  });
}

function playNotificationSound() {
  try {
    const audio = getAudio();
    audio.currentTime = 0;
    audio.play().catch(() => {
      // Autoplay still blocked — silent fail
    });
  } catch {
    // Ignore any other errors (e.g. unsupported format)
  }
}

const NotificationContext = createContext();

export function NotificationProvider({ children }) {
  const { user } = useAuth();
  const [notifications, setNotifications] = useState([]);
  const [unreadCount, setUnreadCount] = useState(0);
  const activeUserId = useRef(null);
  // Track seen IDs to detect genuinely new arrivals and show toasts
  const seenIds = useRef(new Set());
  const initialized = useRef(false);

  const fetch = useCallback(async () => {
    const requestUserId = user?.id;
    if (!requestUserId) return;
    try {
      const res = await api.get('/notifications');

      // Ignore stale responses that arrived after logout or account switch.
      if (activeUserId.current !== requestUserId) return;

      const incoming = res.data.notifications || [];
      setNotifications(incoming);
      setUnreadCount(res.data.unreadCount || 0);

      // First fetch: just seed the seen-set without toasting
      if (!initialized.current) {
        incoming.forEach(n => seenIds.current.add(n.id));
        initialized.current = true;
        return;
      }

      // Subsequent fetches: toast new unread notifications
      const newItems = incoming.filter(n => !n.read && !seenIds.current.has(n.id));
      if (newItems.length > 0) {
        playNotificationSound();
      }
      newItems.forEach(n => {
        seenIds.current.add(n.id);
        const icon = notificationIcon(n.type);
        toast(`${icon} ${n.title}`, { duration: 5000 });
        // Browser notification for calendar reminders
        if (n.type === 'calendar_reminder' && 'Notification' in window) {
          const fire = () => new Notification(n.title, { body: n.body || '', icon: '/favicon.ico' });
          if (Notification.permission === 'granted') {
            fire();
          } else if (Notification.permission !== 'denied') {
            Notification.requestPermission().then(p => { if (p === 'granted') fire(); });
          }
        }
      });
    } catch {
      /* silent – server may not be reachable */
    }
  }, [user]);

  // Unlock audio playback on the first real user interaction.
  // Browsers block audio from timer callbacks unless the element was previously
  // "touched" inside a gesture handler — this one-time listener does exactly that.
  useEffect(() => {
    const events = ['click', 'keydown', 'pointerdown'];
    const handler = () => {
      unlockAudio();
      events.forEach(e => window.removeEventListener(e, handler));
    };
    events.forEach(e => window.addEventListener(e, handler, { once: true }));
    return () => events.forEach(e => window.removeEventListener(e, handler));
  }, []);

  useEffect(() => {
    activeUserId.current = user?.id ?? null;
    if (!user) {
      setNotifications([]);
      setUnreadCount(0);
      seenIds.current = new Set();
      initialized.current = false;
      return;
    }
    fetch();
    const interval = setInterval(fetch, 30_000);
    return () => clearInterval(interval);
  }, [user, fetch]);

  const markRead = async (id) => {
    try {
      await api.post(`/notifications/${id}/read`);
      setNotifications(prev =>
        prev.map(n => (n.id === id ? { ...n, read: 1 } : n)),
      );
      setUnreadCount(prev => Math.max(0, prev - 1));
    } catch { /* silent */ }
  };

  const markAllRead = async () => {
    try {
      await api.post('/notifications/read-all');
      setNotifications(prev => prev.map(n => ({ ...n, read: 1 })));
      setUnreadCount(0);
    } catch { /* silent */ }
  };

  const deleteNotification = async (id) => {
    try {
      await api.delete(`/notifications/${id}`);
      setNotifications(prev => {
        const removed = prev.find(n => n.id === id);
        if (removed && !removed.read) setUnreadCount(c => Math.max(0, c - 1));
        return prev.filter(n => n.id !== id);
      });
      seenIds.current.delete(id);
    } catch { /* silent */ }
  };

  const clearAll = async () => {
    try {
      await api.delete('/notifications/all');
      setNotifications([]);
      setUnreadCount(0);
      seenIds.current = new Set();
    } catch { /* silent */ }
  };

  return (
    <NotificationContext.Provider value={{ notifications, unreadCount, markRead, markAllRead, deleteNotification, clearAll, refresh: fetch }}>
      {children}
    </NotificationContext.Provider>
  );
}

export function useNotifications() {
  const ctx = useContext(NotificationContext);
  if (!ctx) throw new Error('useNotifications must be used within NotificationProvider');
  return ctx;
}

function notificationIcon(type) {
  switch (type) {
    case 'room_share_added':       return '🔗';
    case 'room_share_removed':     return '🚫';
    case 'federation_invite_received': return '📩';
    case 'calendar_reminder':      return '🔔';
    default:                       return '🔔';
  }
}
