import {HttpClient} from '@angular/common/http';
import {AfterViewInit, ChangeDetectorRef, Component, ElementRef, inject, TemplateRef, ChangeDetectionStrategy} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {
  ActivatedRoute,
  Router,
  RouterLink,
  RouterOutlet
} from '@angular/router';
import {
  NgbModal,
  NgbDropdown,
  NgbDropdownToggle,
  NgbDropdownMenu,
  NgbDropdownButtonItem,
  NgbDropdownItem,
  NgbTooltip,
} from '@ng-bootstrap/ng-bootstrap';
import {
  CalendarEvent,
  CalendarEventTimesChangedEvent,
  CalendarPreviousViewDirective,
  CalendarTodayDirective,
  CalendarNextViewDirective,
  CalendarWeekViewComponent,
  CalendarEventTitleComponent,
  CalendarDatePipe,
  DateAdapter,
  provideCalendar,
} from 'angular-calendar';
import {adapterFactory} from 'angular-calendar/date-adapters/date-fns';
import {WeekViewHourSegment} from 'calendar-utils';
import {addMinutes, differenceInMinutes, endOfWeek} from 'date-fns';
import {fromEvent} from 'rxjs';
import {finalize, takeUntil} from 'rxjs/operators';

import {environment} from '../../../environments/environment';
import {CreatePollEventDto} from '../../model';
import {ChooseDateService} from '../services/choose-date.service';

@Component({
  selector: 'app-choose-date',
  templateUrl: './choose-date.component.html',
  styleUrls: ['./choose-date.component.scss'],
  providers: [
    ChooseDateService,
    provideCalendar({
      provide: DateAdapter,
      useFactory: adapterFactory,
    })
  ],
  changeDetection: ChangeDetectionStrategy.Eager,
  imports: [
    CalendarPreviousViewDirective,
    CalendarTodayDirective,
    CalendarNextViewDirective,
    NgbDropdown,
    NgbDropdownToggle,
    NgbDropdownMenu,
    NgbDropdownButtonItem,
    NgbDropdownItem,
    RouterLink,
    CalendarWeekViewComponent,
    CalendarEventTitleComponent,
    NgbTooltip,
    FormsModule,
    RouterOutlet,
    CalendarDatePipe,
  ],
})
export class ChooseDateComponent implements AfterViewInit {
  private modalService = inject(NgbModal);
  private changeDetectorRef = inject(ChangeDetectorRef);
  private chooseDateService = inject(ChooseDateService);
  private http = inject(HttpClient);
  private router = inject(Router);
  private route = inject(ActivatedRoute);
  private activatedRoute = inject(ActivatedRoute);
  private elementRef = inject(ElementRef);
  viewDate = new Date();
  dragToCreateActive = true;
  weekStartsOn = 1 as const;
  previousEventDuration = 15;
  id = '';
  note = '';
  noteEvent?: CalendarEvent;
  disabledBefore = Date.now() - 15 * 60 * 1000;
  segments = 4;

  constructor(
  ) {
    this.route.params.subscribe(({id}) => {
      this.id = id;
      this.chooseDateService.updateEvents(id);
    });
  }

  ngAfterViewInit() {
    this.scrollToCurrentTime();
  }

  scrollToCurrentTime() {
    const selector = '.cal-current-time-marker';
    setTimeout(() => {
      this.elementRef.nativeElement.querySelector(selector).scrollIntoView();
    });
  }

  startDragToCreate(
    segment: WeekViewHourSegment,
    mouseDownEvent: MouseEvent,
    segmentElement: HTMLElement,
  ) {
    if (mouseDownEvent.button !== 0) {
      return;
    }

    const dragToSelectEvent = this.chooseDateService.createDragSelectEvent(segment);
    this.dragToCreateActive = true;
    const endOfView = endOfWeek(this.viewDate, {
      weekStartsOn: this.weekStartsOn,
    });

    fromEvent(document, 'mousemove')
      .pipe(
        finalize(() => {
          delete dragToSelectEvent.meta?.tmpEvent;
          this.dragToCreateActive = false;
          this.refresh();
        }),
        takeUntil(fromEvent(document, 'mouseup')),
      )
      .subscribe((mouseMoveEvent: any) => {
        const newEnd = this.chooseDateService.calculateNewEnd(segment, segmentElement, mouseMoveEvent, 60 / this.segments);
        if (newEnd > segment.date && newEnd < endOfView) {
          dragToSelectEvent.end = newEnd;
          this.previousEventDuration = differenceInMinutes(dragToSelectEvent.end, dragToSelectEvent.start);
        }
        this.chooseDateService.updateEventTime(dragToSelectEvent, dragToSelectEvent.start, dragToSelectEvent.end!, false);
        this.refresh();
      });

    if (!dragToSelectEvent.end) {
      dragToSelectEvent.end = addMinutes(dragToSelectEvent.start, this.previousEventDuration);
    }
    this.chooseDateService.updateEventTime(dragToSelectEvent, dragToSelectEvent.start, dragToSelectEvent.end, false);
  }

  eventTimesChanged({event, newStart, newEnd, allDay}: CalendarEventTimesChangedEvent): void {
    if (!newEnd && !allDay && !event.allDay) {
      return;
    }

    this.chooseDateService.updateEventTime(event, newStart, newEnd ?? newStart, allDay ?? event.allDay);
    this.refresh();
  }

  refresh() {
    this.chooseDateService.events = [...this.chooseDateService.events];
    this.changeDetectorRef.detectChanges();
  }

  getEvents() {
    return this.chooseDateService.events;
  }

  saveEvents() {
    const events: CreatePollEventDto[] = this.chooseDateService.events.map((event: CalendarEvent) => ({
      _id: event.id?.toString(),
      start: event.start.toISOString(),
      end: (event.end ?? event.start).toISOString(),
      note: event.meta.note,
      allDay: !!event.allDay,
    }));
    this.http.post(`${environment.backendURL}/poll/${this.id}/events`, events).subscribe(() => {
      this.router.navigate([`poll/${this.id}/participate`]).then();
    });
  }

  hasEvents() {
    return this.chooseDateService.events.length > 0;
  }

  createAllDayEvent(date: Date) {
    this.chooseDateService.addEvent(date, date, undefined, true);
    this.refresh();
  }

  deleteEvent(event: CalendarEvent) {
    this.chooseDateService.deleteEvent(event);
  }

  openNoteModal(content: TemplateRef<any>, event: CalendarEvent) {
    this.noteEvent = event;
    this.modalService.open(content, {centered: true, size: 'lg'});
  }

  addNote() {
    if (!this.noteEvent) {
      return;
    }

    this.noteEvent.meta.note = this.note;
    this.note = '';
    this.noteEvent = undefined;
    this.modalService.dismissAll();
  }

  deleteNote(event: CalendarEvent) {
    event.meta.note = undefined;
  }

  autofill(event: CalendarEvent) {
    this.chooseDateService.autofillEvent = event;
    this.router.navigate(['autofill'], {relativeTo: this.activatedRoute}).then();
  }

  toggleAllDay(event: CalendarEvent) {
    this.chooseDateService.toggleAllDay(event, this.previousEventDuration);
    this.refresh();
  }
}
