import {
  HttpStatus,
  Inject,
  Injectable,
  Logger,
  UnauthorizedException,
} from '@nestjs/common';
import { validatePassword } from 'src/core/helpers/password/bcrypt.helpers';
import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager';
import { SessionService } from 'src/core/sessions';
import { STATUS } from 'src/core/strings/constants/base.constants';
import { EventTopics } from 'src/core/strings/constants/interface.constants';
import { UserModel } from 'src/modules/user-related/user/data/models/user.model';
import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity';
import { Not } from 'typeorm';
import { UserRole } from 'src/modules/user-related/user/constants';
import { AppSource, LogUserType } from 'src/core/helpers/constant';
import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event';
import { UserLoginEntity } from 'src/modules/user-related/user/domain/entities/user-login.entity';

@Injectable()
export class LoginManager extends BaseCustomManager<UserEntity> {
  @Inject()
  protected session: SessionService;
  protected token;
  protected userLogin;

  async validateProcess(): Promise<void> {
    return;
  }

  async beforeProcess(): Promise<void> {
    return;
  }

  async process(): Promise<void> {
    // get user active by username
    Logger.debug('getOneByOptions', 'LoginManager');
    this.userLogin = await this.dataService.getOneByOptions({
      where: {
        username: this.data.username,
        status: STATUS.ACTIVE,
        role: Not(UserRole.QUEUE_ADMIN),
      },
      relations: [
        'user_privilege',
        'user_privilege.user_privilege_configurations',
      ],
    });
    if (!this.userLogin) this.throwError();

    // validasi password
    const valid = await validatePassword(
      this.data.password,
      this.userLogin?.password,
    );
    if (!valid) this.throwError();

    // * Disini untuk isi token
    const tokenData = {
      id: this.userLogin.id,
      name: this.userLogin.name,
      username: this.userLogin.username,
      role: this.userLogin.role,
      user_privilege_id: this.userLogin.user_privilege_id,
      source: AppSource.POS_ADMIN,
    };

    Logger.debug('Sign Token', 'LoginManager');
    this.token = this.session.createAccessToken(tokenData);

    Logger.debug('Save Login Token', 'LoginManager');
    const userLoginData: UserLoginEntity = {
      user_id: this.userLogin.id,
      login_token: this.token,
      login_date: new Date().getTime(),
      source: AppSource.POS_ADMIN,
      role: this.userLogin.role,
      item_id: null,
      item_name: null,
    };

    // Update refresh token
    await this.dataService.saveUserLogin(userLoginData);

    await this.publishEvents();
    Logger.debug('Process Login Done', 'LoginManager');
    return;
  }

  async afterProcess(): Promise<void> {
    return;
  }

  getResult() {
    return {
      id: this.userLogin.id,
      name: this.userLogin.name,
      username: this.userLogin.username,
      role: this.userLogin.role,
      user_privilege_id: this.userLogin.user_privilege_id,
      token: this.token,
      user_privilege:
        this.userLogin.user_privilege?.user_privilege_configurations
          ?.filter((item) => item.module != 'POS')
          ?.map((item) => {
            return {
              id: item.id,
              menu: item.menu,
              menu_label: item.menu_label,
              view: item.view,
              create: item.create,
              edit: item.edit,
              delete: item.delete,
              cancel: item.cancel,
              confirm: item.confirm,
            };
          }),
    };
  }

  get entityTarget(): any {
    return UserModel;
  }

  get eventTopics(): EventTopics[] {
    return [
      {
        topic: LogUserLoginEvent,
        data: {
          type: LogUserType.login,
          role: this.userLogin.role,
          user_id: this.userLogin.id,
          username: this.userLogin.username,
          created_at: new Date().getTime(),
          source: AppSource.POS_ADMIN,
        },
      },
    ];
  }

  // !throw errornya akan sama, untuk security
  throwError() {
    throw new UnauthorizedException({
      statusCode: HttpStatus.UNAUTHORIZED,
      message: `Gagal! username atau password tidak sesuai`,
      error: 'Unauthorized',
    });
  }
}
