import {Injectable} from '@angular/core'
import {Preferences} from '@capacitor/preferences'
import {LocalNotifications} from '@capacitor/local-notifications'
import {Settings} from '../../models/settings'


@Injectable({
  providedIn: 'root'
})
export class SettingService {

  #settings: Settings = {
    showNotifications: false,
    timeBeforeDeadline: 30,
  }

  readonly #notificationsKey = 'notifications'

  constructor() {
    this.#initialize().then(() => console.log('Settings Loaded'))
  }

  async #initialize() {
    const settings = await this.#loadSettings(this.#notificationsKey)
    if (settings) {
      this.#settings = settings
    }

    // Check if notifications are still allowed, the user might have changed the settings in the OS since the app was
    // last opened.
    const havePermission = await this.#notificationsAreAllowed()

    // Change the current setting to false if the permissions were altered through the OS.
    if (this.#settings.showNotifications && !havePermission) {
      this.#settings.showNotifications = false
      await this.#persistSettings(this.#notificationsKey, this.#settings)
    }
  }

  /**
   * Check if the user wishes to receive notifications for uncompleted tasks.
   */
  notificationsAreEnabled(): boolean {
    return this.#settings.showNotifications
  }

  /**
   * Check the amount of time before the deadline that the notifications must be shown.
   */
  getTimeBeforeDeadline(): number {
    return this.#settings.timeBeforeDeadline
  }

  /**
   * Change the user's preferences from false to true or the reverse.
   * This option can only be toggled when the user allows notifications.
   */
  async toggleShowNotifications(): Promise<boolean> {
    // If notifications aren't enabled, we must first check if we are allowed to display notifications.
    // Even if we no longer have permission, we can always disable notifications in the settings.
    // If we're not allowed to enable them, the method can stop here.
    if (!this.notificationsAreEnabled() && !(await this.#requestPermissions())) {
      return false
    }

    // If permission has been granted, we can switch toggle the setting and persist the changes.
    this.#settings.showNotifications = !this.#settings.showNotifications
    await this.#persistSettings(this.#notificationsKey, this.#settings)

    return this.notificationsAreEnabled()
  }

  /**
   * Change the amount of time before a deadline that the notification is shown.
   *
   * @param timeInMinutes
   */
  async setTimeBeforeDeadline(timeInMinutes: number): Promise<void> {
    this.#settings.timeBeforeDeadline = timeInMinutes
    await this.#persistSettings('notifications', this.#settings)
  }

  /**
   * Persist a settings object.
   *
   * @param key The key under which the settings object is to be saved.
   * @param value The object that contains te settings.
   * @private
   */
  async #persistSettings(key: string, value: any) {
    value = JSON.stringify(value)
    await Preferences.set({key, value})
  }

  /**
   * Load the settings for a given key.
   *
   * @param key The key that corresponds to the setting to be retrieved.
   * @private
   */
  async #loadSettings(key: string): Promise<Settings | null> {
    // The following uses deconstructing syntax.
    // This allows us to unpack an object or array into one or more variables
    const {value} = await Preferences.get({key})
    return value ? JSON.parse(value) : value
  }

  /**
   * Request permission to show notifications.
   *
   * @private
   * @return True if permissions have been granted, false otherwise.
   */
  async #requestPermissions(): Promise<boolean> {
    const {display} = await LocalNotifications.requestPermissions()
    return display === 'granted'
  }

  /**
   * Check if permission has been granted to show notifications.
   *
   * @private
   */
  async #notificationsAreAllowed(): Promise<boolean> {
    const {display} = await LocalNotifications.checkPermissions()
    return display === 'granted'
  }
}
