import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { isBoolean, isNumber } from '@utils/common';
import { BehaviorSubject, Observable } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { ApplicationProcess } from './defs';
import {
  removeProcess,
  selectSection,
  selectProcesses,
  setSection,
  addProcess,
  setActivatedProcess,
  selectActivatedProcess,
  patchProcess,
  patchProcessData,
} from './store';

@Injectable()
export class ApplicationService {
  /** @deprecated */
  private activatedProcessIdSubject = new BehaviorSubject<number>(undefined);

  /** @deprecated */
  get activatedProcessId() {
    return this.activatedProcessIdSubject.value;
  }

  /** @deprecated */
  get activatedProcessId$() {
    return this.activatedProcessIdSubject.asObservable();
  }

  constructor(private store: Store) {}

  getProcesses$(section?: 'customer' | 'branch') {
    const processes$ = this.store.select(selectProcesses);
    return processes$.pipe(map((processes) => processes.filter((process) => (section ? process.section === section : true))));
  }

  getProcessById$(processId: number): Observable<ApplicationProcess> {
    return this.getProcesses$().pipe(map((processes) => processes.find((process) => process.id === processId)));
  }

  getSection$() {
    return this.store.select(selectSection);
  }

  /** @deprecated */
  getActivatedProcessId$() {
    return this.store.select(selectActivatedProcess).pipe(map((process) => process?.id));
  }

  activateProcess(activatedProcessId: number) {
    this.store.dispatch(setActivatedProcess({ activatedProcessId }));
    this.activatedProcessIdSubject.next(activatedProcessId);
  }

  removeProcess(processId: number) {
    this.store.dispatch(removeProcess({ processId }));
  }

  patchProcess(processId: number, changes: Partial<ApplicationProcess>) {
    this.store.dispatch(patchProcess({ processId, changes }));
  }

  patchProcessData(processId: number, data: Record<string, any>) {
    this.store.dispatch(patchProcessData({ processId, data }));
  }

  async createProcess(process: ApplicationProcess) {
    const existingProcess = await this.getProcessById$(process?.id).pipe(first()).toPromise();
    if (existingProcess?.id === process?.id) {
      throw new Error('Process Id existiert bereits');
    }

    if (!isNumber(process.id)) {
      throw new Error('Process Id nicht gesetzt');
    }

    if (!isBoolean(process.closeable)) {
      process.closeable = true;
    }

    if (!isBoolean(process.confirmClosing)) {
      process.confirmClosing = true;
    }

    process.created = this._createTimestamp();
    process.activated = 0;
    this.store.dispatch(addProcess({ process }));
  }

  setSection(section: 'customer' | 'branch') {
    this.store.dispatch(setSection({ section }));
  }

  getLastActivatedProcessWithSectionAndType$(section: 'customer' | 'branch', type: string): Observable<ApplicationProcess> {
    return this.getProcesses$(section).pipe(
      map((processes) =>
        processes
          ?.filter((process) => process.type === type)
          ?.reduce((latest, current) => {
            if (!latest) {
              return current;
            }
            return latest?.activated > current?.activated ? latest : current;
          }, undefined)
      )
    );
  }

  getLastActivatedProcessWithSection$(section: 'customer' | 'branch'): Observable<ApplicationProcess> {
    return this.getProcesses$(section).pipe(
      map((processes) =>
        processes?.reduce((latest, current) => {
          if (!latest) {
            return current;
          }
          return latest?.activated > current?.activated ? latest : current;
        }, undefined)
      )
    );
  }

  private _createTimestamp() {
    return Date.now();
  }
}
