import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

import { FundRecordData } from '../structures/fund-record-data';

import * as Globals from '../globals';

@Injectable({
  providedIn: 'root'
})
export class FundService {

  private fundRecords = new BehaviorSubject<FundRecordData[] | null>(null);
  fundRecords$ = this.fundRecords.asObservable();

  // stav ukladani informaci o fondu
  private fundRecordSaveStatus = new BehaviorSubject<number>(Globals.savingStatus.none);
  fundRecordSaveStatus$ = this.fundRecordSaveStatus.asObservable();

  constructor(private http: HttpClient) { }

  saveStatusReset(): void {
    if(this.fundRecordSaveStatus.value != Globals.savingStatus.inProgress) {
      this.fundRecordSaveStatus.next(Globals.savingStatus.none);
    }
  }

  addLoadedFunds(newFunds: FundRecordData[]): void {
    var allFunds = this.fundRecords.value;
    newFunds.forEach(newFund => {
      if(allFunds != null) {
        var index = allFunds.findIndex(fund => { return fund.ID == newFund.ID; } );
        if(index == -1) {
          // kurz s timto ID jeste nebyl nacten, proto jej pridame
          allFunds.push(newFund);
        } else {
          // kurz s timto ID je jiz nacten, proto jej pouze aktualizujeme
          allFunds[index] = newFund;
        }
      } else {
        // nebyly jeste nacteny zadne kurzy, vkladame prvni
        allFunds = [newFund];
      }
    });
    // pokud je uloziste kurzu null a nacetl jsem prazdne pole, nastavim prazdne pole i do uloziste, aby bylo jasne ze se jedna jiz o realnou hodnotu
    if(newFunds.length == 0 && this.fundRecords.value == null) {
      this.fundRecords.next([]);
    } else {
      this.fundRecords.next(allFunds);
    }
  }

  actualizeMyFund(): void {
    this.http.get<FundRecordData[]>(Globals.api.getMyFund).pipe( catchError(this.handleError)).subscribe(
      data => this.addLoadedFunds(data)
    );
  }

  actualizeInstructorFund(id: number): void {
    this.http.get<FundRecordData[]>(Globals.api.getInstructorFund + '?personId=' + id).pipe( catchError(this.handleError)).subscribe(
      data => this.addLoadedFunds(data)
    );
  }

  async saveFund(data: FundRecordData): Promise<FundRecordData | null> {
    var result = null;
    this.fundRecordSaveStatus.next(Globals.savingStatus.inProgress);
    if(data.ID == 0) {
      // vytvoreni noveho zaznamu
      await this.http.post<FundRecordData>(Globals.api.createFundRecord, data, Globals.httpOptions).pipe( catchError(this.handleError)).toPromise().then(
        (val) => {
          this.addLoadedFunds([val]),
          this.fundRecordSaveStatus.next(Globals.savingStatus.success),
          result = val
        },
        (err) => this.fundRecordSaveStatus.next(Globals.savingStatus.failed)
      );
    } else {
      // ulozeni zmen
      await this.http.post<FundRecordData>(Globals.api.updateFundRecord, data, Globals.httpOptions).pipe( catchError(this.handleError)).toPromise().then(
        (val) => {
          this.addLoadedFunds([val]),
          this.fundRecordSaveStatus.next(Globals.savingStatus.success),
          result = val
        },
        (err) => this.fundRecordSaveStatus.next(Globals.savingStatus.failed)
      );
    }
    return result;
  }

  async removeFund(id: number): Promise<null> {
    this.fundRecordSaveStatus.next(Globals.savingStatus.inProgress);
    await this.http.post<FundRecordData>(Globals.api.removeFundRecord + '?recordId=' + id, {}, Globals.httpOptions).pipe( catchError(this.handleError)).toPromise().then(
      (val) => {
        var funds = this.fundRecords.value;
        var index = funds?.findIndex(fund => { return fund.ID == id; });
        if(index != undefined && index != -1) {
          funds?.splice(index,1);
        }
        this.fundRecords.next(funds);
        this.fundRecordSaveStatus.next(Globals.savingStatus.success);
      },
      (err) => this.fundRecordSaveStatus.next(Globals.savingStatus.failed)
    );
    return null;
  } 

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong.
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // Return an observable with a user-facing error message.
    return throwError(
      'Something bad happened; please try again later.');
  }
}
