import {Injectable} from '@angular/core';
import {ApiService} from "../../shared/service/api.service";
import {Observable} from "rxjs";
import {Recipe, RecipeStep} from "../../shared/model/recipe.model";
import {map} from "rxjs/operators";

@Injectable({
  providedIn: 'root'
})
export class RecipeService {
  constructor(
    private api: ApiService
  ) {
  }

  get all(): Observable<Recipe[]> {
    return this.api.get<Recipe[]>('/recipe')
  }

  get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> {
    return this.all.pipe(map(recipes => {
      const mapped = []
      recipes.forEach(r => {
        if (!mapped[r.company.id]) {
          mapped[r.company.id] = {company: r.company.name, recipes: []}
        }
        mapped[r.company.id].recipes.push(r)
      })
      return mapped.filter(e => e != null)  // Filter to remove empty elements in the array that appears for some reason
    }))
  }

  getById(id: number): Observable<Recipe> {
    return this.api.get<Recipe>(`/recipe/${id}`)
  }

  save(name: string, description: string, sample: number, approbationDate: string, remark: string, companyId: number): Observable<Recipe> {
    const body = {name, description, sample, remark, companyId}
    if (approbationDate) {
      // @ts-ignore
      body.approbationDate = approbationDate
    }
    return this.api.post<Recipe>('/recipe', body)
  }

  update(id: number, name: string, description: string, sample: number, approbationDate: string, remark: string, steps: RecipeStep[] = null) {
    const body = {id, name, description, sample, remark, steps}
    if (approbationDate) {
      // @ts-ignore
      body.approbationDate = approbationDate
    }
    return this.api.put<Recipe>('/recipe', body)
  }

  saveExplorerModifications(id: number, note: string, mixesLocationChange: Map<number, string>): Observable<void> {
    const body = {
      id,
      note,
      mixesLocation: {}
    }
    mixesLocationChange.forEach((l, i) => body.mixesLocation[i] = l)

    return this.api.put<void>('/recipe/public', body)
  }

  deductMixQuantities(recipe: Recipe, mixId: number, quantities: Map<number, number>): Observable<void> {
    return this.sendDeductBody(this.buildDeductMixBody(recipe, mixId, quantities))
  }

  deductQuantities(recipe: Recipe, quantities: Map<number, Map<number, number>>): Observable<void> {
    return this.sendDeductBody(this.buildDeductBody(recipe, quantities))
  }

  delete(id: number): Observable<void> {
    return this.api.delete<void>(`/recipe/${id}`)
  }

  private buildDeductMixBody(recipe: Recipe, mixId: number, quantities: Map<number, number>): any {
    const mix = recipe.mixes.filter(m => m.id === mixId)[0]
    const body = {id: recipe.id, quantities: {}}
    body.quantities[mixId] = {}
    const firstMaterial = mix.mixMaterials[0].material.id
    mix.mixMaterials.forEach(m => {
      if (quantities && quantities.has(m.material.id)) {
        body.quantities[mix.id][m.material.id] = quantities.get(m.material.id)
      } else {
        let quantity = m.quantity
        if (m.material.materialType.usePercentages) quantity = body.quantities[mix.id][firstMaterial] * (quantity / 100)
        body.quantities[mix.id][m.material.id] = quantity
      }
    })
    return body
  }

  private buildDeductBody(recipe: Recipe, quantities: Map<number, Map<number, number>>): any {
    const body = {id: recipe.id, quantities: {}}
    recipe.mixes.forEach(mix => {
      body.quantities[mix.id] = this.buildDeductMixBody(recipe, mix.id, quantities.get(mix.id)).quantities[mix.id]
    })
    return body
  }

  private sendDeductBody(body: any): Observable<void> {
    return this.api.put('/recipe/deduct', body)
  }
}
