import { useState, useEffect, useCallback, useRef } from 'react';
import {
  getVapidPublicKey,
  subscribeToPush,
  unsubscribeFromPush,
  urlBase64ToUint8Array,
} from '@/lib/push-api';
import type { PushSubscriptionData } from '@/lib/push-api';

const STORAGE_KEY = 'push-notification-preference';

function checkPushSupport(): boolean {
  return (
    typeof window !== 'undefined' &&
    'serviceWorker' in navigator &&
    'PushManager' in window &&
    'Notification' in window
  );
}

function getInitialPermission(): NotificationPermission {
  if (!checkPushSupport()) return 'denied';
  return Notification.permission;
}

export interface UsePushNotificationsReturn {
  isSupported: boolean;
  isSubscribed: boolean;
  isLoading: boolean;
  permissionState: NotificationPermission;
  subscribe: () => Promise<boolean>;
  unsubscribe: () => Promise<boolean>;
  requestPermission: () => Promise<NotificationPermission>;
}

export function usePushNotifications(): UsePushNotificationsReturn {
  const [isSupported] = useState(checkPushSupport);
  const [isSubscribed, setIsSubscribed] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [permissionState, setPermissionState] = useState(getInitialPermission);
  const hasCheckedRef = useRef(false);

  useEffect(() => {
    if (!isSupported || hasCheckedRef.current) return;
    hasCheckedRef.current = true;

    navigator.serviceWorker.ready
      .then((registration) => registration.pushManager.getSubscription())
      .then((subscription) => {
        setIsSubscribed(!!subscription);
      })
      .catch(() => {});
  }, [isSupported]);

  const requestPermission =
    useCallback(async (): Promise<NotificationPermission> => {
      if (!isSupported) return 'denied';

      const permission = await Notification.requestPermission();
      setPermissionState(permission);
      return permission;
    }, [isSupported]);

  const subscribe = useCallback(async (): Promise<boolean> => {
    if (!isSupported) return false;

    setIsLoading(true);

    try {
      const permission = await requestPermission();
      if (permission !== 'granted') {
        setIsLoading(false);
        return false;
      }

      const publicKey = await getVapidPublicKey();
      if (!publicKey) {
        setIsLoading(false);
        return false;
      }

      const registration = await Promise.race([
        navigator.serviceWorker.ready,
        new Promise<ServiceWorkerRegistration>((_, reject) => {
          setTimeout(() => {
            reject(new Error('Service worker ready timeout'));
          }, 10000);
        }),
      ]);

      if (!registration.pushManager) {
        setIsLoading(false);
        return false;
      }

      let subscription = await registration.pushManager.getSubscription();

      if (!subscription) {
        const keyArray = urlBase64ToUint8Array(publicKey);
        subscription = await registration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: keyArray.buffer as ArrayBuffer,
        });
      }

      const subJson = subscription.toJSON();

      if (
        !subJson.endpoint ||
        !subJson.keys?.p256dh ||
        !subJson.keys?.auth
      ) {
        setIsLoading(false);
        return false;
      }

      const success = await subscribeToPush(subJson as PushSubscriptionData);

      if (success) {
        setIsSubscribed(true);
        localStorage.setItem(STORAGE_KEY, 'subscribed');
      }

      setIsLoading(false);
      return success;
    } catch {
      setIsLoading(false);
      return false;
    }
  }, [isSupported, requestPermission]);

  const unsubscribe = useCallback(async (): Promise<boolean> => {
    if (!isSupported) return false;

    setIsLoading(true);

    try {
      const registration = await navigator.serviceWorker.ready;
      const subscription = await registration.pushManager.getSubscription();

      if (subscription) {
        await unsubscribeFromPush(subscription.endpoint);
        await subscription.unsubscribe();
      }

      setIsSubscribed(false);
      localStorage.removeItem(STORAGE_KEY);
      setIsLoading(false);
      return true;
    } catch {
      setIsLoading(false);
      return false;
    }
  }, [isSupported]);

  return {
    isSupported,
    isSubscribed,
    isLoading,
    permissionState,
    subscribe,
    unsubscribe,
    requestPermission,
  };
}
