import {
  AnalyticalScope,
  Behavior,
  NotNull,
  ObjectRef,
  SaveButton,
  TextField,
  Visible
} from "@webbpm/base-package";
import {HttpClient, HttpErrorResponse, HttpResponse} from "@angular/common/http";
import {FormField} from "../field/FormField";
import {AuthorizationService} from "../../../modules/app/service/authorization.service";
import {ApplicationKind} from "../enum/ApplicationKind";
import {ProcessKey} from "../enum/ProcessKey";
import {ProcessResponse} from "./response/ProcessResponse";
import {UserApplicationListRpcService} from "../../../generated/ru/micord/ervu/account_applications/component/rpc/UserApplicationListRpcService";
import {Account} from "./dto/Account";
import {Person} from "./dto/Person";
import {Roles} from "./dto/Roles";
import {CreateAccountData} from "./dto/create/CreateAccountData";
import {Role} from "./dto/Role";
import {EditPersonData} from "./dto/edit/EditPersonData";
import {EditAccountData} from "./dto/edit/EditAccountData";
import {EditRolesAccount} from "./dto/edit/EditRolesAccount";
import {EditRolesData} from "./dto/edit/EditRolesData";
import {ResetPasswordAccount} from "./dto/password/ResetPasswordAccount";
import {ResetPasswordData} from "./dto/password/ResetPasswordData";
import {ProcessRequest} from "./dto/ProcessRequest";
import {StatusUpdateService} from "../../../modules/app/service/status-update.service";
import {BlockedRegion} from "../../../generated/ru/micord/ervu/account_applications/enums/BlockedRegion";

@AnalyticalScope(SaveButton)
export class UserManagementService extends Behavior {

  private static PROCESS_START_PATH = '/service/wf/service/start';
  private static ACCOUNT_BLOCK_PATH = "/service/idm/accounts/blocked/v1";
  private static PERSON_BLOCK_PATH = "/service/idm/persons/blocked/v1";

  @NotNull()
  @ObjectRef()
  public applicationKind: TextField;

  private button: SaveButton;
  private httpClient: HttpClient;
  private onClickFunction: Function;
  private sendToErvu: boolean = false;
  private rpc: UserApplicationListRpcService;
  private authService: AuthorizationService;
  private statusUpdateService: StatusUpdateService;

  initialize() {
    super.initialize();
    this.button = this.getScript(SaveButton);
    this.httpClient = this.injector.get(HttpClient);
    this.rpc = this.getScript(UserApplicationListRpcService);
    this.authService = this.injector.get(AuthorizationService);
    this.statusUpdateService = this.injector.get(StatusUpdateService);
    this.onClickFunction = () => {
      if (!this.authService.hasRole('security_administrator') || !this.sendToErvu) {
        return;
      }
      const kind = this.applicationKind.getValue();
      const formJson = this.collectData();
      const appNumber = formJson['appNumber'];
      const accountId = formJson['accountId'];
      const personId = formJson['idPerson'];
      let request;

      switch (kind) {
        case ApplicationKind.CREATE_USER:
          let createData = new CreateAccountData();
          createData.account = this.populateDto(new Account(), formJson);
          createData.person = this.populateDto(new Person(), formJson);
          let roles = new Roles();
          roles.add = formJson['rolesList'].map(id => {
            let role = new Role();
            role.id = id;
            return role;
          });
          createData.roles = roles;
          request = new ProcessRequest<CreateAccountData>();
          request.data = createData;
          request.processKey = ProcessKey.CREATE;
          this.doRequest(request, appNumber);
          break;
        case ApplicationKind.EDIT_USER_MAIN:
          let editPersonData = new EditPersonData();
          editPersonData.accountId = accountId;
          editPersonData.personId = personId;
          editPersonData.organizationId = formJson['idDomain'];
          editPersonData.personData = this.populateDto(new Person(), formJson);
          request = new ProcessRequest<EditPersonData>();
          request.data = editPersonData;
          request.processKey = ProcessKey.EDIT_PERSON;
          this.doRequest(request, appNumber);
          break;
        case ApplicationKind.EDIT_USER_ACCOUNT:
          let editAccountData = new EditAccountData();
          let account = this.populateDto(new Account(), formJson);
          editAccountData.account = account;
          editAccountData.accountId = accountId;
          request = new ProcessRequest<EditAccountData>();
          request.data = editAccountData;
          request.processKey = ProcessKey.EDIT_ACCOUNT;
          this.doRequest(request, appNumber);
          break;
        case ApplicationKind.EDIT_USER_ROLES:
          let editRolesAccount = new EditRolesAccount();
          let rolesList = formJson['rolesList'];
          editRolesAccount.accountId = accountId;
          let roles2 = new Roles();
          roles2.add = rolesList.map(id => {
            let role = new Role();
            role.id = id;
            return role;
          });
          this.rpc.getRemovedRoleIds(accountId, rolesList)
              .then(list => {
                roles2.remove = list;
                editRolesAccount.roles = roles2;
                let editRolesData = new EditRolesData();
                editRolesData.data = editRolesAccount;
                request = new ProcessRequest<EditRolesData>();
                request.data = editRolesData;
                request.processKey = ProcessKey.EDIT_ROLES;
                this.doRequest(request, appNumber);
              });
          break;
        case ApplicationKind.BLOCK_USER:
          this.doBlockingRequest(accountId, personId, appNumber, true);
          break;
        case ApplicationKind.UNBLOCK_USER:
          this.doBlockingRequest(accountId, personId, appNumber, false,
                                 formJson['blockedRegion']);
          break;
        case ApplicationKind.RESET_PASSWORD:
          let resetPasswordAccount = new ResetPasswordAccount();
          resetPasswordAccount.id = accountId;
          let resetPasswordData = new ResetPasswordData();
          resetPasswordData.account = resetPasswordAccount;
          request = new ProcessRequest<ResetPasswordData>();
          request.data = resetPasswordData;
          request.processKey = ProcessKey.RESET_PASSWORD;
          this.doRequest(request, appNumber);
          break;
        default: break;
      }
    };
  }

  @Visible()
  public allowSendToErvu(): void {
    this.sendToErvu = true;
  }

  private collectData(): any {
    let jsonObj = {};
    let fields: FormField[] = this.button.form.getScriptsInChildren(FormField);

    for (let field of fields) {
      jsonObj[`${field.name}`] = field.getValue();
    }
    return jsonObj;
  }

  private populateDto(dto: any, jsonObj: any): any {
    Object.keys(dto).forEach(key => {
      if (jsonObj[key]) {
        dto[key] = jsonObj[key]
      }
    });
    return dto;
  }

  private doRequest(request: any, appNumber: number): void {
    const url = window.location.origin + UserManagementService.PROCESS_START_PATH;
    request.userId = this.authService.getUserId();
    this.httpClient.post(url, request).toPromise()
        .then((response: ProcessResponse) => {
          let code = response.code;

          if (code === '200') {
            this.rpc.saveTraceId(response.traceId, appNumber);
          }
          else {
            this.saveError(appNumber, response.msg);
          }
        })
        .catch(reason => {
          console.error("Error while executing request:", reason.toString());
          this.saveError(appNumber, reason.toString());
        });
  }

  private doBlockingRequest(accountId: string, personId: string, appNumber: number, block: boolean,
                            blockedRegion?: string): void {
    let ids;
    let path;

    if (!block && blockedRegion != BlockedRegion.ACCOUNT) {
      ids = [personId];
      path = UserManagementService.PERSON_BLOCK_PATH;
    }
    else {
      ids = [accountId];
      path = UserManagementService.ACCOUNT_BLOCK_PATH;
    }
    const url = window.location.origin + path;
    let method = block ? 'delete' : 'post';
    this.httpClient.request(method, url, { body: ids, observe: 'response' }).toPromise()
        .then((response: any) => {
          if (response.ok) {
            if (blockedRegion == BlockedRegion.BOTH) {
              this.doBlockingRequest(accountId, personId, appNumber, block, BlockedRegion.ACCOUNT);
            }
            else {
              this.rpc.saveAcceptedStatus(appNumber)
                  .then(() => this.statusUpdateService.publishStatus(appNumber, true))
                  .catch(err => console.log('failed to update application status', err));
            }
          }
          else {
            let reason = response.body ? 'Reason: ' + response.body.toString() : '';
            let errorMsg = `Failed to execute blocking request. Http status: ${response.status}. ${reason}`;
            this.saveError(appNumber, errorMsg);
          }
        })
        .catch(reason => {
          console.error("Error while executing request:", reason.toString());
          this.saveError(appNumber, reason.toString());
        });
  }

  private saveError(appNumber: number, errorMsg: string): void {
    this.rpc.saveError(appNumber, errorMsg)
        .then(() => this.statusUpdateService.publishStatus(appNumber, false))
        .catch(err => console.log('failed to update application status', err));
  }

  bindEvents() {
    super.bindEvents();
    this.button.addClickListener(this.onClickFunction);
  }

  unbindEvents() {
    super.unbindEvents();
    this.button.removeClickListener(this.onClickFunction);
  }
}
