import { Price } from "./price";
import { Item, ItemType } from "./item";
import { Product, Measurement } from "./product";
import { Promotion } from "./promotion";
import { ItemQuantityError } from "./itemQuantityError";
import { New } from './new';
import { MaxQuantityHelper } from './helpers';

type CartOptions = {
  items: Item[];
  storeId: number;
  warehouseId: number;
  locationId: number;
  status: "OK" | "IN_PROGRESS";
  rate?: number
};

class Cart {
  private _items: Item[];
  private _rate: number;
  public readonly storeId: number;
  public readonly warehouseId: number;
  public readonly locationId: number;
  public readonly status: "OK" | "IN_PROGRESS";
  public news: New[] = [];

  constructor({
    items,
    storeId,
    warehouseId,
    status,
    locationId,
    rate
  }: CartOptions) {
    this._items = items;
    this.storeId = storeId;
    this.warehouseId = warehouseId;
    this.status = status;
    this.locationId = locationId;
    this._rate = 1
    if (rate) {
      this._rate = rate;
    }
  }

  get rate(): number {
    return this._rate;
  }

  get items(): Item[] {
    return this._items;
  }

  get totalDollars(): number{
    return +this.items
    .reduce((total: number, item: Item) => total + (item.totalDollars ?? 0), 0)
    .toFixed(2);
  }

  get total(): number {
    return +this.items
      .reduce((total: number, item: Item) => total + (item.total ?? 0), 0)
      .toFixed(2);
  }

  get subtotal(): number {
    return +this.items
      .reduce(
        (subtotal: number, item: Item) => subtotal + (item.subtotal ?? 0),
        0
      )
      .toFixed(2);
  }
  removeItem(id: number | string) {
    this._items = this._items.filter((item) => item.id !== id);
  }
  addItem(item: Item) {
    this._items.push(item);
  }

  get margin(): number {
    const profit = +this.items
      .reduce((total: number, item: Item) => 
      ((item.price - item.chiperPrice) * (item.quantity || 0) + total),
      0);
    return +(profit / this.subtotal).toFixed(2);
  }
  getCartForWarehouse(warehouseId: number): Cart {
    return new Cart({
      items: this.itemsByWarehouseId(warehouseId),
      storeId: this.storeId,
      warehouseId: warehouseId,
      status: this.status,
      locationId: this.locationId,
    });
  }
  itemsByWarehouseId(warehouseId: number): Item[]{
    return this._items.filter((item) => item.warehouseId === warehouseId)
  }

  /* eslint-disable @typescript-eslint/no-explicit-any */
  static from({ items, storeId, warehouseId, status, locationId }: any, rate:number, isBackend?: boolean, isOffline?: boolean): Cart {
    return new Cart({
      items: items.map((itemRaw: any) => {
        let item: Item;
        switch (itemRaw.type) {
          case ItemType.PRODUCT:
            {
              item = Product.from(itemRaw, rate, isBackend, isOffline);
              break;
            }
          case ItemType.COMBO:
            item = Promotion.from(itemRaw, rate);
            break;
          default:
            throw new Error("invalid productType: " + itemRaw.productType);
        }
        return item;
      }),
      storeId,
      warehouseId,
      status,
      locationId,
      rate
    });
  }

  static fromShopCart({
    carts,
    storeId,
    warehouseId,
    status,
    locationId,
    rate
  }: any): Cart {
    return new Cart({
      items: carts[0].items.map((itemRaw: any) => {
        let item: Item;
        switch (itemRaw.productType) {
          case ItemType.PRODUCT:
            {
              item = Product.fromShopCart(
                Object.assign({}, itemRaw, { prices: itemRaw.products[0].prices }),
                rate
              );
              break;
            }
          case ItemType.COMBO:
            item = Promotion.fromShopCart(itemRaw, rate);
            break;
          default:
            throw new Error("invalid productType: " + itemRaw.productType);
        }
        return item;
      }),
      storeId,
      warehouseId,
      status,
      locationId,
      rate
    });
  }

  // toJSON function
  toJSON() {
    return {
      items: this.items.map((item) => item.toJSON()),
      storeId: this.storeId,
      warehouseId: this.warehouseId,
      status: this.status,
      locationId: this.locationId,
      total: this.total,
      totalDollars: this.totalDollars,
      subtotal: this.subtotal,
      news: this.news,
      rate: this.rate
    };
  }
}

export {
  Price,
  Item,
  Product,
  Promotion,
  Cart,
  CartOptions,
  ItemQuantityError,
  Measurement,
  MaxQuantityHelper,
};
