// Service worker registration and PWA functionality

/**
 * Register a service worker for the PWA
 * This enables offline functionality, caching, and push notifications
 */
export async function registerServiceWorker() {
  if ("serviceWorker" in navigator) {
    try {
      const registration = await navigator.serviceWorker.register("/sw.js", {
        scope: "/",
      });

      if (registration.installing) {
        console.log("Service worker installing");
      } else if (registration.waiting) {
        console.log("Service worker installed");
      } else if (registration.active) {
        console.log("Service worker active");
      }

      // Ensure the service worker is ready before trying to subscribe
      await navigator.serviceWorker.ready;
      
      return registration;
    } catch (error) {
      console.error("Registration failed with error:", error);
      return null;
    }
  }
  return null;
}

/**
 * Request permission for push notifications
 * @returns {Promise<string>} The permission status: 'granted', 'denied', or 'default'
 */
export async function requestNotificationPermission() {
  if ("Notification" in window) {
    const permission = await Notification.requestPermission();
    return permission;
  }
  return "denied";
}

/**
 * Show a push notification
 * @param {string} title The notification title
 * @param {object} options The notification options (body, icon, etc.)
 */
export function showNotification(title, options) {
  if ("Notification" in window && Notification.permission === "granted") {
    try {
      const notification = new Notification(title, options);
      return notification;
    } catch (error) {
      console.error("Error showing notification:", error);
    }
  }
  return null;
}

/**
 * Check if the app is running in standalone mode (installed as PWA)
 * @returns {boolean} True if the app is installed as a PWA
 */
export function isPWAInstalled() {
  return (
    window.matchMedia("(display-mode: standalone)").matches ||
    window.navigator.standalone || // iOS Safari
    document.referrer.includes("android-app://")
  );
}

/**
 * Show a budget alert notification
 * @param {number} percentage The percentage of budget used
 * @param {string} category The category name
 */
export function showBudgetAlert(percentage, category) {
  const threshold = parseInt(
    localStorage.getItem("budgetAlertThreshold") || "80",
  );
  const notificationsEnabled =
    localStorage.getItem("notificationsEnabled") === "true";

  if (percentage >= threshold && notificationsEnabled) {
    showNotification("Budget Alert", {
      body: `You've used ${percentage}% of your budget for ${category || "this month"}`,
      icon: "/icons/icon-192px.jpg",
      tag: "budget-alert",
      renotify: true,
    });
  }
}

/**
 * Check if the app is online
 * @returns {boolean} True if the app is online
 */
export function isOnline() {
  return navigator.onLine;
}

/**
 * Get the VAPID public key from the server
 * @returns {Promise<string>} The VAPID public key
 */
export async function getVapidPublicKey(): Promise<string | null> {
  try {
    console.log('Fetching VAPID public key...');
    const response = await fetch('/api/push/vapid-public-key', {
      method: 'GET',
      headers: {
        'Accept': 'text/plain'
      }
    });
    
    if (!response.ok) {
      console.error('Failed to fetch VAPID public key, status:', response.status);
      throw new Error('Failed to fetch VAPID public key');
    }
    
    const key = await response.text();
    console.log('Received VAPID public key:', key.substring(0, 10) + '...');
    return key;
  } catch (error) {
    console.error('Error getting VAPID public key:', error);
    return null;
  }
}

/**
 * Convert a base64 string to a Uint8Array for push subscription
 * @param {string} base64String The base64 string to convert
 * @returns {Uint8Array} The converted array
 */
function urlBase64ToUint8Array(base64String: string): ArrayBuffer {
  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.buffer;
}

/**
 * Subscribe to push notifications
 * @returns {Promise<PushSubscription|null>} The push subscription
 */
export async function subscribeToPushNotifications(): Promise<PushSubscription | null> {
  try {
    if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
      console.log('Push notifications not supported');
      return null;
    }

    // Get service worker registration
    const registration = await navigator.serviceWorker.ready;
    console.log('Service worker is ready for push subscription');
    
    // Check for existing subscription
    const existingSubscription = await registration.pushManager.getSubscription();
    if (existingSubscription) {
      console.log('Already subscribed to push notifications');
      return existingSubscription;
    }
    
    // Get VAPID key
    const vapidPublicKey = await getVapidPublicKey();
    if (!vapidPublicKey) {
      throw new Error('Failed to get VAPID public key');
    }
    
    console.log('Converting VAPID key to Uint8Array');
    // Convert key to array
    const applicationServerKey = urlBase64ToUint8Array(vapidPublicKey);
    
    console.log('Subscribing to push notifications...');
    // Subscribe
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey
    });
    
    console.log('Push subscription successful, registering with server');
    // Register with backend
    await registerSubscriptionWithServer(subscription);
    
    return subscription;
  } catch (error) {
    console.error('Error subscribing to push notifications:', error);
    return null;
  }
}

/**
 * Register push subscription with the backend
 * @param {PushSubscription} subscription The push subscription to register
 */
async function registerSubscriptionWithServer(subscription: PushSubscription): Promise<boolean> {
  try {
    const token = localStorage.getItem('token');
    if (!token) {
      throw new Error('Not authenticated');
    }

    const response = await fetch('/api/push/register', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({ subscription })
    });

    if (!response.ok) {
      throw new Error('Failed to register subscription with server');
    }
    
    return true;
  } catch (error) {
    console.error('Error registering subscription with server:', error);
    return false;
  }
}
