/*
https://docs.nestjs.com/providers#services
*/

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, zip } from 'rxjs';

import { map } from 'rxjs/operators';
import { LotProduct } from 'src/app/models/lot-product.model';

const CREATE_ACTION = 'create';
const UPDATE_ACTION = 'update';
const REMOVE_ACTION = 'destroy';

const itemIndex = (item: LotProduct, data: LotProduct[]): number => {
  for (let idx = 0; idx < data.length; idx++) {
    if (data[idx].lotProduct === item.lotProduct) {
      return idx;
    }
  }

  return -1;
};


const cloneData = (data: LotProduct[]) => data.map(item => Object.assign({}, item));

@Injectable({
  providedIn: 'root'
})
export class EditService extends BehaviorSubject<LotProduct[]> {

  private data: LotProduct[] = [];
  private originalData: LotProduct[] = [];
  private createdItems: LotProduct[] = [];
  private updatedItems: LotProduct[] = [];
  private deletedItems: LotProduct[] = [];

  lotProduct$: Observable<LotProduct[]>;
  lotProduct: LotProduct[] = [];
  constructor(private http: HttpClient) {
    super([]);
  }

  public read(): void {
    if (this.data.length) {
      return super.next(this.data);
    }


    this.fetch()
      .subscribe(data => {
        this.data = data;
        console.log('Data: ' + JSON.stringify(this.data));
        this.originalData = cloneData(data);
        super.next(data);
      });

      console.log('oRIGINAL Data: ' + JSON.stringify(this.originalData));    console.log("itens: " + this.data);
  }

  public create(item: LotProduct): void {
    this.createdItems.push(item);
    this.data.unshift(item);

    super.next(this.data);
  }

  public update(item: LotProduct): void {
    if (!this.isNew(item)) {
      const index = itemIndex(item, this.updatedItems);
      if (index !== -1) {
        this.updatedItems.splice(index, 1, item);
      } else {
        this.updatedItems.push(item);
      }
    } else {
      const index = this.createdItems.indexOf(item);
      this.createdItems.splice(index, 1, item);
    }
  }

  public remove(item: LotProduct): void {
    let index = itemIndex(item, this.data);
    this.data.splice(index, 1);

    index = itemIndex(item, this.createdItems);
    if (index >= 0) {
      this.createdItems.splice(index, 1);
    } else {
      this.deletedItems.push(item);
    }

    index = itemIndex(item, this.updatedItems);
    if (index >= 0) {
      this.updatedItems.splice(index, 1);
    }

    super.next(this.data);
  }

  public isNew(item: LotProduct): boolean {
    return !item.lotProduct;
  }

  public hasChanges(): boolean {
    return Boolean(this.deletedItems.length || this.updatedItems.length || this.createdItems.length);
  }

  public saveChanges(): void {
    if (!this.hasChanges()) {
      return;
    }

    const completed = [];
    if (this.deletedItems.length) {
      completed.push(this.fetch(REMOVE_ACTION, this.deletedItems));
    }

    if (this.updatedItems.length) {
      completed.push(this.fetch(UPDATE_ACTION, this.updatedItems));
    }

    if (this.createdItems.length) {
      completed.push(this.fetch(CREATE_ACTION, this.createdItems));
    }

    this.reset();

    zip(...completed).subscribe(() => this.read());
  }

  public cancelChanges(): void {
    this.reset();

    this.data = this.originalData;
    this.originalData = cloneData(this.originalData);
    super.next(this.data);
  }

  public assignValues(target: LotProduct, source: LotProduct): void {
    Object.assign(target, source);
  }

  private reset() {
    this.data = [];
    this.deletedItems = [];
    this.updatedItems = [];
    this.createdItems = [];
  }

  private fetch(action = '', data?: LotProduct[]): Observable<LotProduct[]> {
    return this.lotProduct$
      .pipe(map((res: Object) => <LotProduct[]>res));
  }

  private serializeModels(data?: LotProduct[]): string {
    return data ? `&models=${JSON.stringify(data)}` : '';
  }
}
