#!/usr/bin/env node

// Build script to generate optimized service worker for production
// Run with: node scripts/build-sw.js

import { writeFileSync, readFileSync, readdirSync, lstatSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const publicDir = join(__dirname, '../public');
const packageJsonPath = join(__dirname, '../package.json');

// Read package.json to get version
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
const version = packageJson.version || '1.0.0';

// Get all static assets from the dist directory (production build)
const getAllStaticAssets = () => {
  const assets = [
    '/', // Root path
    '/index.html' // Main HTML file
  ];

  // Add files from dist directory (built assets)
  const distDir = join(__dirname, '../dist');
  if (existsSync(distDir)) {
    try {
      // Add main HTML file
      if (existsSync(join(distDir, 'index.html'))) {
        assets.push('/index.html');
      }

      // Add manifest if it exists
      if (existsSync(join(distDir, 'manifest.json'))) {
        assets.push('/manifest.json');
      }

      // Add all files from dist root (excluding sw.js to avoid caching the service worker itself)
      readdirSync(distDir).filter(file => {
        const filePath = join(distDir, file);
        return !lstatSync(filePath).isDirectory() && 
               file !== 'sw.js' && 
               file !== 'manifest.json'; // Already added above
      }).forEach(file => {
        assets.push(`/${file}`);
      });

      // Add all assets from the assets directory
      const assetsDir = join(distDir, 'assets');
      if (existsSync(assetsDir)) {
        readdirSync(assetsDir).forEach(file => {
          const filePath = join(assetsDir, file);
          if (!lstatSync(filePath).isDirectory()) {
            assets.push(`/assets/${file}`);
          }
        });
      }

    } catch (error) {
      console.warn('Could not read dist directory:', error.message);
      console.warn('Make sure to run the build before generating the service worker');
    }
  } else {
    console.warn('Dist directory does not exist. Run the build first.');
    console.warn('Falling back to basic asset list.');
    return [
      '/',
      '/index.html',
      '/manifest.json'
    ];
  }

  return [...new Set(assets)]; // Remove duplicates
};

// Service worker generator function (simplified version)
const generateServiceWorkerForApp = (config) => {
  const {
    cacheName = 'app-cache',
    version = 'v1',
    staticAssets = [],
    apiEndpoints = [],
    cacheStrategy = 'staleWhileRevalidate',
    maxCacheAge = 7 * 24 * 60 * 60 * 1000,
    maxCacheSize = 100,
    customStrategies = {}
  } = config;

  return `// Generated Service Worker - Version ${version}
// Generated on: ${new Date().toISOString()}

const CACHE_NAME = '${cacheName}-${version}';
const STATIC_CACHE_NAME = '${cacheName}-static-${version}';
const DYNAMIC_CACHE_NAME = '${cacheName}-dynamic-${version}';
const MAX_CACHE_AGE = ${maxCacheAge};
const MAX_CACHE_SIZE = ${maxCacheSize};

// Assets to cache immediately on install
const STATIC_ASSETS = ${JSON.stringify(staticAssets, null, 2)};

// API endpoints configuration
const API_ENDPOINTS = ${JSON.stringify(apiEndpoints, null, 2)};

// Custom strategies configuration
const CUSTOM_STRATEGIES = ${JSON.stringify(customStrategies, null, 2)};

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

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

// Fetch event - serve from cache with configured strategy
self.addEventListener('fetch', (event) => {
  const request = event.request;
  const url = new URL(request.url);
  
  if (request.method !== 'GET') return;
  if (!url.protocol.startsWith('http')) return;
  
  // Check for custom strategies first
  for (const [pattern, strategy] of Object.entries(CUSTOM_STRATEGIES)) {
    if (url.pathname.includes(pattern)) {
      event.respondWith(executeStrategy(request, strategy));
      return;
    }
  }
  
  // Default strategies
  if (isStaticAsset(url)) {
    event.respondWith(executeStrategy(request, 'cacheFirst'));
  } else if (isAPIRequest(url)) {
    event.respondWith(executeStrategy(request, 'networkFirst'));
  } else {
    event.respondWith(executeStrategy(request, '${cacheStrategy}'));
  }
});

// Cache strategies
async function executeStrategy(request, strategy) {
  switch (strategy) {
    case 'cacheFirst':
      return cacheFirst(request);
    case 'networkFirst':
      return networkFirst(request);
    case 'staleWhileRevalidate':
      return staleWhileRevalidate(request);
    default:
      return fetch(request);
  }
}

async function cacheFirst(request) {
  try {
    const cache = await caches.open(STATIC_CACHE_NAME);
    const cachedResponse = await cache.match(request);
    
    if (cachedResponse && !isExpired(cachedResponse)) {
      return cachedResponse;
    }
    
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      await cache.put(request, networkResponse.clone());
      await limitCacheSize(cache);
    }
    return networkResponse;
  } catch (error) {
    const cache = await caches.open(STATIC_CACHE_NAME);
    const fallback = await cache.match(request);
    if (fallback) return fallback;
    
    if (request.destination === 'document') {
      return cache.match('/index.html') || new Response('Offline', { status: 503 });
    }
    throw error;
  }
}

async function networkFirst(request) {
  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      const cache = await caches.open(DYNAMIC_CACHE_NAME);
      await cache.put(request, networkResponse.clone());
      await limitCacheSize(cache);
    }
    return networkResponse;
  } catch (error) {
    const cache = await caches.open(DYNAMIC_CACHE_NAME);
    const cachedResponse = await cache.match(request);
    if (cachedResponse && !isExpired(cachedResponse)) {
      return cachedResponse;
    }
    throw error;
  }
}

async function staleWhileRevalidate(request) {
  const cache = await caches.open(DYNAMIC_CACHE_NAME);
  const cachedResponse = await cache.match(request);
  
  const fetchPromise = fetch(request).then(async (networkResponse) => {
    if (networkResponse.ok) {
      await cache.put(request, networkResponse.clone());
      await limitCacheSize(cache);
    }
    return networkResponse;
  }).catch(() => null);
  
  if (cachedResponse && !isExpired(cachedResponse)) {
    return cachedResponse;
  }
  
  return fetchPromise || cachedResponse || new Response('Offline', { status: 503 });
}

// Helper functions
function isStaticAsset(url) {
  const staticExtensions = ['.js', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf'];
  const pathname = url.pathname.toLowerCase();
  
  return staticExtensions.some(ext => pathname.endsWith(ext)) ||
         pathname.includes('/assets/') ||
         pathname.includes('/static/') ||
         STATIC_ASSETS.includes(pathname);
}

function isAPIRequest(url) {
  return API_ENDPOINTS.some(endpoint => url.pathname.startsWith(endpoint)) ||
         url.hostname !== location.hostname;
}

function isExpired(response) {
  const dateHeader = response.headers.get('date');
  if (!dateHeader) return false;
  
  const responseDate = new Date(dateHeader);
  const now = new Date();
  return (now.getTime() - responseDate.getTime()) > MAX_CACHE_AGE;
}

async function limitCacheSize(cache) {
  const requests = await cache.keys();
  if (requests.length > MAX_CACHE_SIZE) {
    // Remove oldest entries
    const sortedRequests = requests.sort((a, b) => {
      const aDate = new Date(a.headers.get('date') || 0);
      const bDate = new Date(b.headers.get('date') || 0);
      return aDate - bDate;
    });
    
    const toDelete = sortedRequests.slice(0, requests.length - MAX_CACHE_SIZE);
    await Promise.all(toDelete.map(request => cache.delete(request)));
  }
}

async function getCacheSize() {
  const cacheNames = await caches.keys();
  let totalSize = 0;
  
  for (const cacheName of cacheNames) {
    const cache = await caches.open(cacheName);
    const requests = await cache.keys();
    totalSize += requests.length;
  }
  
  return totalSize;
}

async function clearAllCaches() {
  const cacheNames = await caches.keys();
  await Promise.all(cacheNames.map(name => caches.delete(name)));
}

// Message handling
self.addEventListener('message', (event) => {
  const { type, data } = event.data;
  
  switch (type) {
    case 'SKIP_WAITING':
      self.skipWaiting();
      break;
      
    case 'GET_CACHE_SIZE':
      getCacheSize().then(size => {
        event.ports[0].postMessage({ type: 'CACHE_SIZE', size });
      });
      break;
      
    case 'CLEAR_CACHE':
      clearAllCaches().then(() => {
        event.ports[0].postMessage({ type: 'CACHE_CLEARED' });
      });
      break;
      
    case 'CACHE_URLS':
      if (data && data.urls) {
        cacheUrls(data.urls).then(() => {
          event.ports[0].postMessage({ type: 'URLS_CACHED' });
        });
      }
      break;
  }
});

async function cacheUrls(urls) {
  const cache = await caches.open(DYNAMIC_CACHE_NAME);
  
  for (const url of urls) {
    try {
      const response = await fetch(url);
      if (response.ok) {
        await cache.put(url, response);
      }
    } catch (error) {
      console.error('[SW] Failed to cache:', url, error);
    }
  }
}

// Handle iframe-specific concerns
self.addEventListener('fetch', (event) => {
  const request = event.request;
  const url = new URL(request.url);
  
  // Add headers to support iframe usage
  if (request.destination === 'document' || request.destination === 'iframe') {
    event.respondWith(
      fetch(request).then(response => {
        if (response.ok && response.headers.get('content-type')?.includes('text/html')) {
          const headers = new Headers(response.headers);
          headers.set('X-Frame-Options', 'ALLOWALL');
          headers.delete('X-Frame-Options'); // Remove restrictive frame options
          
          return new Response(response.body, {
            status: response.status,
            statusText: response.statusText,
            headers: headers
          });
        }
        return response;
      }).catch(() => {
        // Fallback to cached version for iframe
        return caches.match(request) || 
               caches.match('/index.html') ||
               new Response('Offline', { status: 503 });
      })
    );
  }
});

console.log('[SW] Service worker loaded and ready for iframe usage');`;
};

console.log('🔧 Generating optimized service worker...');

const staticAssets = getAllStaticAssets();

// Generate service worker with production configuration
const serviceWorkerCode = generateServiceWorkerForApp({
  cacheName: 'cpw-spoof',
  version: `v${version}`,
  staticAssets: staticAssets,
  apiEndpoints: ['/api/', '/graphql'],
  cacheStrategy: 'staleWhileRevalidate',
  maxCacheAge: 7 * 24 * 60 * 60 * 1000, // 7 days
  maxCacheSize: 100,
  customStrategies: {
    '/assets/': 'cacheFirst',
    '/images/': 'cacheFirst',
    '/fonts/': 'cacheFirst',
  }
});

// Write the generated service worker to the dist directory (for production)
const outputPath = join(__dirname, '../dist/sw.js');
writeFileSync(outputPath, serviceWorkerCode, 'utf8');

console.log(`✅ Service worker generated successfully at ${outputPath}`);
console.log(`📦 Version: v${version}`);
console.log(`🎯 Cache name: cpw-spoof-v${version}`);
console.log(`📁 Static assets cached: ${getAllStaticAssets().length} files`);
console.log('\nTo use this service worker:');
console.log('1. Build your application: npm run build');
console.log('2. Serve the built files over HTTPS');
console.log('3. The service worker will automatically register and cache assets');

// Optional: Generate a manifest for PWA
const manifest = {
  name: 'CPW Spoof Application',
  short_name: 'CPW Spoof',
  description: 'Interactive iframe application with offline support',
  start_url: '/',
  display: 'standalone',
  background_color: '#242424',
  theme_color: '#646cff',
  icons: [
    {
      src: '/poppulo-logo.svg',
      sizes: 'any',
      type: 'image/svg+xml'
    }
  ]
};

const manifestPath = join(__dirname, '../dist/manifest.json');
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
console.log(`📱 PWA manifest generated at ${manifestPath}`);

console.log('\n🚀 Ready for offline usage!');
