// Service Worker for Budget Manager PWA

const CACHE_NAME = "budget-manager-v1";

// Assets to precache
const precacheResources = [
  "/",
  "/index.html",
  "/src/main.ts",
  "/src/App.vue",
  "/styles/tailwind.css",
  "/styles/transitions.css",
  "/styles/style.css",
  "/icons/icon-192px.jpg",
  "/icons/icon-512px.jpg",
];

// Install event - precache assets
self.addEventListener("install", (event) => {
  console.log("Service worker install event");
  event.waitUntil(
    caches
      .open(CACHE_NAME)
      .then((cache) => {
        return cache.addAll(precacheResources);
      })
      .then(() => {
        // Skip waiting to activate immediately
        return self.skipWaiting();
      }),
  );
});

// Activate event - clean up old caches
self.addEventListener("activate", (event) => {
  console.log("Service worker activate event");
  event.waitUntil(
    caches
      .keys()
      .then((cacheNames) => {
        return Promise.all(
          cacheNames.map((cacheName) => {
            if (cacheName !== CACHE_NAME) {
              console.log("Deleting old cache:", cacheName);
              return caches.delete(cacheName);
            }
          }),
        );
      })
      .then(() => {
        // Take control of all clients
        return self.clients.claim();
      }),
  );
});

// Fetch event - serve from cache first, then network
self.addEventListener("fetch", (event) => {
  // Skip cross-origin requests
  if (
    !event.request.url.startsWith(self.location.origin) ||
    event.request.url.includes("/api/")
  ) {
    return;
  }

  event.respondWith(
    caches.match(event.request).then((cachedResponse) => {
      if (cachedResponse) {
        return cachedResponse;
      }

      return fetch(event.request)
        .then((response) => {
          // Cache successful responses
          if (
            response &&
            response.status === 200 &&
            response.type === "basic"
          ) {
            const responseToCache = response.clone();

            caches.open(CACHE_NAME).then((cache) => {
              cache.put(event.request, responseToCache);
            });
          }

          return response;
        })
        .catch(() => {
          // If fetch fails (offline), return a fallback
          if (event.request.mode === "navigate") {
            return caches.match("/");
          }

          return null;
        });
    }),
  );
});

// Background sync for offline transactions
self.addEventListener("sync", (event) => {
  if (event.tag === "sync-transactions") {
    event.waitUntil(syncTransactions());
  } else if (event.tag === "sync-categories") {
    event.waitUntil(syncCategories());
  }
});

// Push notification handler
self.addEventListener("push", (event) => {
  let data;
  try {
    data = event.data.json();
  } catch (error) {
    // If the data is not JSON, try to get it as text
    data = {
      message: event.data ? event.data.text() : "New notification",
      additionalData: {}
    };
  }
  
  // Format transaction type for display
  let messageText = data.message || "New transaction";
  
  // Default notification options
  const options = {
    body: messageText,
    icon: "/icons/icon-192px.jpg",
    badge: "/icons/icon-192px.jpg",
    vibrate: [100, 50, 100],
    data: data.additionalData || {},
    actions: [
      {
        action: 'view',
        title: 'View Details'
      }
    ],
    // Tag transactions by ID to prevent duplicate notifications
    tag: data.additionalData?.transactionId || 'transaction'
  };
  
  // Show different icons based on transaction type
  if (data.additionalData?.type === 'income') {
    options.icon = "/icons/income-icon.jpg";
  } else if (data.additionalData?.type === 'expense') {
    options.icon = "/icons/expense-icon.jpg";
  }
  
  console.log('Showing notification:', messageText);
  
  // Forward to any open clients
  forwardNotificationToClients(data);
  
  // Show system notification
  event.waitUntil(self.registration.showNotification("Budget Manager", options));
});

// Notification click handler
self.addEventListener("notificationclick", (event) => {
  event.notification.close();
  
  // Get transaction ID from notification data
  const transactionId = event.notification.data?.transactionId;
  
  // Handle different notification actions
  if (event.action === 'view' && transactionId) {
    // Open transaction details page
    event.waitUntil(
      clients.matchAll({type: 'window'})
        .then(clientList => {
          // Check if there is already a window open
          for (const client of clientList) {
            if (client.url.includes('/') && 'focus' in client) {
              client.navigate(`/edit/${transactionId}`);
              return client.focus();
            }
          }
          // If no window is open, open a new one
          return clients.openWindow(`/edit/${transactionId}`);
        })
    );
  } else {
    // Default: open the app
    event.waitUntil(
      clients.matchAll({type: 'window'})
        .then(clientList => {
          for (const client of clientList) {
            if (client.url.includes('/') && 'focus' in client) {
              return client.focus();
            }
          }
          return clients.openWindow('/');
        })
    );
  }
});

// Forward push notifications to all clients
function forwardNotificationToClients(data) {
  self.clients.matchAll().then(clients => {
    clients.forEach(client => {
      // Send message to client
      client.postMessage({
        type: 'PUSH_NOTIFICATION',
        title: 'Budget Manager',
        body: data.message,
        transactionId: data.additionalData?.transactionId,
        type: data.additionalData?.type
      });
    });
  });
}

// Listen for messages from clients
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'GET_AUTH_TOKEN') {
    // Token request from service worker
    self.clients.matchAll().then(clients => {
      if (clients.length > 0) {
        const token = localStorage.getItem('token');
        event.ports[0].postMessage({ token });
      }
    });
  }
});

// Sync transactions with the server
async function syncTransactions() {
  try {
    const pendingTransactions = await getPendingItems("pendingTransactions");
    const token = await getAuthToken();

    if (!token || pendingTransactions.length === 0) return;

    for (const transaction of pendingTransactions) {
      const response = await fetch("http://localhost:3000/api/transactions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify(transaction),
      });

      if (response.ok) {
        await removePendingItem("pendingTransactions", transaction.id);
      }
    }
  } catch (error) {
    console.error("Error syncing transactions:", error);
  }
}

// Sync categories with the server
async function syncCategories() {
  try {
    const pendingCategories = await getPendingItems("pendingCategories");
    const token = await getAuthToken();

    if (!token || pendingCategories.length === 0) return;

    for (const category of pendingCategories) {
      const response = await fetch("http://localhost:3000/api/categories", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify(category),
      });

      if (response.ok) {
        await removePendingItem("pendingCategories", category.id);
      }
    }
  } catch (error) {
    console.error("Error syncing categories:", error);
  }
}

// Helper function to get pending items from IndexedDB
async function getPendingItems(storeName) {
  return []; // Placeholder - in a real app, this would access IndexedDB
}

// Helper function to remove a pending item from IndexedDB
async function removePendingItem(storeName, id) {
  // Placeholder - in a real app, this would access IndexedDB
}

// Helper function to get auth token from client
async function getAuthToken() {
  const clients = await self.clients.matchAll();

  if (clients.length === 0) {
    return localStorage.getItem("token");
  }

  // Ask client for token
  const client = clients[0];
  return new Promise((resolve) => {
    const channel = new MessageChannel();
    channel.port1.onmessage = (event) => {
      resolve(event.data.token);
    };

    client.postMessage({ type: "GET_AUTH_TOKEN" }, [channel.port2]);
  });
}
