import cookie from 'cookie'
import { fetch } from 'cross-fetch'
import config from './config'

/**
 * NOTE
 *
 * For this class to be used, make sure XSRF validation is disabled on the server.
 * In sogo.conf, you must have:
 *
 *   SOGoXSRFValidationEnabled = NO;
 */

class Preferences {
  constructor(un, pw) {
    this.username = un
    this.password = pw
    this.serverUrl = `http://${config.hostname}:${config.port}`
    this.cookie = null
    this.preferences = null
  }

  async getAuthCookie() {
    if (!this.cookie) {
      const resource = `/SOGo/connect`
      const response = await fetch(this.serverUrl + resource, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({userName: this.username, password: this.password})
      })
      if (response.status == 200) {
        const values = response.headers.get('set-cookie').split(/, /)
        let authCookies = []
        for (let v of values) {
          let c = cookie.parse(v)
          for (let authCookie of ['0xHIGHFLYxSOGo', 'XSRF-TOKEN']) {
            if (Object.keys(c).includes('0xHIGHFLYxSOGo')) {
              authCookies.push(cookie.serialize(authCookie, c[authCookie]))
            }
          }
        }
        this.cookie = authCookies.join('; ')
      }
      else
        throw new Error(`Can't authenticate with username ${this.username} (HTTP status code ${response.status})`)
    }
    return this.cookie
  }

  async getDefaults() {
    const resource = `/SOGo/so/${this.username}/jsonDefaults`
    const authCookie = await this.getAuthCookie()
    const response = await fetch(this.serverUrl + resource, {
      method: 'GET',
      headers: {
        Cookie: authCookie,
        ...this.headers
      }
    })
    if (response.status == 200) {
      const defaults = await response.json()
      return defaults
    }
    else
      throw new Error(`Can't fetch defaults of user ${this.username}: ${response.status}`)
  }

  async getSettings() {
    const resource = `/SOGo/so/${this.username}/jsonSettings`
    const authCookie = await this.getAuthCookie()
    const response = await fetch(this.serverUrl + resource, {
      method: 'GET',
      headers: {
        Cookie: authCookie,
        ...this.headers
      }
    })
    if (response.status == 200) {
      const settings = await response.json()
      return settings
    }
    else
      throw new Error(`Can't fetch settings of user ${this.username}: ${response.status}`)
  }

  async loadPreferences() {
    const defaults = await this.getDefaults()
    const settings = await this.getSettings()

    this.preferences = { defaults, settings }
  }

  findKey(obj, key) {
    if (Object.keys(obj).includes(key)) {
      return obj
    }
    for (let k of Object.keys(obj)) {
      if (obj[k] && typeof obj[k] == 'object') {
        let o = this.findKey(obj[k], key)
        if (o !== null)
          return o
      }
    }
    return null
  }

  async get(preference, withCache = true) {
    if (!withCache || !this.preferences)
      await this.loadPreferences()

    if (!preference)
      return this.preferences // return everything

    const obj = this.findKey(this.preferences, preference)
    if (obj)
      return obj[preference]
    else
      return null
  }

  async setNoSave(preference, value) {
    if (!this.preferences)
      await this.loadPreferences()

    const obj = this.findKey(this.preferences, preference)
    if (obj == null)
      throw new Error(`Can't find key ${preference} in preferences`)

    if (typeof value == 'undefined')
      delete obj[preference]
    else
      obj[preference] = value
  }

  async set(preference, value) {
    await this.setNoSave(preference, value)
    return await this.save()
  }

  async setOrCreate(preference, value, paths = ['defaults']) {
    if (!this.preferences)
      await this.loadPreferences()

    let obj = this.preferences
    for (let path of paths) {
      if (typeof obj[path] == 'undefined')
        obj[path] = {}
      obj = obj[path]
    }

    obj[preference] = value
  }

  async save() {
    const resource = `/SOGo/so/${this.username}/Preferences/save`
    const authCookie = await this.getAuthCookie()
    const response = await fetch(this.serverUrl + resource, {
      method: 'POST',
      headers: {
        Cookie: authCookie,
        'Content-Type': 'application/json',
        ...this.headers
      },
      body: JSON.stringify(this.preferences)
    })
    return response
  }
}

export default Preferences