import { Component, OnInit, OnDestroy, Inject, Input } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, map, filter } from 'rxjs/operators';
import { MatDialog } from '@angular/material';
import { Store, select } from '@ngrx/store';

import { CourseDetails } from '@app/core/models/course-details';
import * as selectors from '@app/degree-planner/store/selectors';
import * as utils from '@app/degree-planner/shared/utils';
import { TermCode } from '@app/core/models/termcode';
import { GlobalState } from '@app/core/state';
import { Course } from '@app/core/models/course';
import { PlannedTerm } from '@app/core/models/planned-term';
import { ConfirmDialogComponent } from '@app/shared/dialogs/confirm-dialog/confirm-dialog.component';

import {
  AddCourse,
  RemoveSaveForLater,
} from '@app/degree-planner/store/actions/course.actions';

const isntUndefined = <T>(thing: T | undefined): thing is T => {
  return thing !== undefined;
};

@Component({
  selector: 'cse-course-details',
  templateUrl: './course-details.component.html',
  styleUrls: ['./course-details.component.scss'],
})
export class CourseDetailsComponent implements OnInit, OnDestroy {
  @Input() termCode: TermCode;
  public courseDetails: CourseDetails;
  public type: 'course' | 'search' | 'saved';
  public selectedSearchTerm: string;
  public term$: Observable<PlannedTerm>;
  public termSelector: FormGroup;

  public selectedSearchTerm$: Observable<string>;
  public droppableTermCodes$: Observable<string[]>;
  public searchTermSubscription: Subscription;
  public termSubscription: Subscription;
  public plannedCourses: ReadonlyArray<Course>;

  constructor(
    @Inject(MAT_DIALOG_DATA) public data: any,
    private store: Store<GlobalState>,
    private fb: FormBuilder,
    public dialog: MatDialog,
  ) {
    this.courseDetails = data.courseDetails;
    this.type = data.courseType;
  }

  ngOnInit() {
    this.selectedSearchTerm$ = this.store.pipe(
      select(selectors.getSelectedSearchTerm),
      map(termCode => (termCode ? termCode.toString() : '0000')),
    );

    this.droppableTermCodes$ = this.store.pipe(
      select(selectors.selectAllVisibleYears),
      utils.yearsToDroppableTermCodes(),
      distinctUntilChanged(utils.compareStringArrays),
    );

    this.searchTermSubscription = this.selectedSearchTerm$.subscribe(
      termCode => {
        this.selectedSearchTerm = termCode;
      },
    );

    this.termSelector = this.fb.group({
      term: this.selectedSearchTerm,
    });
  }

  ngOnDestroy() {
    this.searchTermSubscription.unsubscribe();
  }

  addCourseToPlan($event) {
    $event.preventDefault();

    const termCode = new TermCode(this.termSelector.value.term);
    const subjectCode = this.courseDetails.subject.subjectCode;
    const courseId = this.courseDetails.courseId;
    const payload = {
      courseId,
      termCode,
      subjectCode,
      title: this.courseDetails.title,
      catalogNumber: this.courseDetails.catalogNumber,
    };

    this.term$ = this.store.pipe(
      select(selectors.selectVisibleTerm, {
        termCode: new TermCode(this.termSelector.value.term),
      }),
      filter(isntUndefined),
      distinctUntilChanged(),
    );
    this.termSubscription = this.term$.subscribe(term => {
      this.plannedCourses = term.plannedCourses;
    });

    const isCourseInPlannedCourses = this.plannedCourses.some(
      course => course.courseId === this.courseDetails.courseId,
    );

    if (isCourseInPlannedCourses) {
      this.dialog
        .open(ConfirmDialogComponent, {
          data: {
            title: "Can't add course to term",
            confirmText: 'OK',
            dialogClass: 'alertDialog',
            text: `This course already exists in selected term`,
          },
        })
        .afterClosed();
    } else {
      switch (this.type) {
        case 'search':
          this.store.dispatch(new AddCourse(payload));
          break;

        case 'saved':
          this.store.dispatch(new AddCourse(payload));
          this.store.dispatch(
            new RemoveSaveForLater({ subjectCode, courseId }),
          );
          break;
      }
    }
  }
}