import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';
import * as utils from '@app/degree-planner/shared/utils';
import {
  AddCourse,
  AddSaveForLater,
  MoveCourseBetweenTerms,
  RemoveSaveForLater,
  RemoveCourse,
} from '@app/degree-planner/store/actions/course.actions';
import { GlobalState } from '@app/core/state';
import { Course } from '@app/core/models/course';
import * as selectors from '@app/degree-planner/store/selectors';
import { DegreePlannerApiService } from '@app/degree-planner/services/api.service';
import { ConfirmDialogComponent } from '@app/shared/dialogs/confirm-dialog/confirm-dialog.component';
import { CourseDetailsDialogComponent } from '@app/degree-planner/dialogs/course-details-dialog/course-details-dialog.component';
import { distinctUntilChanged, filter } from 'rxjs/operators';
import { TermCode } from '@app/core/models/termcode';
import { PlannedTerm, PlannedTermEra } from '@app/core/models/planned-term';

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

@Component({
  selector: 'cse-course-item',
  templateUrl: './course-item.component.html',
  styleUrls: ['./course-item.component.scss'],
})
export class CourseItemComponent implements OnInit {
  @Input() course: Course;
  @Input() isCurrentTerm: boolean;
  @Input() isPastTerm: boolean;
  @Input() disabled: boolean;
  @Input() type: 'saved' | 'course' | 'search';
  @Input() era?: unknown;
  visibleTerms: any;
  activeTerm: any;
  public status:
    | 'InProgress'
    | 'Waitlisted'
    | 'Incomplete'
    | 'NotOfferedInTerm'
    | 'NoLongerOffered'
    | 'Normal';
  public visibleTermCodes$: Observable<string[]>;
  public droppableTermCodes$: Observable<string[]>;
  public term$: Observable<PlannedTerm>;
  public plannedCourses: ReadonlyArray<Course>;
  public toActiveTerm: boolean;
  public isStruckthrough = false;

  constructor(
    private api: DegreePlannerApiService,
    public dialog: MatDialog,
    private store: Store<GlobalState>,
  ) {}

  ngOnInit() {
    const isActive = this.era === 'active';
    const isPast = this.era === 'past';
    const isNotOffered = this.course.studentEnrollmentStatus === 'NOTOFFERED';
    const isNoLongerOffered =
      this.course.studentEnrollmentStatus === 'DOESNOTEXIST';
    this.isStruckthrough = (isPast || isActive) && isNoLongerOffered;

    const isIncomplete = isPast && this.course.grade === null;
    const isWaitlisted = this.course.studentEnrollmentStatus === 'Waitlisted';
    const isInProgress =
      isActive && this.course.studentEnrollmentStatus === 'Enrolled';

    if (isIncomplete) {
      this.status = 'Incomplete';
    } else if (isWaitlisted) {
      this.status = 'Waitlisted';
    } else if (isInProgress) {
      this.status = 'InProgress';
    } else if (isNotOffered) {
      this.status = 'NotOfferedInTerm';
    } else {
      this.status = 'Normal';
    }
  }

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

  moveToSavedForLater(course) {
    this.store.dispatch(
      new AddSaveForLater({
        courseId: course.courseId,
        subjectCode: course.subjectCode,
        title: course.title,
        catalogNumber: course.catalogNumber,
        newIndex: 0,
      }),
    );
  }

  /**
   *
   *  Handle moving a course to different terms based on course type
   *
   */
  onMove(termCode: string) {
    const toTermCode = new TermCode(termCode);
    this.term$ = this.store.pipe(
      select(selectors.selectVisibleTerm, { termCode: toTermCode }),
      filter(isntUndefined),
      distinctUntilChanged(),
    );

    this.term$.subscribe(term => {
      this.plannedCourses = term.plannedCourses;
      this.toActiveTerm = term.era === 'active';
    });

    const isCourseInPlannedCourses = this.plannedCourses.some(
      course => course.courseId === this.course.courseId,
    );
    const courseNotOfferedInTerm = this.plannedCourses.some(
      course =>
        course.studentEnrollmentStatus === 'DOESNOTEXIST' ||
        course.studentEnrollmentStatus === 'NOTOFFERED',
    );
    if (isCourseInPlannedCourses) {
      this.dialog
        .open(ConfirmDialogComponent, {
          data: {
            title: "Can't add course",
            confirmText: 'OK',
            dialogClass: 'alertDialog',
            text: `This course already exists in selected term!`,
          },
        })
        .afterClosed();
      return;
    }

    if (this.toActiveTerm && courseNotOfferedInTerm) {
      const notOfferedCourse =
        this.course.subject + ' ' + this.course.catalogNumber;
      this.dialog
        .open(ConfirmDialogComponent, {
          data: {
            title: "Can't add course to term",
            confirmText: 'OK',
            dialogClass: 'alertDialog',
            text: `${notOfferedCourse} is not offered in ${
              toTermCode.description
            }`,
          },
        })
        .afterClosed();
      return;
    }

    switch (this.type) {
      case 'course': {
        const id = this.course.id as number;
        const { subjectCode, courseId } = this.course;
        const from = new TermCode(this.course.termCode);
        this.store.dispatch(
          new MoveCourseBetweenTerms({
            to: toTermCode,
            from,
            id,
            newIndex: 0,
            courseId,
            subjectCode,
          }),
        );
        break;
      }

      case 'saved': {
        const { subjectCode, courseId } = this.course;
        this.addToTerm(toTermCode);
        this.store.dispatch(new RemoveSaveForLater({ subjectCode, courseId }));
        break;
      }

      case 'search': {
        this.addToTerm(toTermCode);
        break;
      }
    }
  }

  /**
   *
   *  Handle saving a course for later (This is not possible if a course is already saved)
   *
   */
  onSaveForLater() {
    const {
      courseId,
      subjectCode,
      title,
      catalogNumber,
      termCode,
    } = this.course;

    // Dispatch a save for later event
    this.store.dispatch(
      new AddSaveForLater({
        courseId: courseId,
        subjectCode: subjectCode,
        title: title,
        catalogNumber: catalogNumber,
        newIndex: 0,
      }),
    );

    // If course is in a term, we need to remove it
    if (this.type === 'course') {
      this.store.dispatch(
        new RemoveCourse({
          fromTermCode: new TermCode(termCode),
          recordId: this.course.id as number,
        }),
      );
    }
  }

  /**
   *
   *  Handle removing a course (This is not possible for type 'search')
   *
   */
  onRemove() {
    const dialogOptions = {
      title: 'Remove Course?',
      text: '',
      confirmText: 'Remove Course',
      confirmColor: 'accent',
    };

    switch (this.type) {
      case 'saved':
        dialogOptions.text = `This will remove "${
          this.course.title
        }" from your saved courses.`;
        break;

      default:
        dialogOptions.text = `This will remove "${
          this.course.title
        }" from your degree plan and your cart.`;
    }

    this.dialog
      .open(ConfirmDialogComponent, { data: dialogOptions })
      .afterClosed()
      .subscribe((result: { confirmed: boolean }) => {
        // If the user confirmed the removal, remove course
        if (result && result.confirmed) {
          console.log(this.type);

          console.log({
            type: this.type,
            fromTermCode: this.course.termCode,
            recordId: this.course.id,
          });

          switch (this.type) {
            case 'course':
              this.store.dispatch(
                new RemoveCourse({
                  fromTermCode: new TermCode(this.course.termCode),
                  recordId: this.course.id as number,
                }),
              );
              break;

            case 'saved':
              const { subjectCode, courseId } = this.course;
              this.store.dispatch(
                new RemoveSaveForLater({ subjectCode, courseId }),
              );
              break;
          }
        }
      });
  }

  addToTerm(toTermCode: TermCode) {
    this.store.dispatch(
      new AddCourse({
        courseId: this.course.courseId,
        termCode: toTermCode,
        subjectCode: this.course.subjectCode,
        title: this.course.title,
        catalogNumber: this.course.catalogNumber,
      }),
    );
  }

  openCourseDetailsDialog(course) {
    this.api
      .getCourseDetails(course.subjectCode, course.courseId)
      .subscribe(courseDetails => {
        const dialogRef = this.dialog.open(CourseDetailsDialogComponent, {
          maxWidth: '800px',
          width: '80%',
          data: { courseDetails: courseDetails, courseType: this.type },
        });
      });
  }
}