import { Injectable } from '@angular/core';
import { MetadataService } from './metadata.service';
import { DataCachingService } from './data-caching.service';
import { Observable, Subject, BehaviorSubject, ReplaySubject } from 'rxjs';
import { map, switchMap } from 'rxjs/internal/operators';
import { isArray } from 'rxjs/internal/util/isArray';

@Injectable()
export class LookupSourceService {
  constructor(private metadataService: MetadataService,
              private dataCachingService: DataCachingService) {

  }

  public getLookup(lookupName: string, clone?: boolean, sort?: boolean, showHistory?: boolean): Observable<any[]> {
    if (!lookupName) {
      return new BehaviorSubject([]);
    }

    lookupName = lookupName.toLowerCase();
    let existing: Subject<any[]> = this.dataCachingService.getCachedData('LookupSourceService', lookupName);
    let existingActual: Subject<any[]> = this.dataCachingService.getCachedData('LookupSourceServiceActual', lookupName);

    if (!existing || !existingActual) {
      existing = new ReplaySubject<any[]>();
      existingActual = new ReplaySubject<any[]>();
      this.dataCachingService.addToCache('LookupSourceService', lookupName, existing, 200);
      this.dataCachingService.addToCache('LookupSourceServiceActual', lookupName, existingActual, 200);
      existing.subscribe((val: any[]) => {
        let val2 = [];
        if (isArray(val)) {
          val.forEach(el => {
            if (el && !el.dateDeleted) {
              val2.push(el);
            }
          });
        } else {
          val2 = val;
        }

        existingActual.next(val2);
      });
      this.preloadData(lookupName, existing);
    }

    const lookupSliceToUse = showHistory ? existing : existingActual;

    if (clone || sort) {
      return lookupSliceToUse.pipe(map(dict => {
        const newDict = [];
        dict.forEach(el => {
          const newEl = {};
          Object.assign(newEl, el);
          newDict.push(newEl);
        });

        if (sort) {
          newDict.sort((a, b) => a.caption.localeCompare(b.caption));
        }

        return newDict;
      }));
    } else {
      return lookupSliceToUse;
    }
  }

  public getLookupObj(lookupName: string): Observable<any> {
    if (lookupName === 'addr-street') {
      throw new Error('getLookupObj для addr-street не работает');
    }

    lookupName = lookupName.toLowerCase();
    let existing: Subject<any> = this.dataCachingService.getCachedData('LookupSourceServiceObj', lookupName);

    if (existing) {
      return existing;
    } else {
      existing = new ReplaySubject<any>();

      this.getLookup(lookupName, false, false, true).subscribe((arr): any => {
        const val = {};
        if (arr) {
          arr.forEach(item => {
            val[item.id] = item.caption;
            val['Obj' + item.id.toString()] = item;
          });
        }
        existing.next(val);
      });

      this.dataCachingService.addToCache('LookupSourceServiceObj', lookupName, existing, 200);

      return existing;
    }
  }

  getLookupCaption(value: any, lookupName: string, useShort?: boolean, objId2?: any): Observable<string> {
    if (!value && value !== 0) {
      return new BehaviorSubject<any>('-');
    }

    return this.getLookupObj(lookupName)
          .pipe(map(lookup => lookup['Obj' + value]
            ? (lookup['Obj' + value].shortCaption || lookup['Obj' + value].caption)
            : 'N/A'));
  }

  private preloadData(lookupNameLower: string, existing: Subject<any[]>) {
    this.metadataService.getMetadata(lookupNameLower).subscribe(val => existing.next(val));
  }
}
