import React, { useState, useEffect, useCallback, useRef } from 'react';
import {
  Badge,
  Box,
  Button,
  CircularProgress,
  Divider,
  IconButton,
  List,
  ListItem,
  ListItemButton,
  ListItemText,
  Popover,
  Tooltip,
  Typography,
} from '@mui/material';
import {
  Notifications as BellIcon,
  NotificationsNone as BellEmptyIcon,
  Circle as CircleIcon,
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import { notificationsApi } from '../../services/notifications';
import { useNotification } from '../../contexts/NotificationContext';
import type { Notification, NotificationSchwere } from '../../types/notification.types';

const POLL_INTERVAL_MS = 15_000; // 15 seconds

let sharedAudioCtx: AudioContext | null = null;

function playNotificationSound() {
  try {
    if (!sharedAudioCtx || sharedAudioCtx.state === 'closed') {
      sharedAudioCtx = new AudioContext();
    }
    const ctx = sharedAudioCtx;
    if (ctx.state === 'suspended') ctx.resume();
    const oscillator = ctx.createOscillator();
    const gain = ctx.createGain();
    oscillator.connect(gain);
    gain.connect(ctx.destination);
    oscillator.type = 'sine';
    oscillator.frequency.value = 600;
    const now = ctx.currentTime;
    gain.gain.setValueAtTime(0, now);
    gain.gain.linearRampToValueAtTime(0.3, now + 0.02);
    gain.gain.linearRampToValueAtTime(0, now + 0.15);
    oscillator.start(now);
    oscillator.stop(now + 0.15);
  } catch {
    // Audio blocked before first user interaction — fail silently
  }
}

/**
 * Only allow window.open for URLs whose origin matches the current app origin.
 * External-looking URLs (different host or protocol-relative) are rejected to
 * prevent open-redirect / tab-napping via notification link data from the backend.
 */
function isTrustedUrl(url: string): boolean {
  try {
    const parsed = new URL(url, window.location.origin);
    return parsed.origin === window.location.origin;
  } catch {
    return false;
  }
}

function schwerebColor(schwere: NotificationSchwere): 'error' | 'warning' | 'info' {
  if (schwere === 'fehler') return 'error';
  if (schwere === 'warnung') return 'warning';
  return 'info';
}

function formatRelative(iso: string): string {
  const diff = Date.now() - new Date(iso).getTime();
  const minutes = Math.floor(diff / 60_000);
  if (minutes < 2) return 'Gerade eben';
  if (minutes < 60) return `vor ${minutes} Min.`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `vor ${hours} Std.`;
  const days = Math.floor(hours / 24);
  return `vor ${days} Tag${days !== 1 ? 'en' : ''}`;
}

const NotificationBell: React.FC = () => {
  const navigate = useNavigate();
  const { showNotificationToast } = useNotification();
  const [unreadCount, setUnreadCount] = useState(0);
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [loadingList, setLoadingList] = useState(false);
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
  // Track known notification IDs to detect new ones; null = not yet initialized
  const knownIdsRef = useRef<Set<string> | null>(null);

  const fetchAndToastNew = useCallback(async () => {
    try {
      const data = await notificationsApi.getNotifications();
      const unread = data.filter((n) => !n.gelesen);
      const count = unread.length;
      setUnreadCount(count);

      if (knownIdsRef.current === null) {
        // First load — initialize without toasting
        knownIdsRef.current = new Set(data.map((n) => n.id));
        return;
      }

      // Find notifications we haven't seen before
      const newOnes = unread.filter((n) => !knownIdsRef.current!.has(n.id));
      if (newOnes.length > 0) {
        playNotificationSound();
      }
      newOnes.forEach((n) => {
        knownIdsRef.current!.add(n.id);
        const severity = n.schwere === 'fehler' ? 'error' : n.schwere === 'warnung' ? 'warning' : 'info';
        showNotificationToast(n.titel, severity);
      });
      // Also add all known IDs to avoid re-toasting on re-fetch
      // Prune to only current IDs to prevent unbounded growth
      const currentIds = new Set(data.map((n) => n.id));
      knownIdsRef.current = currentIds;
    } catch {
      // non-critical
    }
  }, [showNotificationToast]);

  // Poll for new notifications every 15 seconds
  useEffect(() => {
    fetchAndToastNew();
    pollTimerRef.current = setInterval(fetchAndToastNew, POLL_INTERVAL_MS);
    return () => {
      if (pollTimerRef.current) clearInterval(pollTimerRef.current);
    };
  }, [fetchAndToastNew]);

  const handleOpen = async (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
    setLoadingList(true);
    try {
      const data = await notificationsApi.getNotifications();
      setNotifications(data);
      // Mark all as known so we don't toast them again
      if (knownIdsRef.current === null) {
        knownIdsRef.current = new Set(data.map((n) => n.id));
      } else {
        data.forEach((n) => knownIdsRef.current!.add(n.id));
      }
      // Refresh count after loading full list
      const count = await notificationsApi.getUnreadCount();
      setUnreadCount(count);
    } catch {
      // non-critical
    } finally {
      setLoadingList(false);
    }
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleClickNotification = async (n: Notification) => {
    if (!n.gelesen) {
      try {
        await notificationsApi.markRead(n.id);
        setNotifications((prev) =>
          prev.map((item) => item.id === n.id ? { ...item, gelesen: true } : item)
        );
        setUnreadCount((c) => Math.max(0, c - 1));
      } catch {
        // non-critical
      }
    }
    handleClose();
    if (n.link) {
      if (n.link.startsWith('http://') || n.link.startsWith('https://')) {
        if (isTrustedUrl(n.link)) {
          window.open(n.link, '_blank');
        } else {
          console.warn('NotificationBell: blocked navigation to untrusted URL', n.link);
        }
      } else {
        navigate(n.link);
      }
    }
  };

  const handleMarkAllRead = async () => {
    try {
      await notificationsApi.markAllRead();
      setNotifications((prev) => prev.map((n) => ({ ...n, gelesen: true })));
      setUnreadCount(0);
    } catch {
      // non-critical
    }
  };

  const handleDeleteAllRead = async () => {
    try {
      await notificationsApi.deleteAllRead();
      setNotifications((prev) => prev.filter((n) => !n.gelesen));
    } catch {
      // non-critical
    }
  };

  const open = Boolean(anchorEl);
  const hasUnread = unreadCount > 0;

  return (
    <>
      <Tooltip title="Benachrichtigungen">
        <IconButton
          color="inherit"
          onClick={handleOpen}
          aria-label="Benachrichtigungen öffnen"
          size="small"
        >
          <Badge badgeContent={unreadCount} color="error" invisible={!hasUnread}>
            {hasUnread ? <BellIcon /> : <BellEmptyIcon />}
          </Badge>
        </IconButton>
      </Tooltip>

      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
        PaperProps={{ sx: { width: 360, maxHeight: 500, display: 'flex', flexDirection: 'column' } }}
      >
        {/* Header */}
        <Box sx={{ px: 2, py: 1.5, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <Typography variant="subtitle1" fontWeight={600}>
            Benachrichtigungen
          </Typography>
          <Box sx={{ display: 'flex', gap: 0.5 }}>
            {notifications.some((n) => n.gelesen) && (
              <Button size="small" onClick={handleDeleteAllRead} sx={{ fontSize: '0.75rem' }}>
                Gelesene löschen
              </Button>
            )}
            {unreadCount > 0 && (
              <Button size="small" onClick={handleMarkAllRead} sx={{ fontSize: '0.75rem' }}>
                Alle als gelesen markieren
              </Button>
            )}
          </Box>
        </Box>
        <Divider />

        {/* Body */}
        <Box sx={{ overflowY: 'auto', flexGrow: 1 }}>
          {loadingList ? (
            <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
              <CircularProgress size={28} />
            </Box>
          ) : notifications.length === 0 ? (
            <Box sx={{ textAlign: 'center', py: 4 }}>
              <BellEmptyIcon sx={{ fontSize: 40, color: 'text.disabled', mb: 1 }} />
              <Typography variant="body2" color="text.secondary">
                Keine Benachrichtigungen
              </Typography>
            </Box>
          ) : (
            <List disablePadding>
              {notifications.map((n, idx) => (
                <React.Fragment key={n.id}>
                  {idx > 0 && <Divider component="li" />}
                  <ListItem disablePadding>
                    <ListItemButton
                      onClick={() => handleClickNotification(n)}
                      sx={{
                        py: 1.5,
                        px: 2,
                        bgcolor: n.gelesen ? 'transparent' : 'action.hover',
                        alignItems: 'flex-start',
                        gap: 1,
                      }}
                    >
                      <CircleIcon
                        sx={{
                          fontSize: 8,
                          mt: 0.75,
                          flexShrink: 0,
                          color: n.gelesen ? 'transparent' : `${schwerebColor(n.schwere)}.main`,
                        }}
                      />
                      <ListItemText
                        primary={
                          <Typography variant="body2" fontWeight={n.gelesen ? 400 : 600}>
                            {n.titel}
                          </Typography>
                        }
                        secondary={
                          <Box>
                            <Typography variant="caption" color="text.secondary" display="block">
                              {n.nachricht}
                            </Typography>
                            <Typography variant="caption" color="text.disabled">
                              {formatRelative(n.erstellt_am)}
                            </Typography>
                          </Box>
                        }
                        disableTypography
                      />
                    </ListItemButton>
                  </ListItem>
                </React.Fragment>
              ))}
            </List>
          )}
        </Box>
      </Popover>
    </>
  );
};

export default NotificationBell;
