/* global importScripts */
/* global idbKeyval */
/* global workbox */
/* global cfgenv */

const VITE_APP_VERSION = "1.2.40";

// Costanti di configurazione
const DYNAMIC_CACHE = 'dynamic-cache-v2';
const baseUrl = self.location.origin;

function extractDomain(url) {
  return url.replace(/^https?:\/\//, '');
}

function removeTestPrefix(str) {
  return str.startsWith('test.') ? str.slice(5) : str;
}

// Funzione per determinare il dominio API
function determineApiDomain(appDomain) {
  if (ISTEST) {
    return 'testapi.' + removeTestPrefix(appDomain);
  }
  return appDomain.includes('localhost') ? 'localhost:3000' : 'api.' + appDomain;
}

const APP_DOMAIN = extractDomain(baseUrl);
const API_DOMAIN = determineApiDomain(APP_DOMAIN);

console.log('API_DOMAIN', API_DOMAIN);

const CACHE_NAME = 'pwa-cache-' + VITE_APP_VERSION; // Nome della cache

importScripts('workbox/workbox-sw.js')

import { clientsClaim } from 'workbox-core'
import { precacheAndRoute, cleanupOutdatedCaches, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute, NavigationRoute } from 'workbox-routing';
import { setCacheNameDetails } from 'workbox-core';
import {
  NetworkOnly,
  NetworkFirst,
  StaleWhileRevalidate,
  CacheFirst,
} from 'workbox-strategies';

import { CacheableResponsePlugin } from 'workbox-cacheable-response';
import { ExpirationPlugin } from 'workbox-expiration';

const debug = false;  //process.env.NODE_ENV !== 'production';

if (workbox) {

  // Imposta configurazione prima di tutto
  workbox.setConfig({ debug });

  workbox.loadModule('workbox-strategies');

  console.log('Workbox ESISTE ✅ ');

} else {
  console.error('Workbox NON CARICATO ! ❌');
}


setCacheNameDetails({
  prefix: self.location.hostname,
  suffix: 'v2',
  precache: 'precache',
  runtime: 'runtime',
});

const precacheList = (self.__WB_MANIFEST || []).filter(entry => {
  // Esclude tutto ciò che si trova nella cartella 'upload'
  if (entry.url.includes('/upload/')) {
    return false;
  }
  return true;
});

// Precache solo i file filtrati
precacheAndRoute(precacheList);

cleanupOutdatedCaches()


// Installazione del Service Worker
self.addEventListener('install', () => {
  console.log('[Service Worker] Installing ...');
  self.skipWaiting()
  clientsClaim();
});

// Attivazione del Service Worker
self.addEventListener('activate', (event) => {
  console.log('[Service Worker] Activating...');
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter(name => name !== CACHE_NAME && name !== DYNAMIC_CACHE)
          .map((name) => caches.delete(name))
      );
    })
  );
});


console.log('   [  VER-' + VITE_APP_VERSION + ' ] _---------________------  PAO: this is my custom service worker: ');

try {
  importScripts('/js/idb.js', '/js/storage.js');
  console.log('Local scripts imported successfully.');
} catch (error) {
  console.error('Failed to import local scripts ❌:', error);
}

let port = self.location.hostname.startsWith('test') ? 3001 : 3000;
let ISTEST = self.location.hostname.startsWith('test');
let ISLOCALE = self.location.hostname.startsWith('localhost');
console.log('SW- app ver ' + VITE_APP_VERSION);


// Function helpers
async function writeData(table, data) {
  console.log('writeData', table, data);
  await idbKeyval.setdata(table, data);
}

async function readAllData(table) {
  return idbKeyval.getalldata(table);
}

async function clearAllData(table) {
  await idbKeyval.clearalldata(table);
}

async function deleteItemFromData(table, id) {
  await idbKeyval.deletedata(table, id);
}

if (workbox) {


  /*if (process.env.MODE !== 'ssr' || process.env.PROD) {
    registerRoute(
      new NavigationRoute(
        createHandlerBoundToURL(process.env.PWA_FALLBACK_HTML),
        { denylist: [new RegExp(process.env.PWA_SERVICE_WORKER_REGEX), /workbox\workbox-(.)*\.js$/] }
      )
    )
  }*/

  // Gestione richieste statiche
  registerRoute(
    ({ request }) => request.destination === 'document' || request.destination === 'script' || request.destination === 'style',
    new StaleWhileRevalidate({ cacheName: CACHE_NAME })
  );

  // Cache strategy registrations
  registerRoute(
    new RegExp(/\.(?:png|gif|jpg|jpeg)$/),
    new CacheFirst({
      cacheName: `images-upload-${VITE_APP_VERSION}`,
      plugins: [
        new CacheableResponsePlugin({ statuses: [200] }),
        new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days
      ],
    })
  );

  registerRoute(
    new RegExp(/\.(?:svg)$/),
    new CacheFirst({
      cacheName: `svg-${VITE_APP_VERSION}`,
      plugins: [
        new CacheableResponsePlugin({ statuses: [200] }),
        new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days
      ],
    })
  );

  registerRoute(
    new RegExp(/.*(?:googleapis|gstatic)\.com.*$/),
    new StaleWhileRevalidate({
      cacheName: `google-fonts-${VITE_APP_VERSION}`,
      plugins: [
        new CacheableResponsePlugin({ statuses: [200] }),
        new ExpirationPlugin({ maxEntries: 30 }),
      ],
    })
  );

  /*registerRoute(
    new RegExp('^/myicons/.*$'), // Corrisponde a percorsi come "/icons/miaicona.ico"
    new CacheFirst({
      cacheName: `icon-cache-${VITE_APP_VERSION}`,
      plugins: [
        new CacheableResponsePlugin({ statuses: [200] }),
        new ExpirationPlugin({ maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days
      ],
    })
  );*/

  registerRoute(
    new RegExp(/\.(?:js|css|font)$/),
    new StaleWhileRevalidate({
      cacheName: `js-css-fonts-${VITE_APP_VERSION}`,
      plugins: [
        new CacheableResponsePlugin({ statuses: [200] }),
      ],
    })
  );

  // Gestione API
  registerRoute(
    ({ url }) => url.hostname === API_DOMAIN,
    new NetworkFirst({ fetchOptions: { credentials: 'include' } })
  );

  /*registerRoute(
    (routeData) => routeData.event.request.headers.get('accept').includes('text/html'),
    async (args) => {
      let response = await caches.match(args.event.request);
      if (response) return response;

      try {
        response = await fetch(args.event.request);
        const cache = await caches.open('dynamic');
        cache.put(args.event.request.url, response.clone());
        return response;
      } catch (err) {
        return caches.match('/offline');
      }
    }
  );*/

  registerRoute(new RegExp('/admin/'), new NetworkOnly());

  function generateUUID() {
    return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
      (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
    );
  }


  const syncStore = {};
  self.addEventListener('message', event => {
    if (event.data && (event.data.type === 'SKIP_WAITING' || event.data.action === 'skipWaiting')) {
      self.skipWaiting();
    }
    if (event.data.type === 'sync') {
      console.log('addEventListener - message');
      const id = generateUUID();
      syncStore[id] = event.data;
      self.registration.sync.register(id);
    }
    console.log(event.data);
  });



  // Funzione per gestire richieste API
  async function handleApiRequest(request) {
    try {
      const response = await fetch(request);

      // Se la risposta non è valida, restituisci un errore personalizzato
      if (!response.ok) {
        console.warn('[SW] API Response Error:', response.status, response.statusText);
        return new Response(JSON.stringify({
          error: 'API error',
          message: `❌ Invalid response from API: ${response.status} ${response.statusText}`,
        }), { status: response.status, headers: { 'Content-Type': 'application/json' } });
      }

      return response;
    } catch (error) {
      console.error('[Service Worker] API request error ❌:', error);

      // Restituisci una risposta di errore personalizzata
      return new Response(JSON.stringify({
        error: 'Network error',
        message: '❌ Unable to fetch from API: ' + error.message,
      }), { status: 503, headers: { 'Content-Type': 'application/json' } });
    }
  }

  // Funzione per effettuare una richiesta di rete e memorizzare nella cache
  async function fetchAndCache(request) {
    const cache = await caches.open(DYNAMIC_CACHE);
    try {
      const response = await fetch(request);

      // Clona e salva la risposta nella cache solo se valida
      if (response.ok) {
        const responseClone = response.clone();
        cache.put(request, responseClone);
      }

      return response;
    } catch (error) {
      console.error('[SW] Fetch and cache error ❌:', error);
      throw error;
    }
  }

  // Strategia di caching: stale-while-revalidate
  async function cacheWithStaleWhileRevalidate(request, event) {
    const cache = await caches.open(DYNAMIC_CACHE);

    // Prova a recuperare la risorsa dalla cache
    const cachedResponse = await cache.match(request);
    if (cachedResponse) {
      // Aggiorna in background mentre restituisci la risposta in cache
      event.waitUntil(fetchAndCache(request).catch((error) => {
        console.error('[SW] Background fetch and cache error ❌:', error);
      }));
      return cachedResponse;
    }

    // Se non è in cache, fai la richiesta di rete
    try {
      return await fetchAndCache(request);
    } catch (error) {
      console.error('[SW] Cache miss and network error ❌:', error);

      // Restituisci una risposta di fallback personalizzata
      return new Response(JSON.stringify({
        error: 'Network error',
        message: 'Unable to fetch resource from network or cache.',
      }), { status: 503, headers: { 'Content-Type': 'application/json' } });
    }
  }

  // Listener per gestire tutte le richieste
  self.addEventListener('fetch', (event) => {
    const { request } = event;
    const url = new URL(request.url);
    try {

      // Ignora richieste non gestibili
      if (request.method !== 'GET' || url.protocol !== 'https:') {
        return;
      }

      // Ignora richieste per file di sviluppo (es. /src/)
      if (url.pathname.startsWith('/src/') || url.search.includes('vue&type')) {
        return;
      }

      // Gestione richieste API
      if (url.hostname === API_DOMAIN) {
        if (debug) console.log('E\' una RICHIESTA API!');
        event.respondWith(handleApiRequest(request));
        return;
      }

      // Gestione risorse statiche e altre richieste
      if (debug) console.log('E\' una RICHIESTA statica...');
      event.respondWith(cacheWithStaleWhileRevalidate(request, event));
    } catch (error) {
      console.error('[Service Worker] Fetch error ❌:', error);
    }
  });

  // Gestione degli errori non catturati
  self.addEventListener('unhandledrejection', event => {
    console.error('[Service Worker] Unhandled rejection ❌:', event.reason);
  });

  // Gestione degli errori globali
  self.addEventListener('error', event => {
    console.error('[Service Worker] Global error ❌:', event.error);
  });

  // Funzione di utilità per il logging (decommentare se necessario)
  // function logFetchDetails(request) {
  //   console.log('[Service Worker] Fetching:', request.url);
  //   console.log('Cache mode:', request.cache);
  //   console.log('Request mode:', request.mode);
  // }


  self.addEventListener('sync', event => {
    console.log('[Service Worker V5] Background syncing', event);

    let mystrparam = event.tag;
    let multiparams = mystrparam.split('|');
    if (multiparams && multiparams.length > 3) {
      let [cmd, table, method, token, refreshToken] = multiparams;

      if (cmd === 'sync-todos') {
        console.log('[Service Worker] Syncing', cmd, table, method);

        const headers = new Headers();
        headers.append('content-Type', 'application/json');
        headers.append('Accept', 'application/json');
        headers.append('x-auth', token);
        headers.append('x-refrtok', refreshToken);

        event.waitUntil(
          readAllData(table).then(alldata => {
            const myrecs = [...alldata];
            let errorfromserver = false;

            if (myrecs) {
              let promiseChain = Promise.resolve();

              for (let rec of myrecs) {
                //TODO: Risistemare con calma... per ora non penso venga usato...
                let link = cfgenv.serverweb + '/todos';
                if (method !== 'POST') link += '/' + rec._id;

                promiseChain = promiseChain.then(() =>
                  fetch(link, {
                    method: method,
                    headers: headers,
                    cache: 'no-cache',
                    mode: 'cors',
                    body: JSON.stringify(rec),
                  }).then(() => {
                    deleteItemFromData(table, rec._id);
                    deleteItemFromData('swmsg', mystrparam);
                  })
                    .catch(err => {
                      if (err.message === 'Failed to fetch') {
                        errorfromserver = true;
                      }
                    })
                );
              }

              return promiseChain.then(() => {
                const mystate = !errorfromserver ? 'online' : 'offline';
                writeData('config', { _id: 2, stateconn: mystate });
              });
            }
          })
        );
      }
    }
  });

  // Notifications
  self.addEventListener('notificationclick', (event) => {
    const { notification } = event;
    const { action } = event;

    if (action === 'confirm') {
      notification.close();
    } else {
      event.waitUntil(
        self.clients.matchAll().then((clis) => {
          const client = clis.find((c) => c.visibilityState === 'visible');
          if (client) {
            client.navigate(notification.data.url);
            client.focus();
          } else {
            self.clients.openWindow(notification.data.url);
          }
          notification.close();
        }),
      );
    }
  });

  self.addEventListener('notificationclose', (event) => {
    console.log('Notification was closed', event);
  });

  self.addEventListener('push', (event) => {
    console.log('Push Notification received', event);
    let data = event.data ? event.data.json() : { title: 'New!', content: 'Something new happened!', url: '/' };

    const options = {
      body: data.content,
      icon: data.icon ? data.icon : '/images/android-chrome-192x192.png',
      badge: data.badge ? data.badge : '/images/badge-96x96.png',
      data: { url: data.url },
      tag: data.tag,
    };

    event.waitUntil(self.registration.showNotification(data.title, options));

    const myid = data.id || '0';
    self.registration.sync.register(myid);
    writeData('notifications', { _id: myid, tag: options.tag });
  });
} else {
  console.warn('Workbox could not be loaded.');
}

console.log('***** FINE CUSTOM-SERVICE-WORKER.JS ***** ');
