
import { filter, throttleTime } from 'rxjs'
import { Injectable } from '@angular/core'
import { AuthService, AuthStatus } from '@app/core/auth'
import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
import { logger } from '@root-helpers/logger'
import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users'
import { UserRole, UserUpdateMe } from '@shared/models'
import { NSFWPolicyType } from '@shared/models/videos'
import { ServerService } from '../server'
import { LocalStorageService } from '../wrappers/storage.service'

@Injectable()
export class UserLocalStorageService {

  constructor (
    private authService: AuthService,
    private server: ServerService,
    private localStorageService: LocalStorageService
  ) {
    this.authService.userInformationLoaded.subscribe({
      next: () => {
        const user = this.authService.getUser()

        this.setLoggedInUser(user)
        this.setUserInfo(user)
        this.setTokens(user.oauthTokens)
      }
    })

    this.authService.loginChangedSource
      .pipe(filter(status => status === AuthStatus.LoggedOut))
      .subscribe({
        next: () => {
          this.flushLoggedInUser()
          this.flushUserInfo()
          this.flushTokens()
        }
      })

    this.authService.tokensRefreshed
      .subscribe({
        next: () => {
          const user = this.authService.getUser()

          this.setTokens(user.oauthTokens)
        }
      })
  }

  // ---------------------------------------------------------------------------

  getLoggedInUser () {
    const usernameLocalStorage = this.localStorageService.getItem(UserLocalStorageKeys.USERNAME)

    if (!usernameLocalStorage) return undefined

    return {
      id: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ID), 10),
      username: this.localStorageService.getItem(UserLocalStorageKeys.USERNAME),
      email: this.localStorageService.getItem(UserLocalStorageKeys.EMAIL),
      role: {
        id: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ROLE), 10) as UserRole,
        label: ''
      },

      ...this.getUserInfo()
    }
  }

  setLoggedInUser (user: {
    id: number
    username: string
    email: string
    role: {
      id: UserRole
    }
  }) {
    this.localStorageService.setItem(UserLocalStorageKeys.ID, user.id.toString())
    this.localStorageService.setItem(UserLocalStorageKeys.USERNAME, user.username)
    this.localStorageService.setItem(UserLocalStorageKeys.EMAIL, user.email)
    this.localStorageService.setItem(UserLocalStorageKeys.ROLE, user.role.id.toString())
  }

  flushLoggedInUser () {
    this.localStorageService.removeItem(UserLocalStorageKeys.ID)
    this.localStorageService.removeItem(UserLocalStorageKeys.USERNAME)
    this.localStorageService.removeItem(UserLocalStorageKeys.EMAIL)
    this.localStorageService.removeItem(UserLocalStorageKeys.ROLE)
  }

  // ---------------------------------------------------------------------------

  getUserInfo () {
    let videoLanguages: string[]

    try {
      const languagesString = this.localStorageService.getItem(UserLocalStorageKeys.VIDEO_LANGUAGES)
      videoLanguages = languagesString && languagesString !== 'undefined'
        ? JSON.parse(languagesString)
        : null
    } catch (err) {
      videoLanguages = null
      logger.error('Cannot parse desired video languages from localStorage.', err)
    }

    const htmlConfig = this.server.getHTMLConfig()

    const defaultNSFWPolicy = htmlConfig.instance.defaultNSFWPolicy
    const defaultP2PEnabled = htmlConfig.defaults.p2p.webapp.enabled

    return {
      nsfwPolicy: this.localStorageService.getItem<NSFWPolicyType>(UserLocalStorageKeys.NSFW_POLICY) || defaultNSFWPolicy,
      p2pEnabled: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.P2P_ENABLED), defaultP2PEnabled),
      theme: this.localStorageService.getItem(UserLocalStorageKeys.THEME) || 'instance-default',
      videoLanguages,

      autoPlayVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO), true),
      autoPlayNextVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_NEXT_VIDEO), false),
      autoPlayNextVideoPlaylist: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST), true)
    }
  }

  setUserInfo (profile: UserUpdateMe) {
    const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = {
      nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
      p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
      autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
      autoPlayNextVideo: UserLocalStorageKeys.AUTO_PLAY_NEXT_VIDEO,
      autoPlayNextVideoPlaylist: UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
      theme: UserLocalStorageKeys.THEME,
      videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
    }

    const obj = Object.keys(localStorageKeys)
      .filter(key => key in profile)
      .map(key => ([ localStorageKeys[key], profile[key] ]))

    for (const [ key, value ] of obj) {
      try {
        if (value === undefined) {
          this.localStorageService.removeItem(key)
          continue
        }

        const localStorageValue = typeof value === 'string'
          ? value
          : JSON.stringify(value)

        this.localStorageService.setItem(key, localStorageValue)
      } catch (err) {
        logger.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
      }
    }
  }

  flushUserInfo () {
    this.localStorageService.removeItem(UserLocalStorageKeys.NSFW_POLICY)
    this.localStorageService.removeItem(UserLocalStorageKeys.P2P_ENABLED)
    this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO)
    this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST)
    this.localStorageService.removeItem(UserLocalStorageKeys.THEME)
    this.localStorageService.removeItem(UserLocalStorageKeys.VIDEO_LANGUAGES)
  }

  listenUserInfoChange () {
    return this.localStorageService.watch([
      UserLocalStorageKeys.NSFW_POLICY,
      UserLocalStorageKeys.P2P_ENABLED,
      UserLocalStorageKeys.AUTO_PLAY_VIDEO,
      UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
      UserLocalStorageKeys.THEME,
      UserLocalStorageKeys.VIDEO_LANGUAGES
    ]).pipe(
      throttleTime(200),
      filter(() => this.authService.isLoggedIn() !== true)
    )
  }

  // ---------------------------------------------------------------------------

  getTokens () {
    return OAuthUserTokens.getUserTokens(this.localStorageService)
  }

  setTokens (tokens: OAuthUserTokens) {
    OAuthUserTokens.saveToLocalStorage(this.localStorageService, tokens)
  }

  flushTokens () {
    OAuthUserTokens.flushLocalStorage(this.localStorageService)
  }
}
