import { useEffect, useState, useCallback, useRef } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
import { Task } from '@shared/schema';
import { useTranslation } from 'react-i18next';
import { formatDistanceToNow } from 'date-fns';
import { de, enUS } from 'date-fns/locale';

// Convert VAPID public key from base64 to Uint8Array (required for subscription)
function urlBase64ToUint8Array(base64String: string): Uint8Array {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/-/g, '+')
    .replace(/_/g, '/');

  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

// Check if push notifications are supported
function isPushSupported(): boolean {
  return 'serviceWorker' in navigator && 'PushManager' in window;
}

// Check if running as installed PWA (standalone)
function isStandalone(): boolean {
  return window.matchMedia('(display-mode: standalone)').matches ||
    (window.navigator as any).standalone === true;
}

export function useNotifications({ poll = true }: { poll?: boolean } = {}) {
  const { t, i18n } = useTranslation();
  const [permission, setPermission] = useState<NotificationPermission>('default');
  const [pushSupported, setPushSupported] = useState(false);
  const [pushSubscription, setPushSubscription] = useState<PushSubscription | null>(null);
  const [serviceWorkerReady, setServiceWorkerReady] = useState(false);
  const [enabled, setEnabled] = useState(false);

  // Track notified tasks to prevent duplicate alerts in same session
  const notifiedTasksRef = useRef<Set<string>>(new Set());

  // Check support and register service worker on mount
  useEffect(() => {
    const initializePush = async () => {
      if (!isPushSupported()) {
        console.log('[Notifications] Push not supported');
        setPushSupported(false);
        return;
      }

      setPushSupported(true);

      try {
        // Register service worker
        const registration = await navigator.serviceWorker.register('/sw.js', {
          scope: '/'
        });
        console.log('[Notifications] Service worker registered:', registration.scope);

        // Wait for service worker to be ready
        await navigator.serviceWorker.ready;
        setServiceWorkerReady(true);

        // Check current subscription
        const subscription = await registration.pushManager.getSubscription();
        setPushSubscription(subscription);

        // Update permission state
        if ('Notification' in window) {
          setPermission(Notification.permission);
          const isEnabled = localStorage.getItem('taskflow-push-enabled') === 'true';
          setEnabled(isEnabled && Notification.permission === 'granted' && subscription !== null);
        }
      } catch (error) {
        console.error('[Notifications] Service worker registration failed:', error);
      }
    };

    initializePush();
  }, []);

  // Fetch VAPID public key
  const { data: vapidData } = useQuery<{ publicKey: string }>({
    queryKey: ['/api/push/vapid-public-key'],
    enabled: serviceWorkerReady && pushSupported,
    retry: false,
    staleTime: Infinity, // VAPID key doesn't change
  });

  // Subscribe mutation
  const subscribeMutation = useMutation({
    mutationFn: async (subscription: PushSubscription) => {
      const response = await fetch('/api/push/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ subscription: subscription.toJSON() }),
      });
      if (!response.ok) throw new Error('Failed to subscribe');
      return response.json();
    },
    onSuccess: () => {
      localStorage.setItem('taskflow-push-enabled', 'true');
      setEnabled(true);
    },
  });

  // Unsubscribe mutation
  const unsubscribeMutation = useMutation({
    mutationFn: async (endpoint: string) => {
      const response = await fetch('/api/push/unsubscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ endpoint }),
      });
      if (!response.ok) throw new Error('Failed to unsubscribe');
      return response.json();
    },
    onSuccess: () => {
      localStorage.setItem('taskflow-push-enabled', 'false');
      setEnabled(false);
      setPushSubscription(null);
    },
  });

  // Request permission and subscribe to push
  const requestPermission = useCallback(async (): Promise<boolean> => {
    if (!pushSupported || !serviceWorkerReady || !vapidData?.publicKey) {
      console.log('[Notifications] Cannot request permission - prerequisites not met');

      // Fallback to basic notification permission
      if ('Notification' in window) {
        const result = await Notification.requestPermission();
        setPermission(result);
        return result === 'granted';
      }
      return false;
    }

    try {
      // Request notification permission
      const permissionResult = await Notification.requestPermission();
      setPermission(permissionResult);

      if (permissionResult !== 'granted') {
        console.log('[Notifications] Permission denied');
        return false;
      }

      // Get service worker registration
      const registration = await navigator.serviceWorker.ready;

      // Check for existing subscription
      let subscription = await registration.pushManager.getSubscription();

      if (!subscription) {
        // Create new subscription
        const applicationServerKey = urlBase64ToUint8Array(vapidData.publicKey);
        subscription = await registration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey,
        });
        console.log('[Notifications] New push subscription created');
      }

      setPushSubscription(subscription);

      // Send subscription to server
      await subscribeMutation.mutateAsync(subscription);

      return true;
    } catch (error) {
      console.error('[Notifications] Failed to subscribe:', error);
      return false;
    }
  }, [pushSupported, serviceWorkerReady, vapidData?.publicKey, subscribeMutation]);

  // Toggle notifications on/off
  const toggleEnabled = useCallback(async (value: boolean) => {
    if (value) {
      // Enable notifications
      if (permission !== 'granted' || !pushSubscription) {
        await requestPermission();
      } else {
        setEnabled(true);
        localStorage.setItem('taskflow-push-enabled', 'true');
      }
    } else {
      // Disable notifications
      if (pushSubscription) {
        try {
          await pushSubscription.unsubscribe();
          await unsubscribeMutation.mutateAsync(pushSubscription.endpoint);
        } catch (error) {
          console.error('[Notifications] Failed to unsubscribe:', error);
        }
      }
      setEnabled(false);
      localStorage.setItem('taskflow-push-enabled', 'false');
    }
  }, [permission, pushSubscription, requestPermission, unsubscribeMutation]);

  // Send test notification
  const sendTestNotification = useCallback(async () => {
    try {
      const response = await fetch('/api/push/test', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
      });
      return response.ok;
    } catch {
      return false;
    }
  }, []);

  // Query tasks for fallback polling (when push isn't available or app is open)
  const { data: tasks } = useQuery<Task[]>({
    queryKey: ['/api/tasks'],
    enabled: enabled && poll && !pushSupported // Only poll if push isn't supported
  });

  // Fallback Polling Logic (for when push isn't available)
  useEffect(() => {
    if (!poll || !enabled || !tasks || permission !== 'granted' || pushSupported) return;

    const checkTasks = () => {
      const now = new Date();

      tasks.forEach(task => {
        if (!task.dueDate || task.status === 'done' || notifiedTasksRef.current.has(task.id)) return;

        const dueDate = new Date(task.dueDate);
        const diffMs = dueDate.getTime() - now.getTime();
        const diffMinutes = diffMs / (1000 * 60);

        // Alert: Upcoming (15 min before)
        if (diffMinutes > 0 && diffMinutes <= 15) {
          sendNotification(task, 'upcoming');
          notifiedTasksRef.current.add(task.id);
        }

        // Alert: Just Overdue
        if (diffMinutes < 0) {
          sendNotification(task, 'overdue');
          notifiedTasksRef.current.add(task.id);
        }
      });
    };

    const sendNotification = (task: Task, type: 'upcoming' | 'overdue') => {
      const title = type === 'upcoming'
        ? t('notifications.upcomingTitle', { task: task.title })
        : t('notifications.overdueTitle', { task: task.title });

      const body = type === 'upcoming'
        ? t('notifications.upcomingBody', { time: formatDistanceToNow(new Date(task.dueDate!), { locale: i18n.language === 'de' ? de : enUS }) })
        : t('notifications.overdueBody');

      new Notification(title, {
        body,
        tag: `task-${task.id}-${type}`
      });
    };

    checkTasks();
    const interval = setInterval(checkTasks, 60000);

    return () => clearInterval(interval);
  }, [enabled, tasks, permission, t, i18n.language, poll, pushSupported]);

  return {
    permission,
    enabled,
    pushSupported,
    pushSubscription,
    isStandalone: isStandalone(),
    serviceWorkerReady,
    requestPermission,
    toggleEnabled,
    sendTestNotification,
    isLoading: subscribeMutation.isPending || unsubscribeMutation.isPending,
  };
}
