import jwtDecode from 'jwt-decode'
import md5 from 'md5-hex'
import { v4 as uuidv4 } from 'uuid'
import { baseUrl } from './utils'
import config from './config'
import { startEventStream, stopEventStream } from './eventStream'

const authProvider = {
  login: ({ username, password }) => {
    let url = baseUrl('/app/login')
    if (config.firstTime) {
      url = baseUrl('/app/createAdmin')
    }
    const request = new Request(url, {
      method: 'POST',
      body: JSON.stringify({ username, password }),
      headers: new Headers({ 'Content-Type': 'application/json' }),
    })
    return fetch(request)
      .then((response) => {
        if (response.status < 200 || response.status >= 300) {
          throw new Error(response.statusText)
        }
        return response.json()
      })
      .then((response) => {
        // Validate token
        jwtDecode(response.token)
        localStorage.setItem('token', response.token)
        localStorage.setItem('name', response.name)
        localStorage.setItem('username', response.username)
        response.avatar && localStorage.setItem('avatar', response.avatar)
        localStorage.setItem('role', response.isAdmin ? 'admin' : 'regular')
        const salt = generateSubsonicSalt()
        localStorage.setItem('subsonic-salt', salt)
        localStorage.setItem(
          'subsonic-token',
          generateSubsonicToken(password, salt)
        )
        // Avoid going to create admin dialog after logout/login without a refresh
        config.firstTime = false
        if (config.devActivityPanel) {
          startEventStream()
        }
        return response
      })
      .catch((error) => {
        if (
          error.message === 'Failed to fetch' ||
          error.stack === 'TypeError: Failed to fetch'
        ) {
          throw new Error('errors.network_error')
        }

        throw new Error(error)
      })
  },

  logout: () => {
    stopEventStream()
    removeItems()
    clearServiceWorkerCache()
    return Promise.resolve()
  },

  checkAuth: () =>
    localStorage.getItem('token') ? Promise.resolve() : Promise.reject(),

  checkError: ({ status }) => {
    if (status === 401 || status === 403) {
      removeItems()
      return Promise.reject()
    }
    return Promise.resolve()
  },

  getPermissions: () => {
    const role = localStorage.getItem('role')
    return role ? Promise.resolve(role) : Promise.reject()
  },

  getIdentity: () => {
    return {
      id: localStorage.getItem('username'),
      fullName: localStorage.getItem('name'),
      avatar: localStorage.getItem('avatar'),
    }
  },
}

const removeItems = () => {
  localStorage.removeItem('token')
  localStorage.removeItem('name')
  localStorage.removeItem('username')
  localStorage.removeItem('avatar')
  localStorage.removeItem('role')
  localStorage.removeItem('subsonic-salt')
  localStorage.removeItem('subsonic-token')
}

const clearServiceWorkerCache = () => {
  window.caches &&
    caches.keys().then(function (keyList) {
      for (let key of keyList) caches.delete(key)
    })
}

const generateSubsonicSalt = () => {
  const h = md5(uuidv4())
  return h.slice(0, 6)
}

const generateSubsonicToken = (password, salt) => {
  return md5(password + salt)
}

export default authProvider
