import {Component, inject, OnInit} from '@angular/core'
import {AlertController, NavController} from '@ionic/angular'
import {TaskService} from '../../services/task.service'
import {ActivatedRoute} from '@angular/router'
import {LabelService} from '../../services/label.service'
import {ILabel} from '../../../models/ILabel'

@Component({
  selector: 'app-task',
  templateUrl: './task.page.html',
  styleUrls: ['./task.page.scss']
})
export class TaskPage implements OnInit {

  taskName: string | null = null
  deadline: string | null = null
  description: string | null = null
  yearValues: number[] = []
  id?: string = undefined

  navController = inject(NavController)
  taskService = inject(TaskService)
  activatedRoute = inject(ActivatedRoute)
  alertController = inject(AlertController)
  labelService = inject(LabelService)

  labels = this.labelService.labels
  selectedLabels = Array(this.labels.length).fill(false)

  constructor() {
    // The following code generates a list of years that will be passed as options to the date-time picker.
    // Without this list, the date-time picker doesn't allow the user to choose a past date.
    const currentYear: number = (new Date()).getFullYear()
    for (let year = currentYear; year < (currentYear + 50); year++) {
      this.yearValues.push(year)
    }
  }

  ngOnInit(): void {
    // ngOnInit is a lifecycle hook that is called after the constructor is finished.
    // Retrieving data from APIs or services should happen here since the constructor should only initialize class
    // variables and leave the other work to other methods.
    // This ensures that the page is loaded as fast as possible.
    // It is good practice to use skeleton text (https://ionicframework.com/docs/api/skeleton-text) or a loading
    // indicator (https://ionicframework.com/docs/api/loading) to let the user know that data is being retrieved.
    this.setData()
  }

  /**
   * Check if the page is used to view/edit a task and if so, retrieve the details
   * of the task and initialize the form elements with this data.
   */
  setData(): void {
    const id = this.activatedRoute.snapshot.paramMap.get('id')

    // No need to continue with the function if no parameter was specified.
    if (id === null) {
      return
    }

    this.id = id

    const task = this.taskService.getTask(this.id)

    if (task) {
      this.taskName = task.name
      this.deadline = task.deadline ?? null
      this.description = task.description ?? null

      // Map takes an array of type T and transforms it into an array of type K using the supplied function.
      // In this case, an array of ILabels in transformed into an array of booleans.
      // If a label is included in the task, the corresponding position in the boolean array will contain true.
      // The !! operator can be used to turn a falsy or truthy value into a boolean value.
      this.selectedLabels = this.labels.map(l => !!task.labels.find(l2 => l2.id === l.id))
    }
  }

  /**
   * Handle a click event on the create/update button and determine which action has
   * to happen.
   * Either a new task will be created or an existing task will be updated.
   */
  handleCreateAndUpdate(): void {
    if (this.id) {
      this.#updateTask()
    } else {
      this.#createTask()
    }
    this.navController.back()
  }

  /**
   * Create a new task with the attributes defined in the form.
   */
  #createTask(): void {
    if (this.taskName) {
      this.taskService.createTask(
          this.taskName,
          this.description ?? '',
          this.#getSelectedLabels(),
          this.deadline ?? undefined,
      ).then()
    } else {
      this.alertController.create({
        header: 'Invalid input',
        message: 'Please ensure that the task has a name and try again.',
        buttons: ['OK'],
      }).then()
    }
  }

  /**
   * Update an existing task with the attributes defined in the form.
   */
  #updateTask(): void {
    if (this.id && this.taskName) {
      this.taskService.updateTask({
        id: this.id,
        name: this.taskName,
        description: this.description ?? '',
        deadline: this.deadline ?? undefined,
        labels: this.#getSelectedLabels(),
      }).then()
    }
  }

  /**
   * Use the selectedLabels array to determine which items from the label array have been selected.
   *
   * @return The labels that have been selected for the task that is being created or edited.
   */
  #getSelectedLabels(): ILabel[] {
    return this.labels.filter((_, i) => this.selectedLabels[i])
  }
}
