const CACHE_NAME = 'trips-v17';
const STATIC_CACHE = 'trips-static-v17';
const DATA_CACHE = 'trips-data-v17';
const IMAGE_CACHE = 'trips-images-v17';

// Static assets to cache on install
const STATIC_ASSETS = [
  '/'
];

// Install event - cache static assets
self.addEventListener('install', (event) => {
  console.log('[SW] Installing service worker...');
  event.waitUntil(
    caches.open(STATIC_CACHE)
      .then((cache) => {
        console.log('[SW] Caching static assets');
        return cache.addAll(STATIC_ASSETS);
      })
      .then(() => self.skipWaiting())
  );
});

// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
  console.log('[SW] Activating service worker...');
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          // Delete old version caches
          if (cacheName.startsWith('trips-') &&
              cacheName !== STATIC_CACHE &&
              cacheName !== DATA_CACHE &&
              cacheName !== IMAGE_CACHE) {
            console.log('[SW] Deleting old cache:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => self.clients.claim())
  );
});

// Check if response is valid (not error page, not Cloudflare error)
function isValidResponse(response) {
  if (!response) return false;
  if (!response.ok) return false;
  if (response.redirected) return false;
  if (response.status !== 200) return false;

  // Check content-type to detect error pages
  const contentType = response.headers.get('content-type') || '';

  // If we expect HTML but got something else, it might be an error
  return true;
}

// Check if response is a Cloudflare/proxy error page
async function isErrorPage(response) {
  if (!response) return true;
  if (response.status >= 500) return true;
  if (response.status === 0) return true;

  // Clone to read body without consuming
  const clone = response.clone();
  try {
    const text = await clone.text();
    // Detect common error page signatures
    if (text.includes('cloudflare') && text.includes('Error')) return true;
    if (text.includes('502 Bad Gateway')) return true;
    if (text.includes('503 Service')) return true;
    if (text.includes('504 Gateway')) return true;
  } catch (e) {
    // If we can't read it, assume it's fine
  }
  return false;
}

// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  // Skip non-GET requests
  if (event.request.method !== 'GET') {
    return;
  }

  // Never cache auth pages - always go to network
  if (url.pathname === '/login' || url.pathname === '/logout' || url.pathname.startsWith('/auth')) {
    return;
  }

  // Handle CDN requests (Quill, etc.) - cache first
  if (url.origin !== location.origin) {
    if (url.hostname.includes('cdn.jsdelivr.net')) {
      event.respondWith(
        caches.match(event.request).then(cached => {
          return cached || fetch(event.request);
        })
      );
    }
    return;
  }

  // Handle API requests
  if (url.pathname.startsWith('/api/')) {
    event.respondWith(handleApiRequest(event.request));
    return;
  }

  // Handle image requests
  if (url.pathname.startsWith('/images/')) {
    event.respondWith(handleImageRequest(event.request));
    return;
  }

  // Handle document requests
  if (url.pathname.startsWith('/documents/')) {
    event.respondWith(handleDocumentRequest(event.request));
    return;
  }

  // Handle trip pages - CACHE FIRST for offline support
  if (url.pathname.startsWith('/trip/')) {
    event.respondWith(handleTripPageRequest(event.request));
    return;
  }

  // Handle main page - cache first, then pre-cache all trips
  if (url.pathname === '/') {
    event.respondWith(handleMainPageRequest(event.request));
    return;
  }

  // Default: cache first, network fallback
  event.respondWith(handleStaticRequest(event.request));
});

// Handle main page - NETWORK FIRST, only cache when offline
async function handleMainPageRequest(request) {
  // Try network first
  try {
    const response = await fetch(request);

    // If redirected (to login), clear cache and return the redirect
    if (response.redirected) {
      console.log('[SW] Main page redirected (likely to login), clearing cache');
      const cache = await caches.open(STATIC_CACHE);
      cache.delete(request);
      return response;
    }

    if (response.ok) {
      // Clone to read HTML and cache main page images
      const htmlText = await response.clone().text();

      const cache = await caches.open(STATIC_CACHE);
      cache.put(request, response.clone());

      // Cache main page images in background
      cacheMainPageImages(htmlText);

      return response;
    }

    // Non-OK response, just return it
    return response;
  } catch (error) {
    // Network failed - we're offline, try cache
    console.log('[SW] Main page network failed, trying cache');
    const cachedResponse = await caches.match(request);
    if (cachedResponse) return cachedResponse;
    return createOfflineResponse('Main page not available offline');
  }
}

// Cache images from main page
async function cacheMainPageImages(html) {
  const imageCache = await caches.open(IMAGE_CACHE);
  const imageUrls = extractUrls(html, /\/images\/[^"'\s<>]+/g);

  for (const imgUrl of imageUrls) {
    try {
      const existing = await caches.match(imgUrl);
      if (!existing) {
        const imgResponse = await fetch(imgUrl);
        if (imgResponse.ok) {
          await imageCache.put(imgUrl, imgResponse);
          console.log('[SW] Cached main page image:', imgUrl);
        }
      }
    } catch (e) { /* skip */ }
  }
}

// Handle trip pages - NETWORK FIRST, fall back to cache only when offline
async function handleTripPageRequest(request) {
  // Always try network first
  try {
    console.log('[SW] Fetching trip from network:', request.url);
    const response = await fetch(request);

    // Check if response is valid
    if (response.ok && response.status === 200) {
      // Check if we got redirected to login/auth page (auth failure)
      const finalUrl = response.url || '';
      if (finalUrl.includes('/login') || finalUrl.includes('pocket.') || finalUrl.includes('/authorize') || finalUrl.includes('/oauth')) {
        console.log('[SW] Redirected to auth, not caching:', finalUrl);
        // Clear any cached version of this page since user is logged out
        const cache = await caches.open(DATA_CACHE);
        cache.delete(request);
        return response;
      }

      // Cache and return the valid response
      const cache = await caches.open(DATA_CACHE);
      cache.put(request, response.clone());
      console.log('[SW] Cached trip page:', request.url);
      return response;
    }

    // Non-200 response, just return it (might be login redirect)
    console.log('[SW] Non-200 response:', response.status, request.url);
    return response;
  } catch (error) {
    // Network failed - we're offline, try cache
    console.log('[SW] Network failed, trying cache for trip:', request.url);
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      console.log('[SW] Serving trip from cache (offline):', request.url);
      // Add header to indicate offline mode
      const headers = new Headers(cachedResponse.headers);
      headers.set('X-From-Cache', 'true');
      return new Response(cachedResponse.body, {
        status: cachedResponse.status,
        statusText: cachedResponse.statusText,
        headers: headers
      });
    }
    return createOfflineResponse('Trip not available offline. Download this trip while online first.');
  }
}

// Update trip cache in background
async function updateTripCache(request) {
  try {
    const response = await fetch(request);
    if (response.ok && response.status === 200) {
      // Skip if redirected to login/auth
      const finalUrl = response.url || '';
      if (finalUrl.includes('/login') || finalUrl.includes('pocket.') || finalUrl.includes('/authorize') || finalUrl.includes('/oauth')) return;

      const cache = await caches.open(DATA_CACHE);
      cache.put(request, response.clone());
      console.log('[SW] Updated cache for:', request.url);
    }
  } catch (error) {
    // Silent fail - we already served from cache
  }
}

// Send progress message to all clients
async function sendProgress(message, progress, total) {
  const clients = await self.clients.matchAll();
  clients.forEach(client => {
    client.postMessage({
      type: 'CACHE_PROGRESS',
      message,
      progress,
      total
    });
  });
}

// Pre-cache all trips when main page loads
let preCacheInProgress = false;
async function preCacheAllTrips() {
  // Prevent duplicate runs
  if (preCacheInProgress) return;
  preCacheInProgress = true;

  console.log('[SW] Pre-caching all trips (full offline mode)...');

  // Small delay to ensure page JS has loaded
  await new Promise(r => setTimeout(r, 500));

  try {
    const response = await fetch('/api/trips', { credentials: 'include' });
    if (!response.ok) {
      console.log('[SW] Failed to fetch trips list:', response.status);
      await sendProgress('Offline sync failed - not logged in?', 0, 0);
      preCacheInProgress = false;
      return;
    }

    const trips = await response.json();
    const dataCache = await caches.open(DATA_CACHE);
    const imageCache = await caches.open(IMAGE_CACHE);

    const totalTrips = trips.length;
    let currentTrip = 0;
    let totalAssets = 0;
    let cachedAssets = 0;

    await sendProgress('Starting offline sync...', 0, totalTrips);

    // Cache the trips list API response
    const tripsResponse = await fetch('/api/trips', { credentials: 'include' });
    if (tripsResponse.ok) {
      dataCache.put('/api/trips', tripsResponse);
    }

    // Cache each trip page and its assets
    for (const trip of trips) {
      currentTrip++;
      const tripUrl = `/trip/${trip.id}`;

      await sendProgress(`Caching: ${trip.name}`, currentTrip, totalTrips);

      try {
        const tripResponse = await fetch(tripUrl, { credentials: 'include' });
        if (tripResponse.ok && tripResponse.status === 200) {
          // Skip if redirected to login/auth
          const finalUrl = tripResponse.url || '';
          if (finalUrl.includes('/login') || finalUrl.includes('pocket.') || finalUrl.includes('/authorize') || finalUrl.includes('/oauth')) {
            console.log('[SW] Auth required for trip:', trip.name);
            continue;
          }

          // Clone response to read HTML and still cache it
          const htmlText = await tripResponse.clone().text();
          await dataCache.put(tripUrl, tripResponse);
          console.log('[SW] Pre-cached trip:', trip.name);

          // Extract and cache all images
          const imageUrls = extractUrls(htmlText, /\/images\/[^"'\s<>]+/g);
          totalAssets += imageUrls.length;
          for (const imgUrl of imageUrls) {
            try {
              const existing = await caches.match(imgUrl);
              if (!existing) {
                const imgResponse = await fetch(imgUrl);
                if (imgResponse.ok) {
                  await imageCache.put(imgUrl, imgResponse);
                  cachedAssets++;
                  console.log('[SW] Cached image:', imgUrl);
                }
              } else {
                cachedAssets++;
              }
            } catch (e) { /* skip failed images */ }
          }

          // Extract and cache all documents
          const docUrls = extractUrls(htmlText, /\/documents\/[^"'\s<>]+/g);
          totalAssets += docUrls.length;
          for (const docUrl of docUrls) {
            try {
              const existing = await caches.match(docUrl);
              if (!existing) {
                const docResponse = await fetch(docUrl);
                if (docResponse.ok) {
                  await dataCache.put(docUrl, docResponse);
                  cachedAssets++;
                  console.log('[SW] Cached document:', docUrl);
                }
              } else {
                cachedAssets++;
              }
            } catch (e) { /* skip failed docs */ }
          }
        }
      } catch (e) {
        console.log('[SW] Failed to pre-cache trip:', trip.name, e);
      }
    }

    await sendProgress('complete', totalTrips, totalTrips);
    console.log('[SW] Full pre-caching complete!', totalTrips, 'trips,', cachedAssets, 'files');
    preCacheInProgress = false;
  } catch (error) {
    console.error('[SW] Pre-caching failed:', error);
    await sendProgress('Offline sync failed', 0, 0);
    preCacheInProgress = false;
  }
}

// Helper to extract URLs from HTML using regex
function extractUrls(html, pattern) {
  const matches = html.match(pattern) || [];
  // Dedupe and clean
  return [...new Set(matches.map(url => {
    // Decode HTML entities and remove trailing quotes
    return url.replace(/&amp;/g, '&').replace(/["'<>]/g, '');
  }))];
}

// Handle static assets - cache first
async function handleStaticRequest(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    // Return cached, but also update cache in background
    fetchAndCache(request, STATIC_CACHE);
    return cachedResponse;
  }

  return fetchAndCache(request, STATIC_CACHE);
}

// Handle API requests - cache first for trip data, network for others
async function handleApiRequest(request) {
  const url = new URL(request.url);

  // These endpoints can be cached for offline use
  const cacheableEndpoints = [
    '/api/trips',
    '/api/trip/'
  ];

  const shouldCache = cacheableEndpoints.some(endpoint => url.pathname.startsWith(endpoint));

  // NETWORK FIRST - always try network, only use cache when offline
  try {
    const response = await fetch(request);
    if (response.ok && shouldCache) {
      const cache = await caches.open(DATA_CACHE);
      cache.put(request, response.clone());
    }
    return response;
  } catch (error) {
    // Network failed - we're offline, try cache
    console.log('[SW] API request failed (offline), trying cache:', request.url);
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      const headers = new Headers(cachedResponse.headers);
      headers.set('X-From-Cache', 'true');
      return new Response(cachedResponse.body, {
        status: cachedResponse.status,
        statusText: cachedResponse.statusText,
        headers: headers
      });
    }
    return createOfflineJsonResponse({ error: 'Offline - data not cached' });
  }
}

// Fetch and cache API in background
async function fetchAndCacheApi(request) {
  try {
    const response = await fetch(request);
    if (response.ok) {
      const cache = await caches.open(DATA_CACHE);
      cache.put(request, response.clone());
    }
  } catch (e) {
    // Silent fail
  }
}

// Handle image requests - cache first (images don't change)
async function handleImageRequest(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    return cachedResponse;
  }

  try {
    const response = await fetch(request);
    if (response.ok) {
      const cache = await caches.open(IMAGE_CACHE);
      cache.put(request, response.clone());
    }
    return response;
  } catch (error) {
    console.log('[SW] Image not available offline:', request.url);
    return createPlaceholderImage();
  }
}

// Handle document requests - cache first
async function handleDocumentRequest(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    return cachedResponse;
  }

  try {
    const response = await fetch(request);
    if (response.ok) {
      const cache = await caches.open(DATA_CACHE);
      cache.put(request, response.clone());
    }
    return response;
  } catch (error) {
    console.log('[SW] Document not available offline:', request.url);
    return createOfflineResponse('Document not available offline');
  }
}

// Helper: fetch and cache
async function fetchAndCache(request, cacheName) {
  try {
    const response = await fetch(request);
    if (response.ok) {
      const cache = await caches.open(cacheName);
      cache.put(request, response.clone());
    }
    return response;
  } catch (error) {
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }
    throw error;
  }
}

// Helper: create offline HTML response
function createOfflineResponse(message) {
  return new Response(
    `<!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Offline - Trips</title>
      <style>
        body {
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          background: #1a1a2e;
          color: #eee;
          display: flex;
          justify-content: center;
          align-items: center;
          min-height: 100vh;
          margin: 0;
          text-align: center;
        }
        .offline-container {
          padding: 40px;
        }
        .offline-icon {
          font-size: 64px;
          margin-bottom: 20px;
        }
        h1 { color: #fff; margin-bottom: 10px; }
        p { color: #aaa; }
        button {
          background: #4a9eff;
          color: white;
          border: none;
          padding: 12px 24px;
          border-radius: 8px;
          cursor: pointer;
          margin-top: 20px;
          font-size: 16px;
        }
        button:hover { background: #3a8eef; }
      </style>
    </head>
    <body>
      <div class="offline-container">
        <div class="offline-icon">📡</div>
        <h1>You're Offline</h1>
        <p>${message}</p>
        <button onclick="location.reload()">Try Again</button>
      </div>
    </body>
    </html>`,
    {
      status: 503,
      headers: { 'Content-Type': 'text/html' }
    }
  );
}

// Helper: create offline JSON response
function createOfflineJsonResponse(data) {
  return new Response(JSON.stringify(data), {
    status: 503,
    headers: {
      'Content-Type': 'application/json',
      'X-From-Cache': 'false'
    }
  });
}

// Helper: create placeholder image
function createPlaceholderImage() {
  // 1x1 transparent PNG
  const placeholder = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
  const binary = atob(placeholder);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return new Response(bytes, {
    status: 200,
    headers: { 'Content-Type': 'image/png' }
  });
}

// Listen for messages from the main thread
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }

  // Cache specific trip data on demand
  if (event.data && event.data.type === 'CACHE_TRIP') {
    const tripId = event.data.tripId;
    cacheTripData(tripId);
  }

  // Clear all caches
  if (event.data && event.data.type === 'CLEAR_CACHE') {
    caches.keys().then((names) => {
      names.forEach((name) => {
        if (name.startsWith('trips-')) {
          caches.delete(name);
        }
      });
    });
  }

  // Force pre-cache all trips
  if (event.data && event.data.type === 'PRECACHE_ALL') {
    preCacheAllTrips();
  }
});

// Cache trip data for offline use
async function cacheTripData(tripId) {
  const cache = await caches.open(DATA_CACHE);

  try {
    // Cache trip page
    const tripPageUrl = `/trip/${tripId}`;
    const tripPageResponse = await fetch(tripPageUrl);
    if (tripPageResponse.ok) {
      await cache.put(tripPageUrl, tripPageResponse);
    }

    console.log('[SW] Cached trip data for:', tripId);
  } catch (error) {
    console.error('[SW] Failed to cache trip:', error);
  }
}
