import { DatePipe } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
import { DomainPrinterService } from '@domain/printer';
import { DomainTaskCalendarService, ProcessingStatusList } from '@domain/task-calendar';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { DisplayInfoDTO } from '@swagger/eis';
import { DateAdapter } from '@ui/common';
import { UiModalService } from '@ui/modal';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { first, map, withLatestFrom } from 'rxjs/operators';

@Component({
  selector: 'page-task-list',
  templateUrl: 'task-list.component.html',
  styleUrls: ['task-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [DatePipe],
})
export class TaskListComponent {
  today = this.dateAdapter.today();

  /* @internal */
  items$ = new BehaviorSubject<DisplayInfoDTO[]>([]);

  @Input()
  get items() {
    return this.items$.value;
  }
  set items(value) {
    this.items$.next(value);
  }

  /* @internal */
  selected$ = new BehaviorSubject<Date>(new Date());

  @Input()
  get selected() {
    return this.selected$.value;
  }
  set selected(value) {
    if (this.selected !== value) {
      this.selected$.next(value);
    }
  }

  isToday$ = this.selected$.pipe(map((selected) => this.dateAdapter.equals({ first: selected, second: this.today, precision: 'day' })));

  overdueItems$ = combineLatest([this.items$, this.selected$]).pipe(
    map(([items, selectedDate]) => {
      const last7Days = this.dateAdapter.addCalendarDays(this.today, -7);
      return items.filter((item) => {
        const type = this.domainTaskCalendarService.getInfoType(item);
        const processingStatus = this.domainTaskCalendarService.getProcessingStatusList(item);
        if (type === 'Task' && processingStatus.includes('Overdue')) {
          return new Date(item.taskOverdueDate) >= last7Days;
        }
        return false;
      });
    }),
    withLatestFrom(this.selected$),
    map(([list, date]) =>
      list.filter((item) =>
        !!item?.successor
          ? this.dateAdapter.equals({ first: new Date(item.publicationDate || item.taskDate), second: date, precision: 'day' })
          : item
      )
    ),
    map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
  );

  ongoingItems$ = combineLatest([this.items$, this.selected$]).pipe(
    map(([items, selectedDate]) =>
      // Filter Aufgaben die vor dem aktuellen Tag gestartet sind und nicht überfällig oder abgeschlossen sind
      items.filter((item) => {
        const type = this.domainTaskCalendarService.getInfoType(item);
        const processingStatus = this.domainTaskCalendarService.getProcessingStatusList(item);
        if (
          type === 'Task' &&
          (processingStatus.includes('InProcess') || processingStatus.includes('Approved')) &&
          !processingStatus.includes('Removed') &&
          !processingStatus.includes('Uncompleted') &&
          !processingStatus.includes('Overdue') &&
          !processingStatus.includes('Completed')
        ) {
          return new Date(item.publicationDate || item.taskDate) < this.today;
        }
        return false;
      })
    ),
    withLatestFrom(this.selected$),
    map(([list, date]) =>
      list.filter((item) =>
        !!item?.successor
          ? this.dateAdapter.equals({ first: new Date(item.publicationDate || item.taskDate), second: date, precision: 'day' })
          : item
      )
    ),
    map((list) => this.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
    map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
  );

  selectedItems$ = combineLatest([this.items$, this.selected$]).pipe(
    map(([items, date]) =>
      items.filter((item) =>
        this.dateAdapter.equals({
          first: this.domainTaskCalendarService.getDisplayInfoDate(item),
          second: date,
          precision: 'day',
        })
      )
    ),
    withLatestFrom(this.selected$),
    map(([list, date]) =>
      list.filter((item) =>
        !!item?.successor ? this.dateAdapter.equals({ first: new Date(item.publicationDate), second: date, precision: 'day' }) : item
      )
    ),
    // Sortierung der aufgaben nach Rot => Gelb => Grau => Grün
    map((list) => this.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
    map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
  );

  @Output()
  select = new EventEmitter<DisplayInfoDTO>();

  constructor(
    private dateAdapter: DateAdapter,
    private datePipe: DatePipe,
    private domainTaskCalendarService: DomainTaskCalendarService,
    private uiModal: UiModalService,
    private domainPrinterService: DomainPrinterService
  ) {}

  moveRemovedToEnd(a: DisplayInfoDTO, b: DisplayInfoDTO) {
    const statusA = this.domainTaskCalendarService.getProcessingStatusList(a)?.includes('Removed');
    const statusB = this.domainTaskCalendarService.getProcessingStatusList(b)?.includes('Removed');

    if (statusA && statusB) {
      return 0;
    } else if (statusA && !statusB) {
      return 1;
    } else if (!statusA && statusB) {
      return -1;
    }

    return 0;
  }

  /**
   * Returns an Array of DisplayInfoDTO, sorted by ProcessingStatus
   * Ignores Overdue if Task is already Completed
   * Compared DisploayInfoDTO is of Type Task and Info Or PreInfo then sort by Type
   * @param items DisplayInfoDTO Array to sort
   * @param order Processing Status Order
   * @returns DisplayInfoDTO Array ordered by Processing Status anf Type
   */
  sort(items: DisplayInfoDTO[], order: ProcessingStatusList) {
    let result = [...items];
    const reversedOrder = [...order].reverse();

    for (const status of reversedOrder) {
      result = result?.sort((a, b) => {
        const statusA = this.domainTaskCalendarService.getProcessingStatusList(a);
        const statusB = this.domainTaskCalendarService.getProcessingStatusList(b);

        // Ignore Overdue when it is already Completed
        if (status === 'Overdue' && statusA.includes('Completed')) {
          return 0;
        }

        const aHasStatus = statusA.includes(status);
        const bHasStatus = statusB.includes(status);

        if (aHasStatus && bHasStatus) {
          // If it has the same ProcessingStatus then Sort by Type
          const aType = this.domainTaskCalendarService.getInfoType(a);
          const bType = this.domainTaskCalendarService.getInfoType(b);
          if (aType !== bType) {
            if (aType === 'Info' || aType === 'PreInfo') {
              return -1;
            } else {
              return 1;
            }
          }

          if (statusB.includes('Completed')) {
            return -1;
          }

          return 0;
        } else if (aHasStatus && !bHasStatus) {
          return -1;
        } else if (!aHasStatus && bHasStatus) {
          return 1;
        }

        return 0;
      });
    }

    return result;
  }

  async print() {
    let displayInfos = await this.selectedItems$.pipe(first()).toPromise();
    displayInfos = displayInfos.filter((di) => !this.domainTaskCalendarService.getProcessingStatusList(di).includes('Completed'));
    this.uiModal.open({
      content: PrintModalComponent,
      data: {
        printerType: 'Office',
        print: (printer) =>
          this.domainPrinterService
            .printDisplayInfoDTOList({
              displayInfos,
              printer,
              title: `Tätigkeitskalender \n Liste für ${this.datePipe.transform(this.selected, 'EEEE, dd. MMMM yyyy')}`,
            })
            .toPromise(),
      } as PrintModalData,
      config: {
        panelClass: [],
        showScrollbarY: false,
      },
    });
  }
}
