import { HttpClient } from 'aurelia-fetch-client';
import { Router } from 'aurelia-router';
import { autoinject, Container } from "aurelia-framework";
import { FluentRuleCustomizer, ValidationRules } from 'aurelia-validation';
import * as toastr from 'toastr';
import { User, Various } from "./../../../model/common-model";
import { EntityTypeNames, RolesMode } from './../../../constants/constants';
import { CustomLogger } from './../../../modules/custom-logger';
import { ServiceBase } from './../../../data/service-base';
import { IMenuItems } from './../../../ui/menu/ui-menus';
import { EditingModeEnum } from './../../../vm/editing-mode-enum';
import { PasswordResetModal } from '../password-reset/password-reset-modal';
import { UserRole } from './../../../model/common-model';
import { EntityDetailViewModelBase } from './../../../vm/detail-view-model-base';
import { UpdateUsernameFloatingBox } from './update-username-floating-box';
import { FilterQueryOp, Predicate } from 'breeze-client';

@autoinject
export abstract class UserDetailBase<T extends User> extends EntityDetailViewModelBase<T> {
  public ressourceName: string = EntityTypeNames.User;

  protected httpClient: HttpClient;

  protected menuItems: Array<IMenuItems>;

  constructor(protected rolesMode: RolesMode, ressourceName: string = EntityTypeNames.User) {
    super(Container.instance.get(Router), Container.instance.get(CustomLogger));
    super.initialize(new ServiceBase<T>(ressourceName));

    this.httpClient = Container.instance.get(HttpClient);

    this.ressourceName = ressourceName;

    ValidationRules.customRule('validation-email', (val, obj) => {
      if (val?.length > 0) {
        return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(val.trim());
      }
      return true;
    }, 'common:user.errorInvalidEmail');

    this.menuItems = [
      {
        label: this.i18n.tr('common:user.resetPassword'),
        handler: () => this.resetPassword()
      }
    ];
  }

  protected getTranslationKeyEntityTypeName(): string {
    return ('common:' + EntityTypeNames.User + '.' + EntityTypeNames.User).toLowerCase();
  }

  protected getResetPasswordUrl(): string {
    return `${this.config.globalConfig.remoteServiceName}/api/auth/ResetPassword`;
  }

  protected activateExpands(): string[] {
    return [];
  }

  protected activateQueryParameters(): object {
    return {};
  }

  protected getValidationRules(): FluentRuleCustomizer<unknown, any> {
    let rules = ValidationRules
      .ensure('username')
      .satisfiesRule('validation-email')
      .when(() => this.authService.usernameType == 'email');

    if (this.rolesMode == 'single') {
      rules.ensure('roles')
        .satisfies((roles: UserRole[]) => roles?.length == 1)
        .withMessage('common:user.roleIsRequired');
    } else if (this.rolesMode == 'multiple') {
      rules.ensure('roles')
        .satisfies((roles: UserRole[]) => roles?.length > 0)
        .withMessage('common:user.oneRoleIsRequired');
    }

    return rules;
  }

  public async activate(params) {
    let id = params.param1;
    if (id == Various.NewId) {
      this.editingMode = EditingModeEnum.Create;
      this.entity = await this.service.createEntity();
      this.entity.disabled = false;
    }
    else {
      this.editingMode = EditingModeEnum.Update;
      // User "firstEntity" in order to allow implementing service to add query params
      this.entity = await this.service.firstEntity(new Predicate('id', FilterQueryOp.Equals, id), ['roles.role', ...this.activateExpands()], this.activateQueryParameters());
    }

    this.getValidationRules().on(this.entity);
    this.controller.addObject(this.entity);
  }

  public get documentTitle(): string {
    if (this.editingMode === EditingModeEnum.Create) {
      return this.i18n.tr("user.addUser");
    } else {
      return this.entity?.username;
    }
  }

  protected async resetPassword() {
    if (this.authService.usernameType == 'email') {
      await this.httpClient.post(this.getResetPasswordUrl(), JSON.stringify({ Username: this.entity.username }));
      toastr.success(this.i18n.tr("user.resetPasswordSuccessEmail"));
    } else {
      let promise = new Promise<void>((resolve, reject) => {
        this.box.showCustomDialog(PasswordResetModal, null, this.isCreationMode ? this.i18n.tr("user.initPassword") : this.i18n.tr("user.resetPassword"), { lock: true })
          .whenClosed(async result => {
            if (false == result.wasCancelled) {
              await this.httpClient.post(this.getResetPasswordUrl(), JSON.stringify({ Username: this.entity.username, Password: result.output }));
              resolve();
              toastr.success(this.i18n.tr(this.isCreationMode ? "user.initPasswordSuccess" : "user.resetPasswordSuccess"));
            } else {
              reject();
            }
          });
      })
      return promise;
    }
  }

  protected async updateUsername() {
    await this.box.showFloatingBox(new UpdateUsernameFloatingBox(this.entity));
  }

  public async save(silentSave: boolean = false, byPassLocking?: boolean, navigateBack: boolean = false) {
    await super.save(silentSave, byPassLocking, false);

    // Reset password on local auth user creation
    if (this.isCreationMode && this.authService.usernameType == 'username') {
      await this.resetPassword();
    }

    if (navigateBack) {
      this.doNavigateBack();
    }
  }
}