// Angular Core
import { EventEmitter, Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';

// RxJs
import { throwError } from 'rxjs';
import { catchError } from "rxjs/operators";

// Services
import { ApiService } from '../api/api.service';

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

  constructor(private http: HttpClient, public api: ApiService) { }

  RefreshQuickCart = new EventEmitter<any>();
  QuickCart = new EventEmitter<any>();
  updateShippingInfo = new EventEmitter<any>();
  checkoutClick = new EventEmitter<any>();
  emitErrorReturn = new EventEmitter<any>();
  clickRefreshEmitter = new EventEmitter<any>();
  showCartRefreshEmitter = new EventEmitter<any>();
  cartConfirmClickBackEmitter = new EventEmitter<any>();
  checkPurchaseQuantityEmitter = new EventEmitter<any>();
  showCartConfirmationModalEmitter = new EventEmitter<any>();
  changeSuppProductsEmitter = new EventEmitter<any>();
  checkNotifyEmitter = new EventEmitter<any>();
  cartCompleteOrderEmitter = new EventEmitter<any>();
  cartErrorEmitter = new EventEmitter<any>();
  detectMessageChangesEmitter = new EventEmitter<any>();
  checkoutErrorCloseEmitter = new EventEmitter<any>();

  /**
   * Call back error method for cart service
   */
  errorCallBack(response: any) {
    this.api.errorCallback(response);
  };

  /**
   * Gets shopping cart data
   * @param {boolean} trackCheckout
   * @param {number} supplierId
   * @returns {object} promise
   */
  getShoppingCart(trackCheckout: boolean = false, supplierId: number = 0) {
    let supplierParam = (supplierId > 0) ? '&supplierId=' + supplierId : '';
    return this.api.read('/Cart-Cart?trackCheckout='+trackCheckout + supplierParam);
  };

  /**
   * remove from cart an specific auction for the logged in user
   * @returns {object} promise
   */
  removeFromCart(items: any) {
    var data = {
      auctionIds: items
    };
    return this.api.create('/Cart-RemoveFromCart', data);
  };

  /**
   * add to cart an specific auction for the logged in user
   * @returns {object} promise
   */
  addToCart(auction_id: number, supplier_id: number, qty: number, fromPage: any, sourceName?: any) {
    let data: any = {};
    data[auction_id] = {
      'quantity': qty,
      'supplierId' : supplier_id
    };
    if (fromPage !== undefined && fromPage !== '' && fromPage !== null) {
      data[auction_id]['from_page'] = fromPage;
    }
    if (sourceName !== undefined && sourceName !== '' && sourceName !== null) {
        data[auction_id]['sourceName'] = sourceName;
    }
    return this.api.create('/Cart-UpdateCart', data);
  };

  updatePriceChangeConfirmation(data: any) {
    return this.api.create('/Cart-UpdateCart', data);
  }

   /**
    * Gets Payment Information
    * @param {number} paymentId 
    * @param {number} suppId
    * @returns {object} promise
   */
   getPaymentInfo(paymentId: number, suppId: number) {
      var data = {
        payment_id: paymentId,
        supplier_id: suppId
      };
      return this.api.create('/Cart-Checkout/getPaymentInfo', data);
    }

  /**
  * checkOut for users cart
  * @returns {object} promise
  */
  checkOut(cartData: any) {
    return this.api.create('/Cart-Checkout', cartData);
  };

  /**
  * order process for users cart
  * @returns {object} promise
  */
  processOrder(orderData: any) {
    return this.api.create('/Cart-OrderProcess', orderData);
  };

  /**
   * Savings for products in cart.
   * @returns {unresolved}
   */
  getSavings() {
    return this.api.read('/Cart-Cart/getSavings');
  };

  /**
   * Change Supplier 
   * @type object
   */
  changeSupplier: any = {
      searchData: {
          auctionId: 0,
          searchTerms: {
             categoryCode: 'All',
             searchInput: ''
          },
          searchOptions: {
              type: 'ChangeSupplier',
              filters: {
                  shortDate: [],
                  manufacturer: [],
                  supplier: [],
                  packsize: [],
                  strength: [],
                  dosageForm: [],
                  vawdOnly:[],
                  brandOrGeneric:[],
                  cntrlSchedules:[],
                  ADR: [],
                  productCategory: []
              },
              ndcSpecific: false,
              searchSortColumn: 'price_per_unit',
              searchSortOrder: 'asc',
              searchLimit: 100,
              changeInFilter: false,
          },
          staticFilters: {
              shortDate: [],
              manufacturer: [],
              supplier: [],
              packsize: [],
              strength: [],
              dosageForm: [],
              vawdOnly: [],
              brandOrGeneric:[],
              cntrlSchedules:[],
              ADR: [],
              productCategory: []
          },
      }
  };

  /**
   * Change Supplier Default 
   * @type object
   */
  changeSupplierDefault = {
      searchData: {
          auctionId:0,
          searchTerms: {
             categoryCode: 'All',
             searchInput: ''
          },
          searchOptions: {
              type: 'ChangeSupplier',
              filters: {
                  shortDate: [],
                  manufacturer: [],
                  supplier: [],
                  packsize: [],
                  strength: [],
                  dosageForm: [],
                  vawdOnly: [],
                  brandOrGeneric:[],
                  cntrlSchedules:[]
              },
              ndcSpecific: false,
              searchSortColumn: 'price_per_unit',
              searchSortOrder: 'asc',
              searchLimit: 100,
              changeInFilter: false,
          },
          staticFilters: {
              shortDate: [],
              manufacturer: [],
              supplier: [],
              packsize: [],
              strength: [],
              dosageForm: [],
              vawdOnly: [],
              brandOrGeneric:[],
              cntrlSchedules:[]
          },
      }
  };

  /**
   * sets category code in search service
   * @param {string} code
   */
  setChangeSupplierCategoryCode(code: any) {
    if (typeof code === 'string') {
      this.changeSupplier.searchData.searchTerms.categoryCode = code;
    }
  };

  /**
   * sets search input term for change supplier
   * @param {string} term
   */
  setChangeSupplierSearchTerm(term: any) {
    if (typeof term === 'string') {
      this.changeSupplier.searchData.searchTerms.searchInput = term;
    }
  };

  /**
   * sets auction Id for change supplier
   * @param id
   */
  setChangeSupplierAuctionId(id: number) {
    this.changeSupplier.searchData.auctionId = id;
  };

  /**
   * sets the strength for change suppplier
   * @param strength
   */
  setChangeSupplierStrength(strength:any) {
    this.changeSupplier.searchData.searchOptions.filters.strength = [strength] ;
  };

  /**
   * sets the dosage form for change supplier
   * @param dosage
   */
  setChangeSupplierDosage(dosage:any) {
    this.changeSupplier.searchData.searchOptions.filters.dosageForm = [dosage];
  };

  /**
   * Sets search type for change supplier: NDCName/TypeAhead etc...
   * @param {string} type
   */
  setChangeSupplierSearchType(type: string) {
    if (typeof type === 'string') {
      this.changeSupplier.searchData.searchOptions.type = type;
    }
  };

  /**
   * sets search result limit from the backend for change supplier
   * @param {number} limit
   */
  setChangeSupplierSearchLimit(limit: number) {
    if (typeof limit === 'number') {
      this.changeSupplier.searchData.searchOptions.searchLimit = limit;
    }
  };

  /**
   * gets current search term
   * @returns {String|changeSupplier.searchData.searchTerms.searchInput}
   */
  getChangeSupplierSearchTerm() {
    return this.changeSupplier.searchData.searchTerms.searchInput;
  };

  /*
   * gets current search term category code 
   * @returns {String|changeSupplier.searchData.searchTerms.categoryCode}
   */
  getChangeSupplierCategoryCode() {
    return this.changeSupplier.searchData.searchTerms.categoryCode;
  };

  /**
   * gets the change supplier query configuration
   * @returns {object} changeSupplierDefaultConfig
   */
  getChangeSupplierSearchQuery() {
    return this.changeSupplier;
  };

  /**
   * sets the supplier Id's for change suppplier
   * @param supplierId
   */
  setChangeSupplierSpecific(supplierId:any) {
    this.changeSupplier.searchData.searchOptions.filters.supplier.push(supplierId);
  };

  /**
   * resets filters for change Supplier search
   * @returns {undefined}
   */
  resetChangeSupplierFilter(filterName:string) {
    this.changeSupplier.searchData.searchOptions.filters[filterName] = [];
  };

  /**
   * Sets Ndc specific flag for changesupplier
   * @param {boolean} ndcSpecific
   * @returns {undefined}
   */
  setChangeSupplierNdcSpecific(ndcSpecific: boolean) {
    if (typeof (ndcSpecific) === 'boolean') {
      this.changeSupplier.searchData.searchOptions.ndcSpecific = ndcSpecific;
    }
  };

  /**
   * resets the chane supplier search query to default config
   */
  resetChangeSupplierSearchQuery() {
    this.changeSupplier = this.changeSupplierDefault;
  };

  /**
   * Search method to perform a search based on changeSupplier object      
   * @returns {object} ajax promise
   */
  similarProducts() {
    return this.api.create('/User-Search', this.changeSupplier);
  };

  reOrderItems(data: any) {
    return this.api.create('/Cart-UpdateCart', data);
  }

  recommendedProducts(data: any) {
    return this.api.create('/Cart-Recommended', data);
  };
  /**
   * Time conversion is done with respect to current time in
   * Eastern Standard Time irrespective of user's current timezone.
   * The user's timezone may or may not observe Daylight Savings hence 
   * this method determines whether if the EST is running in Daylight 
   * Savings and returns the relative offset with respect to UTC.
   * 
   * Standard Offset w.r.t UTC are
   * EST: -05:00 Hours
   * EDT: -04:00 Hours
   * 
   * @returns (Number} offset
   */
  getEstOffset() {
    var now = new Date();
    var dstStartTime = new Date();
    var dstEndTime = new Date();

    // Set time to March 1st
    dstStartTime.setMonth(2);
    dstStartTime.setDate(1);
    dstStartTime.setHours(2);
    dstStartTime.setMinutes(0);
    dstStartTime.setSeconds(0);
    // Set the day to first Sunday
    if (dstStartTime.getDay()) {
      dstStartTime.setDate(dstStartTime.getDate() + (7 - dstStartTime.getDay()));
    }
    // Set to second Sunday
    dstStartTime.setDate(dstStartTime.getDate() + 7);

    // Set time to March 1st
    dstEndTime.setMonth(10);
    dstEndTime.setDate(1);
    dstEndTime.setHours(2);
    dstEndTime.setMinutes(0);
    dstEndTime.setSeconds(0);
    // Set the day to November 1st
    if (dstEndTime.getDay()) {
      dstEndTime.setDate(dstEndTime.getDate() + (7 - dstEndTime.getDay()));
    }
    // If true, it means EST is observing DST
    if (now > dstStartTime && now < dstEndTime) {
      return 14400000;
    }
    return 18000000;
  };

  /**
   * Converts the supplier cutoff time into milliseconds.
   * 
   * @param string timezoneId
   * @param string supplierCutOffTime
   * @returns Date
   */
  getSupplierCutOffTimeObj(estOffset: any, supplierCutOffTime: any) {
    //Initialize date in supplier time zone
    var now = new Date();
    var d = new Date();
    var time = d.setHours(now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds()) + (d.getTimezoneOffset() * 60 * 1000);
    time = (time - estOffset);
    var currentTime = new Date(time);
    //Extract time from the cutoff_time string
    var timeParts = supplierCutOffTime.match(/(\d+):(\d+) (AM|PM)/);
    var hours = 0;
    var minutes = 0;
    currentTime.setDate(now.getDate());
    if (timeParts) {
      hours = parseInt(timeParts[1]);
      minutes = parseInt(timeParts[2]);
      if (timeParts[3] === 'PM' && hours < 12) {
        hours += 12;
      }
      currentTime.setHours(hours, minutes, 0, 0);
    }
    return currentTime;
  };

  /**
   * This method introduced as per TRX-7562
   * Gets Prime supplier estimated delivery grouped products
   * 
   * @param array data
   * @returns {object} promise
  */
  getETAProducts(data: any) {
    return this.api.create('/Cart-ETAProducts', data);
  };

  /**
  * This method introducted as per TRX-7802
  * Grouped prime supplier products 
  * Based on estimation delivery time.
  * and assigning to supplier object
  * 
  * @param object etaItems
  * @return result
 */
  groupETAItems(etaItems: any) {
    // Object.values is not working in IE Browser   
    etaItems = Object.keys(etaItems).map(function (item) { return etaItems[item]; });
    let result = etaItems.reduce(function (r: any, a: any) {
      r[a.deliveryEstimation] = r[a.deliveryEstimation] || [];
      r[a.deliveryEstimation].push(a);
      return r;
    }, Object.create(null));
    return result;
  };

  /**
   * This method introducted as per TRX-7562
   * Gets prime supplier products 
   * with estimation delivery time.
   * and assigning to supplier object
   * 
   * @param object supplier
   * @param string methodName
   * @return {undefined}
  */
  getETADLVRItems(supplier: any, methodName: any) {
    var cartService = this;
    var data = {
      "supplierId": supplier.supplierData.id,
      "cutOffTime": supplier.shippingData.cutoff_time,
      "shippingMethod": methodName,
      "isFirstOrder": supplier.supplierData.is_first_order,
      "products": supplier.items
    };

    cartService.getETAProducts(data)
      .pipe(
        catchError(() => {
          return throwError(() => new Error('ups sommething happend'));
        })
      )
      .subscribe({
        next: (res: any) => {
          supplier.items = res.data;
          supplier.etaItems = cartService.groupETAItems(supplier.items);
        },
        error: (err: HttpErrorResponse) => {
          this.errorCallBack(err);
        },
        complete: () => { }
      });

  };

  /**
  * checkOut session update for digital payment suppliers
  * @returns {object} promise
  */
  checkoutSessionUpdate(cartData: any) {
    return this.api.create('/Cart-Checkout/reviewOrder', cartData);
  };
  
    /**
     * Saves or updates the selected payment method for a given supplier.
     *
     * @param suppId
     * @param paymentId
     * @param paymentMethod
     * @returns An Observable representing the API response.
     */
    saveSuppPayMethod(suppId: number, paymentId: any, paymentMethod: any) {
        var data = {
          supplierId: suppId,
          paymentMethodId: paymentId,
          paymentMethodName: paymentMethod
        };
        return this.api.create('/Cart-UpdateCart/saveSuppPayMethod', data);
    }
    
    /**
     * Saves or updates all supplier payment methods selected by the user in the cart.
     *
     * @param userCartPayMethods
     * @returns An Observable representing the API response.
     */
    saveUserCartpayMethod(userCartPayMethods: any) {
        var data = {
          userSuppCartPayMethods: userCartPayMethods,
        };
        return this.api.create('/Cart-UpdateCart/saveUserCartPayMethods', data);
    }
}
