import { ClassError, ObjectBase } from '@tomei/general';
import {
  ManualPriceStatus,
  NonSourceFeedName,
  SourceFeedName,
} from '../../enum/feed.enum';
import { IPriceFeedManualPriceHistoryAttr } from '../../interfaces/price-feed-manual-price-history-attr.interface';
import { FeedManualPriceHistoryRepository } from './feed-manual-price-history.repository';
import { Op } from 'sequelize';

export class FeedManualPriceHistory
  extends ObjectBase
  implements IPriceFeedManualPriceHistoryAttr
{
  ObjectId: string;
  ObjectName: string;
  ObjectType = FeedManualPriceHistory.name;
  TableName = 'price_FeedManualPriceHistory';
  FeedName: SourceFeedName | NonSourceFeedName;
  BuyPrice: number;
  SellPrice: number;
  Status: ManualPriceStatus;
  ActivationDateTime: Date;
  ActivatedById: string;
  DeactivationDateTime: Date;
  DeactivatedById: string;

  private static _Repo = new FeedManualPriceHistoryRepository();

  get FeedManualPriceHistoryId(): string {
    return this.ObjectId;
  }

  set FeedManualPriceHistoryId(value: string) {
    this.ObjectId = value;
  }

  private constructor(
    feedManualPriceHistory?: IPriceFeedManualPriceHistoryAttr,
  ) {
    super();
    if (feedManualPriceHistory) {
      this.ObjectId = feedManualPriceHistory.FeedManualPriceHistoryId;
      this.FeedName = feedManualPriceHistory.FeedName;
      this.Status = feedManualPriceHistory.Status;
      this.ActivationDateTime = feedManualPriceHistory.ActivationDateTime;
      this.ActivatedById = feedManualPriceHistory.ActivatedById;
      this.DeactivationDateTime = feedManualPriceHistory.DeactivationDateTime;
      this.DeactivatedById = feedManualPriceHistory.DeactivatedById;
      this.BuyPrice = feedManualPriceHistory.BuyPrice;
      this.SellPrice = feedManualPriceHistory.SellPrice;
    }
  }

  static async init(
    feedManualPriceHistoryId?: string,
    dbTransaction?: any,
  ): Promise<FeedManualPriceHistory> {
    try {
      if (feedManualPriceHistoryId) {
        const data = await this._Repo.findByPk(feedManualPriceHistoryId, {
          transaction: dbTransaction,
        });

        if (data) {
          return new FeedManualPriceHistory(data);
        } else {
          throw new ClassError(
            'FeedManualPriceHistory',
            'FeedManualPriceHistoryErrMsg01',
            'FeedManualPriceHistory not found',
            'init',
          );
        }
      }
      return new FeedManualPriceHistory();
    } catch (error) {
      throw error;
    }
  }

  async create(
    feedName: SourceFeedName | NonSourceFeedName,
    activatedById: string,
    buyPrice: number,
    sellPrice: number,
    dbTransaction?: any,
  ): Promise<FeedManualPriceHistory> {
    try {
      //Make sure all required fields are set
      if (!feedName || !activatedById || !buyPrice || !sellPrice) {
        throw new ClassError(
          'FeedManualPriceHistory',
          'FeedManualPriceErrMsg02',
          'Missing required fields',
          'create',
        );
      }

      //Set the ID, status, and activation date time
      this.ObjectId = this.createId();
      this.FeedName = feedName;
      this.BuyPrice = buyPrice;
      this.SellPrice = sellPrice;
      this.ActivatedById = activatedById;
      this.ActivationDateTime = new Date();
      this.Status = ManualPriceStatus.ACTIVE;

      //Save the record to the database
      await FeedManualPriceHistory._Repo.create(
        {
          FeedManualPriceHistoryId: this.ObjectId,
          FeedName: this.FeedName,
          BuyPrice: this.BuyPrice,
          SellPrice: this.SellPrice,
          Status: this.Status,
          ActivationDateTime: this.ActivationDateTime,
          ActivatedById: this.ActivatedById,
          DeactivationDateTime: this.DeactivationDateTime,
          DeactivatedById: this.DeactivatedById,
        },
        {
          transaction: dbTransaction,
        },
      );

      //Return the object
      return this;
    } catch (error) {
      throw error;
    }
  }

  public static async deactivate(
    feedName: string,
    deactivatedById: string,
    dbTransaction?: any,
  ): Promise<FeedManualPriceHistory> {
    try {
      //Retrieve the latest active record
      const data = await FeedManualPriceHistory._Repo.findAll({
        order: [['ActivationDateTime', 'DESC']],
        transaction: dbTransaction,
        where: {
          FeedName: feedName,
          Status: ManualPriceStatus.ACTIVE,
        },
      });

      //If no record found, throw an error
      if (!data) {
        throw new ClassError(
          'FeedManualPriceHistory',
          'FeedManualPriceErrMsg03',
          'FeedManualPriceHistory not found',
          'deactivate',
        );
      }

      for (const item of data) {
        //Set the deactivation date time, status, and deactivated by id
        item.DeactivationDateTime = new Date();
        item.Status = ManualPriceStatus.DEACTIVATED;
        item.DeactivatedById = deactivatedById;

        await item.save({
          transaction: dbTransaction,
        });
      }

      //Return the object
      return new FeedManualPriceHistory(data[0]);
    } catch (error) {
      throw error;
    }
  }

  public static async findAll(
    page: number,
    row: number,
    search?: {
      ActivationDateTime?: {
        startDate: Date;
        endDate: Date;
      };
      DeactivationDateTime?: {
        startDate: Date;
        endDate: Date;
      };
      FeedName?: string;
      Status?: ManualPriceStatus;
    },
    dbTransaction?: any,
  ): Promise<{
    rows: IPriceFeedManualPriceHistoryAttr[];
    count: number;
  }> {
    try {
      const queryObj: any = {};

      const options: any = {
        transaction: dbTransaction,
        limit: row,
        offset: row * (page - 1),
        order: [['ActivationDateTime', 'DESC']],
      };

      if (search) {
        if (search.ActivationDateTime) {
          queryObj.ActivationDateTime = {
            [Op.between]: [
              search.ActivationDateTime.startDate,
              search.ActivationDateTime.endDate,
            ],
          };
        }

        if (search.DeactivationDateTime) {
          queryObj.DeactivationDateTime = {
            [Op.between]: [
              search.DeactivationDateTime.startDate,
              search.DeactivationDateTime.endDate,
            ],
          };
        }

        if (search.FeedName) {
          queryObj.FeedName = {
            [Op.like]: `%${search.FeedName}%`,
          };
        }

        if (search.Status) {
          queryObj.Status = search.Status;
        }
      }

      return await FeedManualPriceHistory._Repo.findAllWithPagination({
        where: queryObj,
        ...options,
      });
    } catch (error) {
      throw error;
    }
  }
}
