import { EventSender, CartEvent } from "../event";
import {
  getDiscount,
  getMaximumValue,
  roundToMultipleQuantity,
} from "./helpers";
import { ItemQuantityError } from "./itemQuantityError";
import { New } from './new';

export enum ItemType {
  PRODUCT = "PRODUCT",
  COMBO = "COMBO",
}

export type ItemOptions = {
  id: number | string;
  warehouseId: number;
  name: string;
  stock: number;
  image: string;
  packagingType: string;
  minQuantity?: number;
  multipleQuantity?: number;
  quantity?: number;
  measurement:{unit:string, quantity:number}
};

export abstract class Item extends EventSender {
  public readonly id: number | string;
  public readonly warehouseId: number;
  public readonly type: ItemType;
  public readonly name: string;
  protected _quantity?: number;
  protected _stock: number;
  public readonly image: string;
  public readonly packagingType: string;
  public readonly minQuantity: number;
  public readonly multipleQuantity: number;
  public readonly rate: number;
  private isBackend:boolean | undefined;
  private isOffline: boolean | undefined;

  constructor(
    type: ItemType,
    {
      stock,
      multipleQuantity,
      name,
      image,
      id,
      warehouseId,
      packagingType,
      minQuantity,
      measurement
    }: ItemOptions, rate:number, isBackend?: boolean, isOffline?: boolean
  ) {
    super();
    this.isOffline = isOffline;
    this.isBackend = isBackend;
    this.id = id;
    this.warehouseId = warehouseId;
    this.type = type;
    this.name = name;
    this._stock = stock;
    this.image = image;
    let complementPackaging = '';
    if (measurement && measurement.quantity && measurement.unit) {
      complementPackaging = `${measurement.quantity} ${measurement.unit}`
      complementPackaging = packagingType.includes(complementPackaging) ? '' : ` ${complementPackaging}`;
    }
    this.packagingType = `${packagingType}${complementPackaging}`;
    this.minQuantity = minQuantity ?? 1;
    this.multipleQuantity = multipleQuantity ?? 1;
    this.rate = rate;
  }

  get stock(): number {
    return this._stock;
  }

  set stock(stock: number) {
    this._stock = stock;
    if (this.quantity && this.stock < this.quantity) {
      this.emit(CartEvent.NEWS, {
        id: this.id,
        cardType: this.stock === 0 ? "unavailable" : "lowStock",
        productType: this.type,
        medium: this.image,
        name: this.name,
        packagingType: this.packagingType,
        previousTotal: this.regularPrice,
        quantity: this.quantity,
        stock: this.stock,
        total: this.regularPrice,
        warehouseId: this.warehouseId
      } as New);
    }

  }

  abstract clone(): Item;
  set quantity(quantity: number | undefined) {
    if(quantity == null) {
      this._quantity = undefined;
      return;
    }
    if (quantity < this.minQuantity && !this.isOffline) {
      this.emit(CartEvent.BELOW_MIN_QUANTITY, {
        id: this.id,
        quantity: quantity,
        minQuantity: this.minQuantity,
      });
      throw new ItemQuantityError(
        `Quantity can't be less than minQuantity(${this.minQuantity})`,
        this.minQuantity,
        "MinQuantity"
      );
    }
    if (quantity > this.stock && !this.isOffline) {
      const newQuantity = getMaximumValue(
        this.stock,
        this.maxQuantity ?? this.stock,
        this.multipleQuantity
      );
      this.emit(CartEvent.OUT_OF_STOCK, {
        id: this.id,
        quantity: quantity,
        stock: this.stock,
      });
      throw new ItemQuantityError(
        `Quantity can't be more than stock(${this.stock})`,
        newQuantity,
        "Stock"
      );
    }
    if (this.maxQuantity && quantity > this.maxQuantity && !this.isOffline) {
      const newQuantity = getMaximumValue(
        this.stock,
        this.maxQuantity,
        this.multipleQuantity
      );
      if (this.isBackend === true) {
        this.quantity = newQuantity;
      }else{
        this.emit(CartEvent.EXCEEDS_MAX_QUANTITY, {
          id: this.id,
          quantity: quantity,
          maxQuantity: this.maxQuantity,
        });
        throw new ItemQuantityError(
          `Quantity can't be more than maxQuantity(${this.maxQuantity})`,
          newQuantity,
          "MaxQuantity"
        );
      }
    }

    if (quantity % this.multipleQuantity !== 0 && !this.isOffline) {
      const newQuantity = roundToMultipleQuantity(
        quantity,
        this.multipleQuantity,
        this.stock
      );
      this.emit(CartEvent.NOT_MULTIPLE_QUANTITY, {
        id: this.id,
        quantity: quantity,
        multipleQuantity: this.multipleQuantity,
      });
      throw new ItemQuantityError(
        `Quantity can't be different of a multipleQuantity(${this.multipleQuantity})`,
        newQuantity,
        "MultipleQuantity"
      );
    }
    this._quantity = quantity;
  }

  get quantity(): number | undefined{
    return this._quantity;
  }
  /** `discount` is the difference between `regularPrice` and `discountedPrice` in %*/
  get discount(): number {
    return this.discountedPrice
      ? getDiscount(this.regularPrice, this.discountedPrice)
      : 0;
  }
  abstract get maxQuantity(): number | undefined;
  abstract get regularPrice(): number;
  abstract get discountedPrice(): number | undefined;
  abstract get total(): number | null;
  abstract get subtotal(): number | null;
  abstract get chiperPrice(): number;
  abstract get price(): number;
  abstract get totalDollars(): number | null;
  abstract get regularPriceDolar(): number;

  // create toJson method
  toJSON() {
    return {
      id: this.id,
      warehouseId: this.warehouseId,
      type: this.type,
      name: this.name,
      stock: this.stock,
      quantity: this.quantity,
      image: this.image,
      medium: this.image, // deprecated
      packagingType: this.packagingType,
      minQuantity: this.minQuantity,
      multipleQuantity: this.multipleQuantity,
      total: this.total,
      subtotal: this.subtotal,
      maxQuantity: this.maxQuantity,
      maximumQuantity: this.maxQuantity, // deprecated
      regularPrice: this.regularPrice,
      discountedPrice: this.discountedPrice,
      totalDollars: this.totalDollars,
      regularPriceDolar: this.regularPriceDolar 
    };
  }
}
