import { ProductVariantModel } from '../../entities/product-variant.entity';
import { IProductVariantWithInventoryAttr } from '../../interfaces/product-variant-with-inventory-attr.interface';
import { ProductVariant } from '../variant-product/variant-product';
import { ProductVariantWithInventoryRepository } from './variant-product-with-inventory.repository';
import { ClassError } from '@tomei/general';
import { YN } from '../../enum/yn.enum';
import {
  ProductVariantStatus,
  ProductVariantType,
} from '../../enum/product-variant.enum';
import { LoginUser } from '@tomei/sso';
import { ActionEnum, Activity } from '@tomei/activity-history';
import * as cuid from 'cuid';
import { IProductVariantWithInventoryUpdate } from '../../interfaces/product-variant-with-inventory-update.interface';

export class ProductVariantWithInventory extends ProductVariant {
  ObjectType = 'ProductVariantWithInventory';
  protected _TotalUnitsAvailable: number = 0;
  protected _TotalUnitsInCurrentOrder: number = 0;
  protected _TotalUnitsSold: number = 0;
  protected _TotalUnitsReserved: number = 0;
  protected _TotalUnitsInTransit: number = 0;
  protected _TotalUnitsOnConsignment: number = 0;
  protected _TotalUnitsBackOrdered: number = 0;
  protected _TotalUnitsPreOrdered: number = 0;
  StockLowAlertLevel: number = 0;
  StockReorderLevel: number = 0;
  BufferStockLevel: number = 0;
  private static _Repository = new ProductVariantWithInventoryRepository();

  get TotalUnitsAvailable(): number {
    return this._TotalUnitsAvailable;
  }

  get TotalUnitsInCurrentOrder(): number {
    return this._TotalUnitsInCurrentOrder;
  }

  get TotalUnitsSold(): number {
    return this._TotalUnitsSold;
  }

  get TotalUnitsReserved(): number {
    return this._TotalUnitsReserved;
  }

  get TotalUnitsInTransit(): number {
    return this._TotalUnitsInTransit;
  }

  get TotalUnitsOnConsignment(): number {
    return this._TotalUnitsOnConsignment;
  }

  get TotalUnitsBackOrdered(): number {
    return this._TotalUnitsBackOrdered;
  }

  get TotalUnitsPreOrdered(): number {
    return this._TotalUnitsPreOrdered;
  }

  get CreatedAt(): Date {
    return super.CreatedAt;
  }

  get UpdatedAt(): Date {
    return super.UpdatedAt;
  }

  get CreatedById(): string {
    return super.CreatedById;
  }

  get UpdatedById(): string {
    return super.UpdatedById;
  }

  get UpdatedSSYN(): YN {
    return super.UpdatedSSYN;
  }

  get SKU(): string {
    return super.SKU;
  }

  get Size(): string {
    return super.Size;
  }

  get Colour(): string {
    return super.Colour;
  }

  get MinWeight(): number {
    return super.MinWeight;
  }

  get MaxWeight(): number {
    return super.MaxWeight;
  }

  get MinWidth(): number {
    return super.MinWidth;
  }

  get MaxWidth(): number {
    return super.MaxWidth;
  }

  get MinHeight(): number {
    return super.MinHeight;
  }

  get MaxHeight(): number {
    return super.MaxHeight;
  }

  get MinLength(): number {
    return super.MinLength;
  }

  get MaxLength(): number {
    return super.MaxLength;
  }

  get ParentId(): string | null {
    return super.ParentId;
  }

  get Status(): ProductVariantStatus {
    return super.Status;
  }

  get VariantId(): string {
    return super.VariantId;
  }

  set VariantId(variantId: string) {
    super.VariantId = variantId;
  }

  get ProductId(): string {
    return super.ProductId;
  }

  set ProductId(productId: string) {
    super.ProductId = productId;
  }

  get Name(): string {
    return super.Name;
  }

  set Name(name: string) {
    super.Name = name;
  }

  get Description(): string {
    return super.Description;
  }

  set Description(description: string) {
    super.Description = description;
  }

  get Type(): ProductVariantType {
    return super.Type;
  }

  set Type(type: ProductVariantType) {
    super.Type = type;
  }

  get Level(): number {
    return super.Level;
  }

  set Level(level: number) {
    super.Level = level;
  }

  setSKU(SKU: string): Promise<void> {
    return super.setSKU(SKU);
  }

  setSize(size: string): Promise<void> {
    return super.setSize(size);
  }

  setColour(colour: string): Promise<void> {
    return super.setColour(colour);
  }

  protected constructor(variant?: IProductVariantWithInventoryAttr) {
    super(variant);
    if (variant) {
      this._TotalUnitsAvailable = variant.TotalUnitsAvailable;
      this._TotalUnitsInCurrentOrder = variant.TotalUnitsInCurrentOrder;
      this._TotalUnitsSold = variant.TotalUnitsSold;
      this._TotalUnitsReserved = variant.TotalUnitsReserved;
      this._TotalUnitsInTransit = variant.TotalUnitsInTransit;
      this._TotalUnitsOnConsignment = variant.TotalUnitsOnConsignment;
      this._TotalUnitsBackOrdered = variant.TotalUnitsBackOrdered;
      this._TotalUnitsPreOrdered = variant.TotalUnitsPreOrdered;
      this.StockLowAlertLevel = variant.StockLowAlertLevel;
      this.StockReorderLevel = variant.StockReorderLevel;
      this.BufferStockLevel = variant.BufferStockLevel;
      this.VariantId = variant.VariantId;
      this.ProductId = variant.ProductId;
      this.Name = variant.Name;
      this.Description = variant.Description;
      this.Type = variant.Type;
      this.Level = variant.Level;
    }
  }

  static async init(dbTransaction?: any, variantId?: string) {
    if (variantId) {
      try {
        const variant = await this._Repository.findOne({
          where: { VariantId: variantId },
          include: [
            {
              model: ProductVariantModel,
            },
          ],
          transaction: dbTransaction,
        });

        const data: IProductVariantWithInventoryAttr = {
          VariantId: variant.ProductVariant.VariantId,
          ProductId: variant.ProductVariant.ProductId,
          Name: variant.ProductVariant.Name,
          Description: variant.ProductVariant.Description,
          SKU: variant.ProductVariant.SKU,
          Size: variant.ProductVariant.Size,
          Colour: variant.ProductVariant.Colour,
          Type: variant.ProductVariant.Type,
          Level: variant.ProductVariant.Level,
          ParentId: variant.ProductVariant.ParentId,
          MinWeight: variant.ProductVariant.MinWeight,
          MaxWeight: variant.ProductVariant.MaxWeight,
          MinWidth: variant.ProductVariant.MinWidth,
          MaxWidth: variant.ProductVariant.MaxWidth,
          MinHeight: variant.ProductVariant.MinHeight,
          MaxHeight: variant.ProductVariant.MaxHeight,
          MinLength: variant.ProductVariant.MinLength,
          MaxLength: variant.ProductVariant.MaxLength,
          Status: variant.ProductVariant.Status,
          CreatedById: variant.ProductVariant.CreatedById,
          CreatedAt: variant.ProductVariant.CreatedAt,
          UpdatedById: variant.ProductVariant.UpdatedById,
          UpdatedAt: variant.ProductVariant.UpdatedAt,
          UpdatedSSYN: variant.ProductVariant.UpdatedSSYN,
          TotalUnitsAvailable: variant.TotalUnitsAvailable,
          TotalUnitsInCurrentOrder: variant.TotalUnitsInCurrentOrder,
          TotalUnitsSold: variant.TotalUnitsSold,
          TotalUnitsReserved: variant.TotalUnitsReserved,
          TotalUnitsInTransit: variant.TotalUnitsInTransit,
          TotalUnitsOnConsignment: variant.TotalUnitsOnConsignment,
          TotalUnitsBackOrdered: variant.TotalUnitsBackOrdered,
          TotalUnitsPreOrdered: variant.TotalUnitsPreOrdered,
          StockLowAlertLevel: variant.StockLowAlertLevel,
          StockReorderLevel: variant.StockReorderLevel,
          BufferStockLevel: variant.BufferStockLevel,
        };

        return new ProductVariantWithInventory(data);
      } catch (err) {
        throw new ClassError(
          'ProductVariantWithInventory',
          'ProductVariantWithInventoryErrMsg01',
          'VariantId not found.',
        );
      }
    }
    return new ProductVariantWithInventory();
  }

  public async create(loginUser: LoginUser, dbTransaction: any) {
    try {
      await super.create(loginUser, dbTransaction);

      const data = {
        VariantId: this.VariantId,
        TotalUnitsAvailable: this.TotalUnitsAvailable,
        TotalUnitsInCurrentOrder: this.TotalUnitsInCurrentOrder,
        TotalUnitsSold: this.TotalUnitsSold,
        TotalUnitsReserved: this.TotalUnitsReserved,
        TotalUnitsInTransit: this.TotalUnitsInTransit,
        TotalUnitsOnConsignment: this.TotalUnitsOnConsignment,
        TotalUnitsBackOrdered: this.TotalUnitsBackOrdered,
        TotalUnitsPreOrdered: this.TotalUnitsPreOrdered,
        StockLowAlertLevel: this.StockLowAlertLevel,
        StockReorderLevel: this.StockReorderLevel,
        BufferStockLevel: this.BufferStockLevel,
      };

      await ProductVariantWithInventory._Repository.create(data, {
        transaction: dbTransaction,
      });

      const activity = new Activity();
      activity.ActivityId = cuid();
      activity.Action = ActionEnum.CREATE;
      activity.Description = 'Add new product variant with inventory';
      activity.EntityType = 'ProductVariantWithInventory';
      activity.EntityId = data.VariantId;
      activity.EntityValueBefore = JSON.stringify({});
      activity.EntityValueAfter = JSON.stringify(data);

      await activity.create(loginUser.ObjectId, dbTransaction);

      return this;
    } catch (error) {
      throw error;
    }
  }

  public async update(
    payload: IProductVariantWithInventoryUpdate,
    loginUser: LoginUser,
    dbTransaction?: any,
  ) {
    await super.update(payload, loginUser, dbTransaction);

    const entityValueBefore = {
      TotalUnitsAvailable: this.TotalUnitsAvailable,
      TotalUnitsInCurrentOrder: this.TotalUnitsInCurrentOrder,
      TotalUnitsSold: this.TotalUnitsSold,
      TotalUnitsReserved: this.TotalUnitsReserved,
      TotalUnitsInTransit: this.TotalUnitsInTransit,
      TotalUnitsOnConsignment: this.TotalUnitsOnConsignment,
      TotalUnitsBackOrdered: this.TotalUnitsBackOrdered,
      TotalUnitsPreOrdered: this.TotalUnitsPreOrdered,
      StockLowAlertLevel: this.StockLowAlertLevel,
      StockReorderLevel: this.StockReorderLevel,
      BufferStockLevel: this.BufferStockLevel,
    };

    this.StockLowAlertLevel = payload.StockLowAlertLevel;
    this.StockReorderLevel = payload.StockReorderLevel;
    this.BufferStockLevel = payload.BufferStockLevel;

    await ProductVariantWithInventory._Repository.update(
      {
        StockLowAlertLevel: this.StockLowAlertLevel,
        StockReorderLevel: this.StockReorderLevel,
        BufferStockLevel: this.BufferStockLevel,
      },
      {
        where: { VariantId: this.VariantId },
        transaction: dbTransaction,
      },
    );

    const entityValueAfter = {
      TotalUnitsAvailable: this.TotalUnitsAvailable,
      TotalUnitsInCurrentOrder: this.TotalUnitsInCurrentOrder,
      TotalUnitsSold: this.TotalUnitsSold,
      TotalUnitsReserved: this.TotalUnitsReserved,
      TotalUnitsInTransit: this.TotalUnitsInTransit,
      TotalUnitsOnConsignment: this.TotalUnitsOnConsignment,
      TotalUnitsBackOrdered: this.TotalUnitsBackOrdered,
      TotalUnitsPreOrdered: this.TotalUnitsPreOrdered,
      StockLowAlertLevel: this.StockLowAlertLevel,
      StockReorderLevel: this.StockReorderLevel,
      BufferStockLevel: this.BufferStockLevel,
    };

    const activity = new Activity();
    activity.ActivityId = cuid();
    activity.Action = ActionEnum.UPDATE;
    activity.Description = 'Update product variant with inventory';
    activity.EntityType = 'ProductVariantWithInventory';
    activity.EntityId = this.VariantId;
    activity.EntityValueBefore = JSON.stringify(entityValueBefore);
    activity.EntityValueAfter = JSON.stringify(entityValueAfter);

    await activity.create(loginUser.ObjectId, dbTransaction);

    return this;
  }

  public async delete(loginUser: LoginUser, dbTransaction?: any) {
    try {
      const entityValueBefore = {
        TotalUnitsAvailable: this.TotalUnitsAvailable,
        TotalUnitsInCurrentOrder: this.TotalUnitsInCurrentOrder,
        TotalUnitsSold: this.TotalUnitsSold,
        TotalUnitsReserved: this.TotalUnitsReserved,
        TotalUnitsInTransit: this.TotalUnitsInTransit,
        TotalUnitsOnConsignment: this.TotalUnitsOnConsignment,
        TotalUnitsBackOrdered: this.TotalUnitsBackOrdered,
        TotalUnitsPreOrdered: this.TotalUnitsPreOrdered,
        StockLowAlertLevel: this.StockLowAlertLevel,
        StockReorderLevel: this.StockReorderLevel,
        BufferStockLevel: this.BufferStockLevel,
      };

      await ProductVariantWithInventory._Repository.delete(
        this.VariantId,
        dbTransaction,
      );

      const activity = new Activity();
      activity.ActivityId = cuid();
      activity.Action = ActionEnum.DELETE;
      activity.Description = 'Delete product variant with inventory';
      activity.EntityType = 'ProductVariantWithInventory';
      activity.EntityId = this.VariantId;
      activity.EntityValueBefore = JSON.stringify(entityValueBefore);
      activity.EntityValueAfter = JSON.stringify({});
      await activity.create(loginUser.ObjectId, dbTransaction);

      await super.delete(loginUser, dbTransaction);
      return {
        MessageCode: 'ProductVariantWithInventoryDeleted',
        Message: 'Product Variant With Inventory is deleted.',
      };
    } catch (error) {
      throw error;
    }
  }
}
