console.log('[SW] Push worker started')

var pendingNotification = false

var defaultBaseUrl
switch (location.hostname) {
  case 'localhost':
    defaultBaseUrl = 'http://localhost:8000/app/index.html#/im'
    break
  case 'zhukov.github.io':
    defaultBaseUrl = 'https://zhukov.github.io/webogram/#/im'
    break
  default:
  case 'web.telegram.org':
    defaultBaseUrl = 'https://' + location.hostname + '/#/im'
}

self.addEventListener('push', function(event) {
  var obj = event.data.json()
  console.log('[SW] push', obj)

  var hasActiveWindows = false
  var checksPromise = new Promise(function (resolve, reject) {
    var nowTime = +(new Date())
    Promise.all([getMuteUntil(), getLastAliveTime()]).then(function (result) {
      var muteUntil = result[0]
      var lastAliveTime = result[1]
      return clients.matchAll({type: 'window'}).then(function(clientList) {
        console.log('matched clients', clientList)
        hasActiveWindows = clientList.length > 0
        if (hasActiveWindows) {
          console.log('Supress notification because some instance is alive')
          return reject()
        }
        if (userInvisibleIsSupported() &&
            muteUntil &&
            nowTime < muteUntil) {
          console.log('Supress notification because mute for ', Math.ceil((muteUntil - nowTime) / 60000), 'min')
          return reject()
        }
        if (!obj.badge) {
          return reject()
        }
        return resolve()
      })
    })
  })

  var notificationPromise = checksPromise.then(function () {
    return Promise.all([getSettings(), getLang()]).then(function (result) {
      return fireNotification(obj, result[0], result[1])
    })
  })

  var closePromise = notificationPromise.catch(function () {
    console.log('[SW] Closing all notifications on push', hasActiveWindows)
    if (userInvisibleIsSupported() || hasActiveWindows) {
      return closeAllNotifications()
    }
    return self.registration.showNotification('Telegram', {
      tag: 'unknown_peer'
    }).then(function () {
      if (hasActiveWindows) {
        return closeAllNotifications()
      }
      setTimeout(closeAllNotifications, hasActiveWindows ? 0 : 100)
    }).catch(function (error) {
      console.error('Show notification error', error)
    })
  })

  event.waitUntil(closePromise)
})

self.addEventListener('install', function(event) {
  event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', function(event) {
  console.log('[SW] on activate')
  event.waitUntil(self.clients.claim());
});


self.addEventListener('message', function(event) {
//   console.log('[SW] on message', event.data)
  var client = event.ports && event.ports[0] || event.source
  if (event.data.type == 'ping') {
    if (event.data.localNotifications) {
      lastAliveTime = +(new Date())
      IDBManager.setItem('push_last_alive', lastAliveTime)
    }

    if (pendingNotification &&
        client &&
        'postMessage' in client) {
      client.postMessage(pendingNotification)
      pendingNotification = false
    }

    if (event.data.lang) {
      lang = event.data.lang
      IDBManager.setItem('push_lang', lang)
    }
    if (event.data.settings) {
      settings = event.data.settings
      IDBManager.setItem('push_settings', settings)
    }
  }
  if (event.data.type == 'notifications_clear') {
    closeAllNotifications()
  }
})

function fireNotification(obj, settings, lang) {
  var title = obj.title || 'Telegram'
  var body = obj.description || ''
  var icon = 'img/logo_share.png'
  var peerID

  if (obj.custom && obj.custom.channel_id) {
    peerID = -obj.custom.channel_id
  }
  else if (obj.custom && obj.custom.chat_id) {
    peerID = -obj.custom.chat_id
  }
  else {
    peerID = obj.custom && obj.custom.from_id || 0
  }
  obj.custom.peerID = peerID
  var tag = 'peer' + peerID

  if (settings && settings.nopreview) {
    title = 'Telegram'
    body = lang.push_message_nopreview || 'You have a new message'
    tag = 'unknown_peer'
  }

  console.log('[SW] show notify', title, body, icon, obj)

  var notificationPromise = self.registration.showNotification(title, {
    body: body,
    icon: icon,
    tag: tag,
    data: obj,
    actions: [
      {
        action: 'mute1d',
        title: lang.push_action_mute1d || 'Mute for 24H'
      },
      {
        action: 'push_settings',
        title: lang.push_action_settings || 'Settings'
      }
    ]
  })

  return notificationPromise.then(function (event) {
    if (event && event.notification) {
      pushToNotifications(event.notification)
    }
    return Promise.resolve()
  }).catch(function (error) {
    console.error('Show notification promise', error)
  })
}


var notifications = []
function pushToNotifications(notification) {
  if (notifications.indexOf(notification) == -1) {
    notifications.push(notification)
    notification.onclose = onCloseNotification
  }
}

function onCloseNotification(event) {
  removeFromNotifications(event.notification)
}

function removeFromNotifications(notification) {
  var pos = notifications.indexOf(notification)
  if (pos != -1) {
    notifications.splice(pos, 1)
  }
}

function closeAllNotifications() {
  for (var i = 0, len = notifications.length; i < len; i++) {
    try {
      notifications[i].close()
    } catch (e) {}
  }

  var promise
  if ('getNotifications' in self.registration) {
    promise = self.registration.getNotifications({}).then(function(notifications) {
      for (var i = 0, len = notifications.length; i < len; i++) {
        try {
          notifications[i].close()
        } catch (e) {}
      }
    }).catch(function (error) {
      console.error('Offline register SW error', error)
    })
  } else {
    promise = Promise.resolve()
  }

  notifications = []

  return promise
}


self.addEventListener('notificationclick', function(event) {
  var notification = event.notification
  console.log('On notification click: ', notification.tag)
  notification.close()

  var action = event.action
  if (action == 'mute1d' && userInvisibleIsSupported()) {
    console.log('[SW] mute for 1d')
    muteUntil = +(new Date()) + 86400000
    IDBManager.setItem('push_mute_until', muteUntil)
    return
  }
  if (!notification.data) {
    return
  }

  var promise = clients.matchAll({
    type: 'window'
  }).then(function(clientList) {
    notification.data.action = action
    pendingNotification = {type: 'push_click', data: notification.data}
    for (var i = 0; i < clientList.length; i++) {
      var client = clientList[i]
      if ('focus' in client) {
        client.focus()
        client.postMessage(pendingNotification)
        pendingNotification = false
        return
      }
    }
    if (clients.openWindow) {
      return getSettings().then(function (settings) {
        return clients.openWindow(settings.baseUrl || defaultBaseUrl)
      })
    }
  }).catch(function (error) {
    console.error('Clients.matchAll error', error)
  })

  event.waitUntil(promise)
})

self.addEventListener('notificationclose', onCloseNotification)




;(function () {
  var dbName = 'keyvalue'
  var dbStoreName = 'kvItems'
  var dbVersion = 2
  var openDbPromise
  var idbIsAvailable = self.indexedDB !== undefined &&
    self.IDBTransaction !== undefined

  function isAvailable () {
    return idbIsAvailable
  }

  function openDatabase () {
    if (openDbPromise) {
      return openDbPromise
    }

    return openDbPromise = new Promise(function (resolve, reject) {
      try {
        var request = indexedDB.open(dbName, dbVersion)
        var createObjectStore = function (db) {
          db.createObjectStore(dbStoreName)
        }
        if (!request) {
          return reject()
        }
      } catch (error) {
        console.error('error opening db', error.message)
        idbIsAvailable = false
        return reject(error)
      }

      var finished = false
      setTimeout(function () {
        if (!finished) {
          request.onerror({type: 'IDB_CREATE_TIMEOUT'})
        }
      }, 3000)

      request.onsuccess = function (event) {
        finished = true
        var db = request.result

        db.onerror = function (error) {
          idbIsAvailable = false
          console.error('Error creating/accessing IndexedDB database', error)
          reject(error)
        }

        resolve(db)
      }

      request.onerror = function (event) {
        finished = true
        idbIsAvailable = false
        console.error('Error creating/accessing IndexedDB database', event)
        reject(event)
      }

      request.onupgradeneeded = function (event) {
        finished = true
        console.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion)
        var db = event.target.result
        if (event.oldVersion == 1) {
          db.deleteObjectStore(dbStoreName)
        }
        createObjectStore(db)
      }
    })
  }

  function setItem (key, value) {
    return openDatabase().then(function (db) {
      try {
        var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName)
        var request = objectStore.put(value, key)
      } catch (error) {
        idbIsAvailable = false
        return Promise.reject(error)
      }

      return new Promise(function(resolve, reject) {
        request.onsuccess = function (event) {
          resolve(value)
        }

        request.onerror = function (error) {
          reject(error)
        }
      })
    })
  }

  function getItem (key) {
    return openDatabase().then(function (db) {
      return new Promise(function(resolve, reject) {
        var objectStore = db.transaction([dbStoreName], IDBTransaction.READ || 'readonly').objectStore(dbStoreName)
        var request = objectStore.get(key)

        request.onsuccess = function (event) {
          var result = event.target.result
          if (result === undefined) {
            resolve()
          } else {
            resolve(result)
          }
        }

        request.onerror = function (error) {
          reject(error)
        }
      })
      
    })
  }

  openDatabase()

  self.IDBManager = {
    name: 'IndexedDB',
    isAvailable: isAvailable,
    setItem: setItem,
    getItem: getItem
  }
})()


var lastAliveTime, muteUntil, settings, lang

function getMuteUntil() {
  if (muteUntil !== undefined) {
    return Promise.resolve(muteUntil)
  }
  return IDBManager.getItem('push_mute_until').then(function (newMuteUntil) {
    return muteUntil = Math.max(muteUntil || 0, newMuteUntil || 0) || false
  }).catch(function (error) {
    console.error('IDB error', error)
    return false
  })
}

function getLastAliveTime() {
  if (lastAliveTime !== undefined) {
    return Promise.resolve(lastAliveTime)
  }
  return IDBManager.getItem('push_last_alive').then(function (newLastAliveTime) {
    return lastAliveTime = Math.max(lastAliveTime || 0, newLastAliveTime || 0) || false
  }).catch(function (error) {
    console.error('IDB error', error)
    return false
  })
}

function getLang() {
  if (lang !== undefined) {
    return Promise.resolve(lang)
  }
  return IDBManager.getItem('push_lang').then(function (newLang) {
    return lang = newLang || {}
  }).catch(function (error) {
    console.error('IDB error', error)
    return {}
  })
}

function getSettings() {
  if (settings !== undefined) {
    return Promise.resolve(settings)
  }
  return IDBManager.getItem('push_settings').then(function (newSettings) {
    return settings = newSettings || {}
  }).catch(function (error) {
    console.error('IDB error', error)
    return {}
  })
}

function userInvisibleIsSupported() {
  var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
  return isFirefox ? true : false
}
