const VAPID_PUBLIC_KEY = import.meta.env.VITE_PUBLIC_VAPID_KEY as string;
const VITE_BASE_URL = import.meta.env.VITE_BASE_URL as string;

function urlBase64ToUint8Array(base64String: string) {
  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;
}

export async function registerServiceWorker() {
  if (!('serviceWorker' in navigator)) {
    throw new Error('Service workers are not supported in this browser');
  }

  let registration: ServiceWorkerRegistration;
  
  try {
    // Check if service worker is already registered
    const existingRegistration = await navigator.serviceWorker.getRegistration('/service-worker.js');
    
    if (existingRegistration) {
      // Service worker already registered, wait for it to be ready
      registration = await navigator.serviceWorker.ready;
    } else {
      // Register new service worker
      registration = await navigator.serviceWorker.register('/service-worker.js');
      
      // Wait for the service worker to be ready (may take a moment)
      registration = await navigator.serviceWorker.ready;
    }
  } catch (error: any) {
    throw new Error(`Failed to register service worker: ${error?.message || 'Unknown error'}`);
  }

  return registration;
}

export async function subscribeUserToPush(register: ServiceWorkerRegistration) {
  if (!VAPID_PUBLIC_KEY) {
    throw new Error('Missing VAPID public key configuration');
  }

  // Validate VAPID key format (should be base64 URL-safe string)
  if (!VAPID_PUBLIC_KEY || VAPID_PUBLIC_KEY.trim().length === 0) {
    throw new Error('VAPID public key is empty. Please configure VITE_PUBLIC_VAPID_KEY in your environment variables.');
  }

  // Check if pushManager is available
  if (!register.pushManager) {
    throw new Error('Push manager is not available. Please ensure your browser supports push notifications and the service worker is properly registered.');
  }

  // Check if already subscribed
  let subscription: PushSubscription;
  try {
    const existingSubscription = await register.pushManager.getSubscription();
    
    if (existingSubscription) {
      // Already subscribed, check if it's still valid by trying to use it
      try {
        // Verify the subscription is still valid
        subscription = existingSubscription;
      } catch (error) {
        // Existing subscription is invalid, unsubscribe and create new one
        console.warn('[Push] Existing subscription is invalid, creating new one...');
        await existingSubscription.unsubscribe().catch(() => {
          // Ignore unsubscribe errors
        });
        subscription = await register.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
        });
      }
    } else {
      // Subscribe to push
      try {
        subscription = await register.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
        });
      } catch (subscribeError: any) {
        // If subscription fails, try to clear any invalid subscriptions and retry once
        console.warn('[Push] Initial subscription failed, attempting to clear and retry...');
        try {
          const allSubscriptions = await register.pushManager.getSubscription();
          if (allSubscriptions) {
            await allSubscriptions.unsubscribe().catch(() => {
              // Ignore unsubscribe errors
            });
          }
          // Retry subscription
          subscription = await register.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
          });
        } catch (retryError: any) {
          // Provide more specific error messages
          const errorMsg = subscribeError?.message || retryError?.message || 'Unknown error';
          if (errorMsg.includes('push service error') || errorMsg.includes('Registration failed')) {
            throw new Error('Push service error: The browser\'s push service rejected the subscription. This may be due to an invalid VAPID key, network issues, or browser push service problems. Please verify your VAPID key configuration and try again.');
          }
          throw new Error(`Failed to subscribe to push notifications: ${errorMsg}`);
        }
      }
    }
  } catch (error: any) {
    // Provide more helpful error messages
    const errorMsg = error?.message || 'Unknown error';
    if (errorMsg.includes('push service error') || errorMsg.includes('Registration failed')) {
      throw new Error('Push service error: The browser\'s push service rejected the subscription. Please verify your VAPID key is correct and matches the backend configuration. If the problem persists, try clearing your browser cache and service workers.');
    }
    if (errorMsg.includes('Invalid key')) {
      throw new Error('Invalid VAPID key format. Please verify that VITE_PUBLIC_VAPID_KEY is correctly set and matches the backend VAPID_PUBLIC_KEY.');
    }
    throw new Error(`Failed to subscribe to push notifications: ${errorMsg}`);
  }

  // Convert subscription to JSON format for backend
  const subscriptionJson = subscription.toJSON();
  
  // Check if we're in production mode (cookies used for auth)
  const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production';
  
  // In development, get token from localStorage
  // In production, httpOnly cookies are sent automatically with credentials: 'include'
  const token = isProduction ? null : (window as any)?.localStorage?.getItem?.('accessToken');
  
  // In development mode, we need a token
  if (!isProduction && !token) {
    throw new Error('Authentication token not found. Please log in again.');
  }

  // Send subscription to backend
  try {
    // Build headers - in production, auth is via httpOnly cookies
    const headers: HeadersInit = {
      'Content-Type': 'application/json',
    };
    
    // Only add Authorization header in development mode
    if (!isProduction && token) {
      headers['Authorization'] = `Bearer ${token}`;
    }
    
    const response = await fetch(`${VITE_BASE_URL}/api/v1/workflows/notifications/subscribe`, {
      method: 'POST',
      headers,
      credentials: 'include', // Always include credentials for cookie-based auth
      body: JSON.stringify(subscriptionJson)
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
      throw new Error(errorData?.error || errorData?.message || `Server error: ${response.status}`);
    }

    const result = await response.json();
    if (!result.success) {
      throw new Error(result.error || 'Failed to save subscription');
    }
  } catch (error: any) {
    // If it's already our error, rethrow it
    if (error instanceof Error && error.message.includes('Failed')) {
      throw error;
    }
    throw new Error(`Failed to save subscription to server: ${error?.message || 'Network error'}`);
  }

  return subscription;
}

export async function setupPushNotifications() {
  // Check if notifications are supported
  if (!('Notification' in window)) {
    throw new Error('Notifications are not supported in this browser');
  }

  // Check permission status
  let permission = Notification.permission;
  
  if (permission === 'denied') {
    throw new Error('Notification permission was denied. Please enable notifications in your browser settings and try again.');
  }

  if (permission === 'default') {
    // Request permission if not already requested
    permission = await Notification.requestPermission();
    if (permission !== 'granted') {
      throw new Error('Notification permission was denied. Please enable notifications in your browser settings and try again.');
    }
  }

  // Final check - permission should be 'granted' at this point
  if (permission !== 'granted') {
    throw new Error('Notification permission is required. Please grant permission and try again.');
  }

  // Register service worker (or get existing)
  let reg: ServiceWorkerRegistration;
  try {
    reg = await registerServiceWorker();
  } catch (error: any) {
    throw new Error(`Service worker registration failed: ${error?.message || 'Unknown error'}`);
  }

  // Subscribe to push
  try {
    await subscribeUserToPush(reg);
  } catch (error: any) {
    throw error; // Re-throw with detailed message
  }
}


