import { StatusEnum } from '../../enum/status.enum';
import { ProductCollectionRepository } from './collection-product.repository';
import { LoginUser } from '@tomei/sso';
import { ApplicationConfig } from '@tomei/config';
import { IProductCollectionArr } from '../../interfaces/product-collection-attr.interface';
import { ActionEnum, Activity } from '@tomei/activity-history';
import * as cuid from 'cuid';
import { Op } from 'sequelize';
import { ObjectBase } from '@tomei/general';
import { ProductProductCollection } from '../product-collection-product/product-collection-product';

interface IproductCollectionInitParam {
  code?: string;
  collectionInfo?: IProductCollectionArr;
}

export class ProductCollection extends ObjectBase {
  ObjectId: string;
  ObjectName: string;
  TableName: string;
  ObjectType = 'ProductCollection';
  Name: string;
  Description: string;
  private _Status = StatusEnum.ACTIVE;
  private _CreatedById: string;
  private _CreatedAt: Date;
  private _UpdatedById: string;
  private _UpdatedAt: Date;
  private static _Repo = new ProductCollectionRepository();

  get Status() {
    return this._Status;
  }

  set Status(value: StatusEnum) {
    this._Status = value;
  }

  get Code() {
    return this.ObjectId;
  }

  set Code(value: string) {
    this.ObjectId = value;
  }

  get CreatedById() {
    return this._CreatedById;
  }

  set CreatedById(value: string) {
    this._CreatedById = value;
  }

  get CreatedAt() {
    return this._CreatedAt;
  }

  set CreatedAt(value: Date) {
    this._CreatedAt = value;
  }

  get UpdatedById() {
    return this._UpdatedById;
  }

  set UpdatedById(value: string) {
    this._UpdatedById = value;
  }

  get UpdatedAt() {
    return this._UpdatedAt;
  }

  set UpdatedAt(value: Date) {
    this._UpdatedAt = value;
  }

  private constructor(productCollection?: IProductCollectionArr) {
    super();
    if (productCollection) {
      this.Code = productCollection.Code;
      this.Name = productCollection.Name;
      this.Description = productCollection.Description;
      this._Status = productCollection.Status;
      this._CreatedById = productCollection.CreatedById;
      this._CreatedAt = productCollection.CreatedAt;
      this._UpdatedById = productCollection.UpdatedById;
      this._UpdatedAt = productCollection.UpdatedAt;
    }
  }

  static async init(options?: IproductCollectionInitParam) {
    try {
      if (options) {
        const { code, collectionInfo } = options;
        if (code) {
          const productCollection =
            await ProductCollection._Repo.findByPk(code);
          if (!productCollection) {
            throw Error('Product Collection not found.');
          }
          return new ProductCollection(productCollection);
        } else if (!collectionInfo) {
          throw Error('Group Collection or code is required.');
        } else {
          return new ProductCollection(collectionInfo);
        }
      } else {
        return new ProductCollection();
      }
    } catch (error) {
      throw error;
    }
  }

  static async findAll(
    loginUser: LoginUser,
    dbTransaction?: any,
    page?: number,
    rows?: number,
    search = {},
  ): Promise<any> {
    try {
      // Privilege checking
      const systemCode =
        ApplicationConfig.getComponentConfigValue('system-code');

      const isPrivileged = await loginUser.checkPrivileges(
        systemCode,
        'ProductCollection - View',
      );
      if (!isPrivileged) {
        throw new Error(
          `Forbidden Error, does not have right privilege - "ProductCollection - View"`,
        );
      }

      // Retrieve listing
      let result: any;
      if (page && rows) {
        const offset = rows * (page - 1);
        const options: any = {
          offset,
          limit: rows,
          order: [['CreatedAt', 'DESC']],
          transaction: dbTransaction,
        };

        const whereObj = {};

        Object.entries(search).forEach(([key, value]) => {
          if (key === 'Status') {
            whereObj[key] = value;
          } else {
            whereObj[key] = {
              [Op.substring]: value,
            };
          }
        });

        if (!whereObj['Status']) {
          whereObj['Status'] = {
            [Op.ne]: StatusEnum.DEACTIVATED,
          };
        }

        if (whereObj) {
          options.where = whereObj;
        }

        result = await ProductCollection._Repo.findAndCountAll(options);
      } else {
        result = await ProductCollection._Repo.findAndCountAll();
      }

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

  public async create(loginUser: LoginUser, dbTransaction?: any): Promise<any> {
    try {
      // Privilege checking
      const systemCode =
        ApplicationConfig.getComponentConfigValue('system-code');

      const isPrivileged = await loginUser.checkPrivileges(
        systemCode,
        'ProductCollection - Create',
      );
      if (!isPrivileged) {
        throw new Error(
          `Forbidden Error, does not have right privilege - "ProductCollection - Create"`,
        );
      }

      // Fields Validation
      this.CreatedById = loginUser.ObjectId;
      this.CreatedAt = new Date();
      this.UpdatedById = loginUser.ObjectId;
      this.UpdatedAt = new Date();

      if (!this.Code) {
        throw Error('Code is required.');
      }

      if (!this.Name) {
        throw Error('Name is required.');
      }

      if (!this.Description) {
        throw Error('Description is required.');
      }

      if (!this.Status) {
        this.Status = StatusEnum.ACTIVE;
      }

      // Inserting Data
      const collectionProduct = await ProductCollection._Repo.create({
        Code: this.Code,
        Name: this.Name,
        Description: this.Description,
        Status: this.Status,
        CreatedById: this.CreatedById,
        CreatedAt: this.CreatedAt,
        UpdatedById: this.UpdatedById,
        UpdatedAt: this.UpdatedAt,
      });

      // Record Activity History
      const entityValueAfter = {
        Code: this.Code,
        Name: this.Name,
        Description: this.Description,
        Status: this.Status,
        CreatedById: this.CreatedById,
        CreatedAt: this.CreatedAt,
        UpdatedById: this.UpdatedById,
        UpdatedAt: this.UpdatedAt,
      };

      const activity = new Activity();
      activity.ActivityId = cuid();
      activity.Action = ActionEnum.CREATE;
      activity.Description = 'Create Product Collection';
      activity.EntityType = 'ProductCollection';
      activity.EntityId = this.Code;
      activity.EntityValueBefore = JSON.stringify({});
      activity.EntityValueAfter = JSON.stringify(entityValueAfter);
      await activity.create(loginUser.ObjectId, dbTransaction);

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

  public async update(
    loginUser: LoginUser,
    collectionInfo: IProductCollectionArr,
    dbTransaction?: any,
  ): Promise<any> {
    try {
      // Privilege checking
      const systemCode =
        ApplicationConfig.getComponentConfigValue('system-code');

      const isPrivileged = await loginUser.checkPrivileges(
        systemCode,
        'ProductCollection - Update',
      );
      if (!isPrivileged) {
        throw new Error(
          `Forbidden Error, does not have right privilege - "ProductCollection - Update"`,
        );
      }

      // Preparing Data
      const EntityValueBefore = {
        Code: this.Code,
        Name: this.Name,
        Description: this.Description,
        Status: this.Status,
        CreatedById: this.CreatedById,
        CreatedAt: this.CreatedAt,
        UpdatedById: this.UpdatedById,
        UpdatedAt: this.UpdatedAt,
      };

      this.Code = collectionInfo.Code;
      this.Name = collectionInfo.Name;
      this.Description = collectionInfo.Description;
      this.Status = collectionInfo.Status;
      this.CreatedById = collectionInfo.CreatedById;
      this.CreatedAt = collectionInfo.CreatedAt;

      this.UpdatedById = loginUser.ObjectId;
      this.UpdatedAt = new Date();

      // Updating Data
      const collection = await ProductCollection._Repo.findOne({
        where: { Code: this.Code },
        transaction: dbTransaction,
      });

      if (!collection) {
        throw new Error('Product Collection not found.');
      }

      collection.Code = this.Code;
      collection.Name = this.Name;
      collection.Description = this.Description;
      collection.Status = this.Status;
      collection.CreatedById = this.CreatedById;
      collection.CreatedAt = this.CreatedAt;
      collection.UpdatedById = this.UpdatedById;
      collection.UpdatedAt = this.UpdatedAt;

      const updatedInfo = await collection.save({
        transaction: dbTransaction,
      });

      const entityValueAfter = collectionInfo;

      const activity = new Activity();
      activity.ActivityId = cuid();
      activity.Action = ActionEnum.UPDATE;
      activity.Description = 'Update Product Collection';
      activity.EntityType = 'ProductCollection';
      activity.EntityId = this.Code;
      activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
      activity.EntityValueAfter = JSON.stringify(entityValueAfter);
      await activity.create(loginUser.ObjectId, dbTransaction);
      return updatedInfo;
    } catch (error) {
      throw error;
    }
  }

  public async delete(loginUser: LoginUser, dbTransaction?: any): Promise<any> {
    try {
      // Privilege checking
      const systemCode =
        ApplicationConfig.getComponentConfigValue('system-code');

      const isPrivileged = await loginUser.checkPrivileges(
        systemCode,
        'ProductCollection - Delete',
      );

      if (!isPrivileged) {
        throw new Error(
          `Forbidden Error, does not have right privilege - "ProductCollection - Delete"`,
        );
      }

      // Deleting Data
      const EntityValueBefore = {
        Code: this.Code,
        Name: this.Name,
        Description: this.Description,
        Status: this.Status,
        CreatedById: this.CreatedById,
        CreatedAt: this.CreatedAt,
        UpdatedById: this.UpdatedById,
        UpdatedAt: this.UpdatedAt,
      };

      const checkProduct =
        await ProductProductCollection.CheckProductAssignToCollection(
          this.Code,
          dbTransaction,
        );

      if (checkProduct) {
        this._Status = StatusEnum.DEACTIVATED;
        this.update(loginUser, {
          Code: this.Code,
          Name: this.Name,
          Description: this.Description,
          Status: this.Status,
          CreatedById: this.CreatedById,
          CreatedAt: this.CreatedAt,
          UpdatedById: this.UpdatedById,
          UpdatedAt: this.UpdatedAt,
        });
        return;
      }

      // Record Activity History
      const collection = await ProductCollection._Repo.findOne({
        where: { Code: this.Code },
        transaction: dbTransaction,
      });

      if (!collection) {
        throw new Error('Product Collection not found.');
      }

      await ProductCollection._Repo.delete(this.Code, dbTransaction);

      const entityValueAfter = {};

      const activity = new Activity();
      activity.ActivityId = cuid();
      activity.Action = ActionEnum.DELETE;
      activity.Description = 'Delete Product Collection';
      activity.EntityType = 'ProductCollection';
      activity.EntityId = this.Code;
      activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
      activity.EntityValueAfter = JSON.stringify(entityValueAfter);
      await activity.create(loginUser.ObjectId, dbTransaction);
      return;
    } catch (error) {
      throw error;
    }
  }
}
