import { HttpService } from './http.service';
import { parseImage, parsePrice, decimalPrice } from './utils';
import axios from 'axios';

const CancelToken = axios.CancelToken;

export class ResolveService {
  private _NFHttp: any;
  private _mainCancel: Function;
  private _recipes:any;

  total:any = {};
  products = [];
  ingredients = [];
  resolving:boolean = false;


  constructor( config?:any ) {
    config = { ...config || {} };

    this._NFHttp = new HttpService( { ...{
      host: 'https://api.northfork.se/api',
    }, ...config } );
  }

  cancel( type?:string ) {
    if( ( !type || type ==='main' ) && this._mainCancel ) this._mainCancel();
    if( !type || type ==='diff' ) {
    }
  }

  /**
   * the recipe id can be gastrofy_id or external_id:
   * { gastrofy_id: 1, portions: 1 }
   * OR
   * { external_id: 1, portions: 1 }
   * 
   * options: {
   *   tags: Array,
   *   storeProvider: String,
   *   storeIdentifier: String',
   *   productExceptions: Array,
   *   calculation: price | waste,
   *   prioritization: String,
   *   promotedBrand: String,
   *   params: {}
   * }
   */
  resolve( recipes:any, options:any ) {
    this.resolving = true;
    return this._NFHttp.post( 'smart-cart/recipes/resolve', {
      recipes: recipes,
      store: {
        tags: options.tags || [],
        provider: options.storeProvider,
        identifier: options.storeIdentifier,
      },
      options: {
        calculation: options.calculation,
        prioritization:options.prioritization,
        product_exceptions: options.productExceptions,
      },
    }, {
      params: options.params,
      promoted_brand: options.promotedBrand,
      cancelToken: new CancelToken( ( c:any ) => {
        this._mainCancel = c;
      } ),
    } ).then( ( res:any ) => {
      this.resolving = false;
      let data = res.data;
      this.products = this._formatProducts( data.products );
      this.ingredients = this._formaIngredients( data.recipes );
      this._recipes = data.recipes;
      this._calculateTotal();
      return this;
    }, ( err:any ) => {
      this.resolving = false;
      return err;
    } );
  }

  // substitute can be new quantity or new product
  substituteProduct( identifier:string, substitute:any ) {
    let original = null;

    for( let product of this.products ) {
      if( product.substitute_identifier === identifier ) {
        original = product;
        break;
      }
    }
    if( !original ) return;

    // TODO: TO FIX:
    // NOTE: The consumption of the replaced product is 0 from api, so set to 0 at frontend now
    original.consumption = 0;

    original.replaced = true;

    if( typeof substitute === 'number' ) {
      original.quantity = substitute;
    } else {
      for( let field in original ) {
        if( field === 'substitute_identifier' || typeof substitute[field] === 'undefined' ) continue;
        original[field] = substitute[field];
      }
    }

    this._calculateTotal();
  }

  productsBySections() {
    let sharedProducts = [];
    let commonProducts = [];
    let recipesProducts = [];

    for( let product of this.products ) {
      if( product.common ) commonProducts.push( product );
      else if( product.shared ) sharedProducts.push( product );
      else recipesProducts.push( product );
    }

    return {
      common: commonProducts,
      shared: sharedProducts,
      recipes: this._recipes.map( ( recipe:any ) => {
        let recipeProducts = [];
        for( let ingredient of recipe.ingredients ) {
          for( let product of recipesProducts ) {
            for( let ingredientId of product.recipe_ingredient_ids ) {
              if( ingredientId !== ingredient.id  ) continue;
              recipeProducts.push( product );
            }
          }
        }

        return {
          id: recipe.id,
          title: recipe.title,
          portions: recipe.portions,
          image_url: parseImage( recipe ),
          products: recipeProducts,
        };
      } ),
    };
  }

  productsByCategories() {
    let index = {};
    let result = [];

    for( let product of this.products ) {
      const category = product.categories[0] || { id: -1, sort_order: 999999999, title: null };
      const categoryId = category.id;
      let productIndex = index[ categoryId ];

      if( productIndex === undefined ) {
        productIndex = result.length;
        index[categoryId] = productIndex;
        result[productIndex] = {
          id: category.id,
          title: category.title,
          sort_order: category.sort_order,
          products: [],
        };
      }
      result[productIndex].products.push( product );
    }

    result.sort( ( a:any, b:any ) => {
      if( a.sort_order > b.sort_order ) return 1;
      return -1;
    } );

    return result;
  }

  private _formatProducts( data ) {
    let products = [];

    for( let productGroup of data ) {
      products = products.concat( productGroup.map( ( product:any ) => {
        product.brand = product.brand && product.brand !== 'na'? product.brand: null;
        product.price = parsePrice( product.price );
        product.is_active = product.price>0&&(product.strategy!=='out_of_stock'&&product.strategy!=='replaced_out_of_stock');
        product.replaced = product.strategy === 'replaced' || product.strategy === 'replaced_out_of_stock';
        product.recipe_ingredient_ids = product.recipe_ingredient_ids.map( item => item.id );
        product.common = product.common === true;
        product.shared = product.recipe_ingredient_ids.length > 1;
        product.name = product.name.trim();

        delete product.strategy;
        delete product.partner_attributes;
        delete product.ean;
        delete product.sub_text;

        product.ingredients = ( ( ids:any ) => {
          return () => {
            return ids.map( ( id:any ) => {
              for( let ingredient of this.ingredients ) {
                if( ingredient.id === id ) return ingredient;
              }
            } );
          };
        } )( product.recipe_ingredient_ids );

        return product;
      } ) );
    }

    return products;
  }

  private _formaIngredients( recipes:any ) {
    let ingredients = [];

    for( let recipe of recipes ) {
      ingredients = ingredients.concat( recipe.ingredients.map( ( ingredient:any ) => {
        ingredient.amount = ingredient.amount.replace( /(\.\w)0$/, ( v:any, m:any ) => {
          return m > 0 ? m: '';
        } );

        ingredient.products = ( ( ingredientId:number ) => {
          return () => {
            let result = [];
            for( let product of this.products ) {
              for( let id of product.recipe_ingredient_ids ) {
                if( id === ingredientId ) {
                  result.push( product );
                  break;
                }
              }
            }
            return result;
          };
        } )( ingredient.id );

        return ingredient;
      } ) );
    }

    return ingredients;
  }

  private _calculateTotal() {
    let total = {
      price: 0,
      price_with_common: 0,
      consumption_price: 0,
      consumption_price_with_common: 0,
      quantity: 0,
      quantity_with_common: 0,

      checkout_price: 0,
      checkout_quantity: 0,
    };

    for( let product of this.products ) {
      const curTotal = product.price * product.quantity;
      const consumptionPrice = curTotal * product.consumption;

      total.price_with_common += curTotal;
      total.consumption_price_with_common += consumptionPrice;
      total.quantity_with_common += product.quantity;

      if( !product.common || product.replaced ) {
        total.price += curTotal;
        total.consumption_price += consumptionPrice;
        total.quantity += product.quantity;
        product.quantity = product.quantity;

        if( product.is_active ) {
          total.checkout_price += curTotal;
          total.checkout_quantity += product.quantity;
        }
      }
    }

    for( let totalField in total ) {
      this.total[totalField] = decimalPrice( total[ totalField ] );
    }
  }
}
