import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'

import { clientsClaim, setCacheNameDetails, skipWaiting } from 'workbox-core'

import {
  NetworkFirst,
  NetworkOnly,
  StaleWhileRevalidate,
  CacheFirst,
} from 'workbox-strategies'

// Used for filtering matches based on status code, header, or both
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
// Used to limit entries in cache, remove entries after a certain period of time
import { ExpirationPlugin } from 'workbox-expiration'

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

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

importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js');

// importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0/workbox-sw.js');

// importScripts('./ChabokSDKWorker.js', 'https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');

let port = 3000
if (self.location.hostname.startsWith('test')) {
  port = 3001
}
console.log('SW-06 1')

const cfgenv = {
  serverweb: `${self.location.protocol}//${self.location.hostname}:${port}`,
  dbname: 'mydb3',
  dbversion: 11,
}

// console.log('serverweb', cfgenv.serverweb)

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

async function readAllData (table) {
  // console.log('readAllData', table);
  return idbKeyval.getalldata(table)
}

async function clearAllData (table) {
  // console.log('clearAllData', table);
  await idbKeyval.clearalldata(table)
}

async function deleteItemFromData (table, id) {
  // console.log('deleteItemFromData', table, 'ID:', id);

  await idbKeyval.deletedata(table, id)
}

// self.addEventListener('activate', function(event) {
//   event.waitUntil(
//     // createDB()
//   );
// });

if (!workbox) {
  const workbox = new self.WorkboxSW()
}

if (workbox) {
  const debug = false
  workbox.setConfig({ debug })

  const precacheList = self.__WB_MANIFEST || [];
  setCacheNameDetails({
    prefix: self.location.hostname,
    suffix: 'v1',
    precache: 'precache',
    runtime: 'runtime'
  })

  // clientsClaim()
  // skipWaiting()

  precacheAndRoute(precacheList)
  // cleanupOutdatedCaches()

  registerRoute(
    new RegExp(/\.(?:png|gif|jpg|jpeg)$/),
    new CacheFirst({
      cacheName: 'images',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxEntries: 60,
          maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
        }),
      ],
    }),
  )
  registerRoute(
    new RegExp(/\.(?:svg)$/),
    new CacheFirst({
      cacheName: 'svg',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxEntries: 60,
          maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
        }),
      ],
    }),
  )

  // Per Articoli....
  const articleHandler = new NetworkFirst({
    cacheName: 'articles-cache',
    plugins: [
      // Ensure that only requests that result in a 200 status are cached
      new CacheableResponsePlugin({
        statuses: [200],
      }),
      new ExpirationPlugin({
        maxEntries: 50,
      }),
    ],
  })

  registerRoute(
    new RegExp(/(.*)article(.*)\.html/), args => articleHandler.handle(args),
  )

  registerRoute(
    new RegExp(/.*(?:googleapis|gstatic)\.com.*$/),
    new StaleWhileRevalidate({
      cacheName: 'google-fonts',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxEntries: 30,
        }),
      ],
    }),
  )

  // console.log('  routing.registerRoute function declaration:')

  function Execute_Fetch (table, args) {
    console.log('Execute_Fetch registerRoute! ',
      `${cfgenv.serverweb}/${table}/`)
    // console.log('DATABODY:', args.event.request.body)
    let myres = null
    // return fetch(args.event.request, args.event.headers)
    return fetch(args.event.request, args.event.headers)
      .then((res) => {
        myres = res
        if (res.status === 200) {
          const clonedRes = res.clone()

          let secondatab = ''
          if (table === 'todos') {
            secondatab = 'categories'
          }
          console.log('1) clearAllData: ', table)
          return clearAllData(table)
            .then(() => {
              if (secondatab !== '') {
                // console.log('2) clearAllData(todos)')
                return clearAllData(secondatab)
                  .then(() =>
                    // console.log('3)  ....return clonedRes')
                    clonedRes)
              }
              return clonedRes
            })
        }
      })
      .then((clonedRes) => {
        // console.log('  3) ')
        if (clonedRes) return clonedRes.json()
        return null
      })
      .then(data => {
        // console.log('  4) data = ', data)
        if (data) {
          const myarr = idbKeyval.getArrayByTable(table, data)
          if (myarr) {
            let promiseChain = Promise.resolve()

            console.log('*********+++++++++++++++++**********    Records ',
              `${table} Received from Server [`, myarr.length, 'record]', myarr)

            if (table === 'todos') {
              for (const cat in data.categories) {
                promiseChain = promiseChain.then(() => writeData('categories', {
                  _id: cat,
                  valore: data.categories[cat],
                }))
              }

              for (const arrsing of myarr) {
                for (const rec of arrsing) {
                  promiseChain = promiseChain.then(() => writeData(table, rec))
                }
              }
            } else {
              // Others tables
              for (const rec of myarr) {
                promiseChain = promiseChain.then(() => writeData(table, rec))
              }
            }

            // console.log('promiseChain', promiseChain)

            return promiseChain
          }
        }
      })
      .then(() => myres)
      .catch(err => {
        console.log('ERROR registerRoute FETCH:', err)
        return myres
      })
  }

  for (const table of MainTables) {
    registerRoute(
      new RegExp(`${cfgenv.serverweb}/${table}/`),
      (args) => {
        Execute_Fetch(table, args)
      },
    )
  }

  registerRoute(
    (routeData) => (routeData.event.request.headers.get('accept')
      .includes('text/html')), (args) => caches.match(args.event.request)
      .then((response) => {
        if (response) {
          return response
        }
        return fetch(args.event.request)
          .then((res) => caches.open('dynamic')
            .then((cache) => {
              cache.put(args.event.request.url, res.clone())
              return res
            }))
          .catch((err) => caches.match('/offline')
            .then((res) => res))
      }),
  )

  registerRoute(
    new RegExp(/.*\/(?:icons).*$/),
    new CacheFirst({
      cacheName: 'icon-cache',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxAgeSeconds: 30 * 24 * 60 * 60,
        }),
      ],
    }),
  )

  registerRoute(
    new RegExp(/\.(?:js|css|font)$/),
    new StaleWhileRevalidate({
      cacheName: 'js-css-fonts',
      // Ensure that only requests that result in a 200 status are cached
      plugins: [
        new CacheableResponsePlugin({
          statuses: [200],
        }),
      ],
    }),
  )

  // Storage
  registerRoute(
    new RegExp(/.*(?:storage)/),
    new StaleWhileRevalidate({
      cacheName: 'storage',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxAgeSeconds: 30 * 24 * 60 * 60,
          // Only cache 10 requests.
          maxEntries: 200,
        }),
      ],
    }),
  )

  registerRoute(
    new RegExp(/.*\/(?:statics).*$/),
    new CacheFirst({
      cacheName: 'statics',
      plugins: [
        // Ensure that only requests that result in a 200 status are cached
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxAgeSeconds: 10 * 24 * 60 * 60,
          // Only cache 10 requests.
        }),
      ],
    }),
  )

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

  /*registerRoute(
    new RegExp('/owa/'),
    new NetworkOnly(),
  )
   */

}

if ('serviceWorker' in navigator) {

  // console.log('*****************      Entering in custom-service-worker.js:')

}

// self.addEventListener('fetch', (event) => {
//   if (event.request.url === '/') {
//     const StaleWhileRevalidate = new StaleWhileRevalidate();
//     event.respondWith(StaleWhileRevalidate.handle({ event }));
//   }
// });

// self.addEventListener('fetch', function (event) {
//   console.log('[Service Worker] Fetching something ....', event);
//   console.log('event.request.cache=', event.request.cache)
//   if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
//     console.log('SAME ORIGIN!', event);
//     return;
//   }
//   event.respondWith(caches.match(event.request));
// });
//

// const syncStore = {}
// self.addEventListener('message', event => {
//   if (event.data.type === 'sync') {
//     // get a unique id to save the data
//     const id = uuid()
//     syncStore[id] = event.data
//     // register a sync and pass the id as tag for it to get the data
//     self.registration.sync.register(id)
//   }
//   console.log(event.data)
// })

// addEventListener('fetch', event => {
//   // Prevent the default, and handle the request ourselves.
//   event.respondWith(async function() {
//     // Try to get the response from a cache.
//     const cachedResponse = await caches.match(event.request);
//     // Return it if we found one.
//     if (cachedResponse && (event.request.cache !== 'no-cache'))
//       return cachedResponse;
//
//     // If we didn't find a match in the cache, use the network.
//     return fetch(event.request);
//   }());
// });

// self.addEventListener('fetch', function (event) {
//   event.respondWith(
//     caches.match(event.request).then(function (response) {
//       return response ||
//         fetch(event.request, event.headers)
//           .catch(err => {
//             console.log('_______________________ ERRORE FETCH SW: ', event.request, err)
//             writeData('config', { _id: 2, stateconn: 'offline' })
//             return caches.match(event.request);
//           })
//     })
//   );
// });

// self.addEventListener('fetch', function (event) {
//   event.respondWith(
//     fetch(event.request, event.headers)
//       .catch(err => {
//         console.log('_______________________ ERRORE FETCH SW: ', event.request, err)
//         writeData('config', {_id: 2, stateconn: 'offline'})
//         return caches.match(event.request);
//       })
//   );
// });

// self.addEventListener('sync', function (event) {
//   console.log('[Service Worker V5] Background syncing', event.tag);
//
//   let mystrparam = event.tag
//   let multiparams = mystrparam.split('|')
//   if (multiparams) {
//     if (multiparams.length > 3) {
//       let cmd = multiparams[0]
//       let table = multiparams[1]
//       let method = multiparams[2]
//       let token = multiparams[3]
//       // let lang = multiparams[3]
//
//       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)
//
//
//         // console.log('A1) INIZIO.............................................................');
//
//         event.waitUntil(
//           readAllData(table)
//             .then(function (alldata) {
//               const myrecs = [...alldata]
//               console.log('----------------------- LEGGO QUALCOSA DAL WAITUNTIL ')
//               let errorfromserver = false
//               if (myrecs) {
//                 for (let rec of myrecs) {
//                   //console.log('syncing', table, '', rec.descr)
//                   let link = cfgenv.serverweb + '/todos'
//
//                   if (method !== 'POST')
//                     link += '/' + rec._id
//
//                   console.log('++++++++++++++++++ SYNCING !!!!  ', rec.descr, table, 'FETCH: ', method, link, 'data:')
//
//                   // console.log('DATATOSAVE:', JSON.stringify(rec))
//
//                   // Insert/Delete/Update table to the server
//                   fetch(link, {
//                     method: method,
//                     headers: headers,
//                     cache: 'no-cache',
//                     mode: 'cors',   // 'no-cors',
//                     body: JSON.stringify(rec)
//                   })
//                     .then(() => {
//                       deleteItemFromData(table, rec._id)
//                     })
//                     .then(() => {
//                       deleteItemFromData('swmsg', mystrparam)
//                     })
//                     .catch(function (err) {
//                       console.log('!!!!!!!!!!!!!!!   Error while sending data', err, err.message);
//                       if (err.message === 'Failed to fetch') {
//                         errorfromserver = true
//                       }
//                     })
//                 }
//                 return errorfromserver
//               }
//             })
//             .then((errorfromserver) => {
//               const mystate = !errorfromserver ? 'online' : 'offline'
//               writeData('config', { _id: 2, stateconn: mystate })
//             })
//         );
//         // console.log('A2) ?????????????????????????? ESCO DAL LOOP !!!!!!!!! err=')
//       }
//     }
//   }
// })
// ;

/*

// send message to serviceWorker
function sync (url, options) {
  navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})
}

const syncStore = {}
self.addEventListener('message', event => {
  if(event.data.type === 'sync') {
    // get a unique id to save the data
    const id = uuid()
    syncStore[id] = event.data
    // register a sync and pass the id as tag for it to get the data
    self.registration.sync.register(id)
  }
  console.log(event.data)
})

self.addEventListener('sync', event => {
  // get the data by tag
  const {url, options} = syncStore[event.tag]
  event.waitUntil(fetch(url, options))
})
*/

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

  console.log(notification)

  if (action === 'confirm') {
    console.log('Confirm was chosen')
    notification.close()
  } else {
    console.log(action)
    event.waitUntil(
      clients.matchAll()
        .then((clis) => {
          const client = clis.find((c) => c.visibilityState === 'visible')

          if (client) {
            client.navigate(notification.data.url)
            client.focus()
          } else {
            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 = {
    title: 'New!',
    content: 'Something new happened!',
    url: '/',
  }

  try {
    if (event.data) {
      try {
        data = JSON.parse(event.data.text())
      } catch (e) {
        data = event.data.text()
      }
    }

    const options = {
      body: data.content,
      icon: '/images/android-chrome-192x192.png',
      badge: '/images/android-chrome-192x192.png',
      data: {
        url: data.url,
      },
      tag: 'received',
      renitify: true, // vibrate also with others messages.
    }

    event.waitUntil(
      self.registration.showNotification(data.title, options),
    )
  } catch (e) {
    console.log('Error on event push:', e)
  }
})
