import { CartEvent } from '../event';
import { Item, ItemOptions, ItemType } from "./item";
import { New } from './new';
import { Price, PriceOptions } from "./price";
import { Product } from "./product";

export type PromotionOptions = ItemOptions & {
  products: Product[];
  termsUrl: string;
  banner?: string;
  maxQuantity?: number;
};

export class Promotion extends Item {
  public readonly products: Product[];
  public readonly termsUrl: string;
  public readonly banner?: string;
  public readonly _maxQuantity?: number;

  constructor({
    stock,
    multipleQuantity,
    name,
    warehouseId,
    image,
    id,
    packagingType,
    products,
    termsUrl,
    banner,
    quantity,
    maxQuantity,
  }: PromotionOptions, rate: number) {
    super(ItemType.COMBO, {
      stock,
      multipleQuantity,
      name,
      image,
      id,
      packagingType,
      warehouseId,
      measurement:{quantity:0, unit:""}
    }, rate);
    this.products = products;
    this.termsUrl = termsUrl;
    this.banner = banner;
    this._maxQuantity = maxQuantity;
    this.quantity = quantity;
  }

  override get stock(): number {
    if (
      this.products &&
      this.products.length > 0 &&
      this.products.every((p) => p.stock !== undefined)
    ) {
      return Math.min(
        ...this.products.map((p) => Math.floor(p.stock / p.multipleQuantity))
      );
    }
    return this._stock;
  }

  override set stock(stock: number) {
    this._stock = stock;
    for (const product of this.products) {
      product.stock = stock * product.multipleQuantity;
    }
    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);
    }
  }

  get subtotal(): number | null {
    if(this.quantity == null) return null;
    return +this.products
      .reduce((total: number, product: Product) => total + (product.subtotal??0), 0)
      .toFixed(2);
  }

  get total(): number | null {
    if(this.quantity == null) return null;
    return +this.products.reduce(
      (total: number, product: Product) => total + (product.total??0),
      0
    ).toFixed(2);
  }

  get totalDollars(): number | null {
    if(this.quantity == null) return null;
    return +(this.products.reduce(
      (total: number, product: Product) => total + (product.total??0),
      0
    )* this.rate).toFixed(2);
  }

  increase() {
    this.quantity = this.quantity == null ? this.minQuantity : this.quantity + this.multipleQuantity;
  }

  decrease() {
    this.quantity = this.quantity == null ? this.minQuantity : this.quantity - this.multipleQuantity;
  }

  override set quantity(quantity: number | undefined) {
    super.quantity = quantity;
    if(quantity != null) {
      this.products.forEach((product: Product) => {
        product.quantity = quantity * product.multipleQuantity;
      });
    }
  }

  override get quantity(): number | undefined {
    return super.quantity;
  }

  get regularPrice(): number {
    return +this.products.reduce(
      (total: number, product: Product) =>
        total + product.multipleQuantity * product.regularPrice,
      0
    ).toFixed(2);
  }

  get regularPriceDolar(): number{
    return +(this.products.reduce(
      (total: number, product: Product) =>
        total + product.multipleQuantity * product.regularPrice,
      0
    ) * this.rate).toFixed(2);
  }

  get price(): number {
    return +this.products.reduce(
      (total: number, product: Product) =>
        total + product.multipleQuantity * product.price,
      0
    ).toFixed(2);
  }

  get discountedPrice(): number | undefined {
    if (
      this.products.some((product) => product.discountedPrice !== undefined)
    ) {
      return +this.products.reduce((total: number, product: Product) => {
        return (
          total +
          product.multipleQuantity *
            (product.discountedPrice ?? product.regularPrice)
        );
      }, 0).toFixed(2);
    }
    return undefined;
  }

  get chiperPrice(): number {
    return +this.products.reduce((total: number, product: Product) => (
        product.multipleQuantity * (product.chiperPrice || product.regularPrice) + total
      )
    , 0).toFixed(2);
  }

  get maxQuantity(): number | undefined {
    if(this._maxQuantity) return this._maxQuantity;
    return this.products.reduce(
      (total: number | undefined, product: Product) => {
        let max = product.maxQuantity;
        if (max === undefined) return total;
        max = +(max / product.multipleQuantity).toFixed(0);
        return total === undefined ? max : Math.min(total, max);
      },
      undefined
    );
  }

  static fromShopCart({
    products,
    stock,
    quantity,
    name,
    medium,
    id,
    warehouseId,
    packagingType,
    termsUrl,
    banner,
    maximumQuantity,
  }: any, rate: number): any {
    return new Promotion({
      products: products.map((product: any) =>
        Product.fromShopCart(
          Object.assign(product, {
            multipleQuantity: product.quantity,
            stock: product.stock ?? stock * product.quantity,
          }), rate
        )
      ),
      warehouseId,
      stock,
      quantity,
      name,
      image: medium,
      id,
      packagingType,
      termsUrl,
      banner,
      maxQuantity: maximumQuantity,
      measurement:{quantity:0, unit:""}
    }, rate);
  }

  static from({
    products,
    quantity,
    stock,
    name,
    medium,
    id,
    warehouseId,
    packagingType,
    termsUrl,
    maximumQuantity,
    maxQuantity,
    banner,
  }: any, rate: number): Item {
    return new Promotion({
      products: products.map(
        (product: any) =>
          new Product({
            ...product,
            prices: product.prices.map(
              (p: PriceOptions) =>
                new Price({
                  ...p,
                  expireDate: p.expireDate && new Date(p.expireDate),
                })
            ),
            multipleQuantity: product.multipleQuantity ?? Math.max(product.quantity / quantity, 1),
            stock: product.stock ?? stock * product.quantity,
          }, rate)
      ),
      stock,
      quantity,
      name,
      image: medium,
      id,
      warehouseId,
      packagingType,
      termsUrl,
      maxQuantity: maxQuantity ?? maximumQuantity,
      banner,
      measurement:{quantity:0, unit:""}
    }, rate);
  }

  static fromCatalog({
    detailsDescription,
    stock,
    name,
    medium,
    id,
    warehouseId,
    packagingType,
    termsUrl,
    banner,
    quantity,
    maxQuantity,
  }: any, rate: number): Promotion {
    return new Promotion({
      products: detailsDescription.map((detail: any) =>
        Product.fromPromoDetail(detail, rate)
      ),
      stock,
      name,
      image: medium,
      id,
      warehouseId,
      packagingType,
      termsUrl,
      banner,
      quantity,
      maxQuantity,
      measurement:{quantity:0, unit:""}
    }, rate);
  }

  clone(): Promotion {
    return new Promotion({
      id: this.id,
      warehouseId: this.warehouseId,
      name: this.name,
      image: this.image,
      packagingType: this.packagingType,
      products: this.products,
      quantity: this.quantity,
      stock: this.stock,
      termsUrl: this.termsUrl,
      banner: this.banner,
      minQuantity: this.minQuantity,
      multipleQuantity: this.multipleQuantity,
      measurement:{quantity:0, unit:""}
    }, this.rate);
  }

  // create toJSON function
  toJSON(): any {
    return {
      ...super.toJSON(),
      products: this.products.map((product: Product) => product.toJSON()),
      termsUrl: this.termsUrl,
      banner: this.banner,
      citrusAdId: ""
    };
  }
}
