import { ClassError, ObjectBase } from '@tomei/general';
import { ComponentConfig } from '@tomei/config';
import { IUserPasswordHistoryAttr } from '../../interfaces/user-password-history.interface';
import { UserPasswordHistoryRepository } from './user-password-history.repository';
import { PasswordHashService } from '../../components/password-hash';

export class UserPasswordHistory
  extends ObjectBase
  implements IUserPasswordHistoryAttr
{
  ObjectId: string;
  ObjectName: string;
  ObjectType = 'UserPasswordHistory';
  TableName = 'sso_UserPasswordHistory';
  UserId: number;
  PasswordHash: string;
  private _CreatedAt: Date;

  private static _Repo = new UserPasswordHistoryRepository();

  get HistoryId(): string {
    return this.ObjectId;
  }

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

  get CreatedAt(): Date {
    return this._CreatedAt;
  }

  private constructor(params?: IUserPasswordHistoryAttr) {
    super();
    if (params) {
      this.ObjectId = params.HistoryId;
      this.UserId = params.UserId;
      this.PasswordHash = params.PasswordHash;
      this._CreatedAt = params.CreatedAt;
    }
  }

  public static async init(
    historyId?: number,
    dbTransaction?: any,
  ): Promise<UserPasswordHistory> {
    try {
      if (historyId) {
        const data = await UserPasswordHistory._Repo.findByPk(
          historyId.toString(),
          dbTransaction,
        );
        if (!data) {
          throw new ClassError(
            'UserPasswordHistory',
            'UserPasswordHistoryErrMsg01',
            'UserPasswordHistory not found',
            'init',
            400,
          );
        }

        return new UserPasswordHistory(data.get({ plain: true }));
      }
      return new UserPasswordHistory();
    } catch (error) {
      throw error;
    }
  }

  public static async validate(
    dbTransaction: any,
    UserId: number,
    Password: string,
    passwordHashService: PasswordHashService,
  ): Promise<void> {
    // This method used to check if password entered is valid by checking previous password history
    try {
      // Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
      // - ComponentName: "@tomei/sso"
      // - ConfigKey: "passwordHistory"
      // If no password history set, use default value 3

      const passwordHistoryPolicy =
        ComponentConfig.getComponentConfigValue(
          '@tomei/sso',
          'passwordHistory',
        ) || 3;

      // Part 3-4: Retrieve records from the table by using  class._repo findAll() by passing:
      // where: { UserId: params.UserId }
      // order: [['CreatedAt', 'DESC']]
      // limit: passwordHistory count above.
      // If no record found, return null.

      let passwordHistory = await UserPasswordHistory._Repo.findAll({
        where: { UserId: UserId },
        order: [['CreatedAt', 'DESC']],
        limit: passwordHistoryPolicy,
        transaction: dbTransaction,
      });

      if (passwordHistory?.length < 1) {
        return null;
      } else {
        // Part 5: If record found, map each record to compare params.Password and record.PasswordHash using the params.passwordHashService. If match, stop the mapping, and return ClassError:
        // ClassName: "UserPasswordHistory"
        // MethodName: "validate"
        // MessageCode: "UserPasswordHistory01"
        // Message: You cannot reuse your last ${passwordHistory} passwords. Please choose a new and unique password.
        for (let index = 0; index < passwordHistory.length; index++) {
          const isPasswordSame = await passwordHashService.verify(
            Password,
            passwordHistory[index].PasswordHash,
          );

          if (isPasswordSame) {
            throw new ClassError(
              'UserPasswordHistory',
              'UserPasswordHistory01',
              `You cannot reuse your last ${passwordHistoryPolicy} passwords. Please choose a new and unique password.`,
              'validate',
              403,
            );
          }
        }
      }
    } catch (error) {
      throw error;
    }
  }

  public static async create(
    dbTransaction: any,
    UserId: number,
    PasswordHash: string,
  ): Promise<void> {
    // This method used to check if password entered is valid by checking previous password history
    try {
      // Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
      // - ComponentName: "@tomei/sso"
      // - ConfigKey: "passwordHistory"
      // If no password history set, use default value 3

      const passwordHistoryPolicy =
        ComponentConfig.getComponentConfigValue(
          '@tomei/sso',
          'passwordHistory',
        ) || 3;

      // Part 3: Insert new password history by calling class _repo create() method.
      const userPasswordHistory = new UserPasswordHistory();
      let passwordHistory = await UserPasswordHistory._Repo.create(
        {
          HistoryId: userPasswordHistory.createId(),
          UserId: UserId,
          PasswordHash: PasswordHash,
        },
        {
          transaction: dbTransaction,
        },
      );

      // Part 3: When inserted successfully, retrieve all the password history for the user to check
      // how many previous password records. If records more than the passwordHistory count from
      // config. Remove the oldest record.
      if (passwordHistory) {
        let passwordHistoryList = await UserPasswordHistory._Repo.findAll({
          where: { UserId: UserId },
          order: [['CreatedAt', 'DESC']],
          transaction: dbTransaction,
        });

        if (passwordHistoryList.length > passwordHistoryPolicy) {
          let deleteList = passwordHistoryList.slice(passwordHistoryPolicy);
          let historyIdList = deleteList.map((record) => record.HistoryId);
          await UserPasswordHistory._Repo.destroyMultiple(
            historyIdList,
            dbTransaction,
          );
        }
      }
    } catch (error) {
      throw error;
    }
  }
}
