//  Copyright (c) 2024 Huawei Technologies Co., Ltd.
//  openUBMC is licensed under Mulan PSL v2.
//  You can use this software according to the terms and conditions of the Mulan PSL v2.
//  You may obtain a copy of Mulan PSL v2 at:
//        #  http://license.coscl.org.cn/MulanPSL2
//  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
//  EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
//  MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//  See the Mulan PSL v2 for more details.
import $http from '@/utils/http-service';
import $sensitive from '@/utils/sensitive';
import {
  IUserInfo,
  ILoginRule,
  IError,
  ISaveUser,
  IResponseUserData,
  ICustError,
  IUsersInfo,
  IAddUser,
  IBackError,
  IUserId,
} from '../pages/User/models/type';
import { USER_ROLES, UnDeleteUsers, PasswordFailedProp } from '../pages/User/models/model';
import UserInfoService from '@/services/user-info.service';
import { traduction } from '@/utils/language';

const idList = [
  '2',
  '3',
  '4',
  '5',
  '6',
  '7',
  '8',
  '9',
  '10',
  '11',
  '12',
  '13',
  '14',
  '15',
  '16',
  '17',
];
import { UI_REST_FIRMWAREINVENTORY } from '@/api/api';
import { importRsaPublicKey, rsaEncrypt, arrayBufferToHexStr } from '@/utils/crypto';

export default class LocaluserService {
  public static userInfo: UserInfoService = new UserInfoService();
  public static setUserState(item: IResponseUserData, user: IUsersInfo, admins: any, resArr: any) {
    /**
     * 设置可删除状态和禁用启动状态
     * 1. 只有1个管理员，紧急用户无删除，其他场景由Oem.Huawei.Deleteable值决定
     * 2. 只有1个管理员，紧急用户无法被禁用，其他场景可以被禁用
     * 3. 禁用的鼠标提示默认不显示
     * 4. 当只有1个管理员时，禁用鼠标提示显示
     */
    if (admins.length <= 1 && item.RoleID === USER_ROLES.ADMINISTRATOR && item.Enabled) {
      user.canDeleted = false;
      user.canDisabled = false;
      user.canDisbledTipSHow = true;
      user.unDeletedReason = traduction('USER_UNDELETED_LAST');
      user.unDisabledReason = traduction('USER_UNDELETED_LAST');
    } else {
      // DelDisableReason可能为null, 也可能是具体提示原因
      const delDisableReason = item.DelDisableReason;
      if (delDisableReason) {
        if (delDisableReason === UnDeleteUsers.SSHLOGIN) {
          user.unDeletedReason = '';
        } else if (delDisableReason === UnDeleteUsers.TRAPV3USER && !item.Deleteable) {
          user.unDeletedReason = traduction('USER_UNDELETED_TRAPV3');
        } else if (delDisableReason === UnDeleteUsers.EMERGENCYUSER) {
          user.unDeletedReason = traduction('USER_UNDELETED_URGENT');
        } else {
          user.unDeletedReason = '';
        }
      }
      // 如果是紧急登录用户，则不能删除，不能禁用
      const emergencyUser = resArr[1].emergencyLoginUser;
      if (emergencyUser === item.UserName) {
        user.canDeleted = false;
        user.canDisabled = false;
        user.deleteableTip = traduction('USER_UNDELETED_LAST');
      } else {
        user.canDeleted = item.Deleteable;
      }
    }
  }

  public static initUserItem(item: IResponseUserData) {
    const rules: string[] = item.LoginRule.map((rule: string) => {
      return rule.split('/').reverse().shift()?.[0] || '';
    });

    return {
      id: item.ID,
      userName: item.UserName,
      roleId: item.RoleID,
      loginInterface: item.LoginInterface,
      loginRole: rules,
      validate: item.PasswordValidityDays,
      sshPublicKeyHash: item.SSHPublicKeyHash || '',
      state: item.Enabled,
      canDeleted: true,
      canDisabled: true,
      canDisbledTipSHow: false,
      unDeletedReason: '',
      unDisabledReason: '',
      isTrapV3: item?.DelDisableReason === 'TrapV3User' && item?.Deleteable,
    };
  }

  // 获取用户列表
  public static getUserList(): Promise<IUsersInfo[]> {
    return new Promise((resolve, reject) => {
      Promise.all([this.getUsedIdList(), this.getPwdCheck()])
        .then((resArr: any[]) => {
          const members = resArr[0];
          const resultArr: IUsersInfo[] = [];
          const admins = members.filter((item: IResponseUserData) => {
            return item.RoleID === USER_ROLES.ADMINISTRATOR && item.Enabled;
          });

          members.forEach((item: IResponseUserData) => {
            const user: IUsersInfo = this.initUserItem(item);
            // 设置单个用户的编辑等状态
            this.setUserState(item, user, admins, resArr);
            resultArr.push(user);
          });
          resolve(resultArr);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // 查询用户列表
  public static getUsedIdList(): Promise<IResponseUserData[]> {
    return new Promise((resolve, reject) => {
      $http
        .get('/UI/Rest/AccessMgnt/Accounts')
        .then((res: any) => {
          resolve(res.data.Members);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // 查询是否开启了用户密码校验和查询紧急用户
  public static getPwdCheck(): Promise<{
    pwdCheck: boolean;
    emergencyLoginUser: string;
    minPwdLen: number;
    firstLoginEnabled: boolean;
    localNamePattern: any;
    allowedLoginInterfaces: any;
  }> {
    return new Promise((resolve, reject) => {
      $http
        .get('/UI/Rest/AccessMgnt/AdvancedSecurity')
        .then((res: any) => {
          let escapedStr = '';
          if (res.data?.LocalAccountNamePattern) {
            escapedStr = res.data.LocalAccountNamePattern.replace(/[\n\r\t\b\v\f\\]/g, (match: string) => {
              return {
                '\n': '\\n',
                '\r': '\\r',
                '\t': '\\t',
                '\b': '\\b',
                '\v': '\\v',
                '\f': '\\f',
                '\\': '\\\\',
              }[match];
            });
          }
          resolve({
            pwdCheck: res.data.PasswordComplexityCheckEnabled,
            emergencyLoginUser: res.data.EmergencyLoginUser,
            minPwdLen: res.data.MinimumPasswordLength,
            firstLoginEnabled: res.data.InitialPasswordNeedModify,
            allowedLoginInterfaces: res.data.AllowedLoginInterfaces || {},
            localNamePattern: escapedStr || '',
          });
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // 获取规则项列表
  public static getRules(): Promise<ILoginRule[]> {
    return new Promise((resolve, reject) => {
      $http
        .get('/UI/Rest/AccessMgnt/LoginRule')
        .then((res: any) => {
          const ruleArr: ILoginRule[] = [];
          res.data.Members.forEach((item: { [key: string]: any }, index: number) => {
            const ruleItem: ILoginRule = {
              id: item.ID + 1,
              checked: false,
              ruleEnabled: item.Status,
              startTime: item.StartTime,
              endTime: item.EndTime,
              ip: item.IP,
              mac: item.Mac,
              active: index === 0 ? true : false,
            };
            ruleArr.push(ruleItem);
          });
          resolve(ruleArr);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // 查询特定用户相关信息
  public static getUserInfo(id: number): Promise<IUserInfo> {
    return new Promise((resolve, reject) => {
      this.getUsedIdList()
        .then((res: IResponseUserData[]) => {
          const userMsg = res.filter((item: IResponseUserData) => {
            return item.ID === id;
          })[0];
          const user: IUserInfo = {
            RoleId: userMsg?.RoleID,
            LoginInterface: userMsg?.LoginInterface,
            LoginRule: userMsg?.LoginRule,
            UserName: userMsg?.UserName,
            AuthProtocol: userMsg?.SnmpV3AuthProtocol || 'SHA256',
            EncryProtocol: userMsg?.SnmpV3PrivProtocol || 'AES',
            SNMPEncryptPwdInit: userMsg?.SNMPEncryptPwdInit,
            FirstLoginPolicy: userMsg?.FirstLoginPolicy,
          };
          resolve(user);
        })
        .catch(error => reject(error));
    });
  }

  // 获取添加用户时的相关信息
  public static getAddUserParams(): Promise<any> {
    return new Promise((resolve, reject) => {
      Promise.all([this.getUsedIdList(), this.getPwdCheck()])
        .then((resArr: any[]) => {
          const members = resArr[0] || [];
          const tempArr: any[] = [];
          let firstLoginResetSupport = false;
          // 去除已经存在的用户ID
          const idListCopy: string[] = [...idList];
          members.forEach((item: any) => {
            if (!firstLoginResetSupport && item.FirstLoginPolicy !== undefined) {
              firstLoginResetSupport = true;
            }
            const position: number = idListCopy.indexOf(String(item.ID));
            if (position > -1) {
              idListCopy.splice(position, 1);
            }
          });
          idListCopy.forEach(item => {
            const tempId: IUserId = {
              id: item,
              label: item,
            };
            tempArr.push(tempId);
          });

          // 是否开启密码检查
          const pwdCheck = resArr[1].pwdCheck;
          const firstLoginEnabled = resArr[1].firstLoginEnabled;
          const result = {
            pwdCheck,
            firstLoginEnabled,
            userIdList: tempArr,
            firstLoginResetSupport,
            minPwdLen: resArr[1].minPwdLen,
            localNamePattern: resArr[1].localNamePattern,
            allowedLoginInterfaces: resArr[1].allowedLoginInterfaces,
          };
          resolve(result);
        })
        .catch((error: any) => {
          reject(error);
        });
    });
  }

  // 获取编辑用户时相关信息
  public static getEditUserParams(userId: number): Promise<any> {
    return new Promise((resolve, reject) => {
      Promise.all([this.getPwdCheck(), this.getUserInfo(userId)])
        .then(resArr => {
          // 密码修改策略：0，1，2 （0是用户自定义修改的，在前端与1的功能一致，统一为提示修改。）
          if (resArr[1].FirstLoginPolicy === 0) {
            resArr[1].FirstLoginPolicy = 1;
          }

          // 是否开启密码检查
          const pwdCheck = resArr[0].pwdCheck;
          const firstLoginEnabled = resArr[0].firstLoginEnabled;

          // 获取用户信息,主要包括rule,role,interface,
          const userMsg = {
            roleId: resArr[1].RoleId,
            interfaceList: resArr[1].LoginInterface,
            loginRule: resArr[1].LoginRule,
            userName: resArr[1].UserName,
            authProtocol: resArr[1].AuthProtocol,
            encryProtocol: resArr[1].EncryProtocol,
            firstLoginPolicy: resArr[1].FirstLoginPolicy,
          };
          const snmpv3PwdInit = resArr[1].SNMPEncryptPwdInit;
          const result = {
            pwdCheck,
            firstLoginEnabled,
            userInfo: userMsg,
            snmpv3PwdInit,
            minPwdLen: resArr[0].minPwdLen,
            localNamePattern: resArr[0].localNamePattern,
            allowedLoginInterfaces: resArr[0].allowedLoginInterfaces,
          };
          resolve(result);
        })
        .catch((error: any) => {
          reject(error);
        });
    });
  }

  public static async addUserParamGen(
    key: string,
    param: IAddUser,
    password: string,
  ): Promise<any> {
    const pubKey = await importRsaPublicKey(key, 'SHA-1');
    const encUser = arrayBufferToHexStr(await rsaEncrypt(param.UserName, pubKey));
    const encPwd = arrayBufferToHexStr(await rsaEncrypt(param.Password, pubKey));
    const ret: Record<string, any> = {
      ID: param.Id,
      RoleID: param.RoleId,
      UserName: encUser,
      Password: encPwd,
      FirstLoginPolicy: param.FirstLoginPolicy,
    };
    if (password !== '') {
      let base64Password = window.btoa(password);
      ret.ReauthKey = arrayBufferToHexStr(await rsaEncrypt(base64Password, pubKey));
    }
    if ((param.LoginInterface || []).length > 0) {
      ret.LoginInterface = param.LoginInterface;
    }
    if ((param.LoginRule || '').length > 0) {
      ret.LoginRule = param.LoginRule;
    }
    return ret;
  }

  public static async addUserSensitive(param: IAddUser, password: string): Promise<any> {
    const encryptedPaths = ['UserName', 'Password', 'ReauthKey'];
    return await $sensitive(encryptedPaths, key =>
      LocaluserService.addUserParamGen(key, param, password),
    ).post('/UI/Rest/AccessMgnt/Accounts');
  }

  public static addUser(param: IAddUser, password: string): Promise<ICustError> {
    /**
     * 新增用户的逻辑如下
     * 1.对ID为参数值的对象赋值密码，角色，用户名
     * 2.对ID为参数值的用户设置规则和接口
     * 3.2步骤必须等到1步骤操作成功后才执行
     */
    return new Promise((resolve, reject) => {
      LocaluserService.addUserSensitive(param, password)
        .then(() => {
          resolve({
            type: 'success',
            errors: null,
          });
        })
        .catch(error => {
          const custError: IError[] = this.packingErrorMsg(error.data.error);
          reject({
            type: 'error',
            errors: custError,
          });
        });
    });
  }

  public static async editUserParamGen(
    key: string,
    param: ISaveUser,
    password: string,
  ): Promise<any> {
    const pubKey = await importRsaPublicKey(key, 'SHA-1');
    const encUser = param.UserName ? arrayBufferToHexStr(await rsaEncrypt(param.UserName, pubKey)) : undefined;
    const encPwd = param.Password ? arrayBufferToHexStr(await rsaEncrypt(param.Password, pubKey)) : undefined;
    const params: { [key: string]: any } = {
      Password: encPwd,
      RoleID: param.RoleId,
      UserName: encUser,
    };
    this.changeParams(param, params);
    const encSnmpV3PrivPasswd = param.SNMPV3Password ?
      arrayBufferToHexStr(await rsaEncrypt(param.SNMPV3Password, pubKey)) : undefined;
    params.SNMPV3PrivPasswd = encSnmpV3PrivPasswd;

    // 去除无效属性
    for (const k in params) {
      if (Object.prototype.hasOwnProperty.call(params, k)) {
        const value = params[k];
        if (value === undefined) {
          delete params[k];
        }
      }
    }

    if (password !== '') {
      params.ReauthKey = arrayBufferToHexStr(await rsaEncrypt(window.btoa(password), pubKey));
    }
    return params;
  }

  public static async editUserSensitive(param: ISaveUser, password: string): Promise<any> {
    const encryptedPaths = ['UserName', 'Password', 'ReauthKey', 'SNMPV3PrivPasswd'];
    return await $sensitive(encryptedPaths, key =>
      LocaluserService.editUserParamGen(key, param, password),
    ).patch(`/UI/Rest/AccessMgnt/Accounts/${param.Id}`);
  }

  // 编辑用户
  public static editUser(param: ISaveUser, password: string): Promise<ICustError> {
    return new Promise((resolve, reject) => {
      // 保存用户信息
      LocaluserService.editUserSensitive(param, password)
        .then(res => {
          let result: ICustError | null = null;
          if (res.data.error) {
            const custError: IError[] = this.packingErrorMsg(res.data.error);
            result = { type: 'someFailed', errors: custError };
          } else {
            result = { type: 'success', errors: null };
          }
          resolve(result);
        })
        .catch(error => {
          const custError: IError[] = this.packingErrorMsg(error.data.error);
          reject({ type: 'error', errors: custError });
        });
    });
  }

  public static changeParams(param: any, params: any) {
    if (param.LoginInterface) {
      params.LoginInterface = param.LoginInterface;
    }
    if (param.LoginRule) {
      params.LoginRule = param.LoginRule;
    }
    if (param.SNMPV3Password) {
      params.SNMPV3PrivPasswd = param.SNMPV3Password;
    }
    if (param.SnmpV3AuthProtocol) {
      params.SnmpV3AuthProtocol = param.SnmpV3AuthProtocol;
    }
    if (param.SnmpV3PrivProtocol) {
      params.SnmpV3PrivProtocol = param.SnmpV3PrivProtocol;
    }
    if (param.FirstLoginPolicy) {
      params.FirstLoginPolicy = param.FirstLoginPolicy;
    }
  }

  public static async deleteUserParamGen(key: string, password: string): Promise<any> {
    let params: any = {};
    if (password !== '') {
      const pubKey = await importRsaPublicKey(key, 'SHA-1');
      params.ReauthKey = arrayBufferToHexStr(await rsaEncrypt(window.btoa(password), pubKey));
    }
    return params;
  }

  public static async deleteUserSensitive(userId: string, password: string): Promise<any> {
    const encryptedPaths = ['ReauthKey'];
    return await $sensitive(encryptedPaths, key =>
      LocaluserService.deleteUserParamGen(key, password)).delete(`/UI/Rest/AccessMgnt/Accounts/${userId}`);
  }

  // 删除用户
  public static deleteUser(userId: string, password: string): Promise<ICustError> {
    return new Promise((resolve, reject) => {
      LocaluserService.deleteUserSensitive(userId, password)
        .then(() => {
          resolve({
            type: 'success',
            errors: null,
          });
        })
        .catch(error => {
          const custError: IError[] = this.packingErrorMsg(error.data.error);
          reject({ type: 'error', errors: custError });
        });
    });
  }

  public static async modifyUserParamGen(key: string, password: string, state: boolean): Promise<any> {
    const params: { [key: string]: any } = {
      Enabled: state,
    };
    if (password !== '') {
      const pubKey = await importRsaPublicKey(key, 'SHA-1');
      params.ReauthKey = arrayBufferToHexStr(await rsaEncrypt(window.btoa(password), pubKey));
    }
    return params;
  }

  public static async modifyUserSensitive(
    userId: string,
    password: string,
    state: boolean,
  ): Promise<any> {
    const encryptedPaths = ['ReauthKey'];
    return await $sensitive(encryptedPaths, key =>
      LocaluserService.modifyUserParamGen(key, password, state),
    ).patch(`/UI/Rest/AccessMgnt/Accounts/${userId}`);
  }

  // 禁用用户
  public static modifyUserState(
    userId: string,
    password: string,
    state: boolean,
  ): Promise<ICustError> {
    return new Promise((resolve, reject) => {
      LocaluserService.modifyUserSensitive(userId, password, state)
        .then(() => {
          resolve({
            type: 'success',
            errors: null,
          });
        })
        .catch(error => {
          const custError: IError[] = this.packingErrorMsg(error.data.error);
          reject({ type: 'error', errors: custError });
        });
    });
  }

  // 上传SSH公钥
  public static uploadCerFile(formData: FormData) {
    return $http.post(UI_REST_FIRMWAREINVENTORY, formData);
  }
  
  // 导入SSH公钥
  public static uploadFile(param: {
    userId: number;
    secPwd: string;
    Content?: string;
    type: string;
  }): Promise<ICustError> {
    return new Promise((resolve, reject) => {
      const url = `/UI/Rest/AccessMgnt/Accounts/${param.userId}/ImportSSHPublicKey`;
      $sensitive(['ReauthKey'], async (key) => {
        const pubKey = await importRsaPublicKey(key, 'SHA-1');
        const params = {
          ReauthKey: arrayBufferToHexStr(await rsaEncrypt(param.secPwd, pubKey)),
          Content: param.type === 'Text' ? param.Content : `/tmp/web/${param.Content}`,
          Type: param.type,
        };
        return params;
      }).post(url)
        .then(() => {
          resolve({
            type: 'success',
            errors: null,
          });
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // 删除SSH公钥
  public static deleteSSHKey(userId: string, password: string): Promise<ICustError> {
    return new Promise((resolve, reject) => {
      const url = `/UI/Rest/AccessMgnt/Accounts/${userId}/DeleteSSHPublicKey`;
      $sensitive(['ReauthKey'], async (key) => {
        let params = {};
        if (password !== '') {
          const pubKey = await importRsaPublicKey(key, 'SHA-1');
          params = {
            ReauthKey: arrayBufferToHexStr(await rsaEncrypt(window.btoa(password), pubKey)),
          };
        }
        return params;
      }).delete(url)
        .then(() => {
          resolve({
            type: 'success',
            errors: null,
          });
        })
        .catch(error => {
          const custError: IError[] = this.packingErrorMsg(error.data.error);
          reject({ type: 'error', errors: custError });
        });
    });
  }

  private static packingErrorMsg(error: IBackError[]): IError[] {
    // 封装的错误对象结构为 [{errorId: 'id1', relatedProp: 'Password'}]
    const extendedInfo = error;
    const errorData: IError[] = [];
    extendedInfo.forEach(errorItem => {
      const errorId = errorItem.code;
      let relatedProp = '';
      if (errorItem?.relatedProperties?.[0]) {
        relatedProp = errorItem.relatedProperties[0].slice(2);
      }
      if (errorItem.message && errorItem.message.indexOf(PasswordFailedProp.PASSWORDPROP) > -1) {
        relatedProp = PasswordFailedProp.PASSWORD;
      }
      if (
        errorItem.message &&
        errorItem.message.indexOf(PasswordFailedProp.SNMPV3PASSWORDPROP) > -1
      ) {
        relatedProp = PasswordFailedProp.SNMPV3PASSWORD;
      }
      const temp: IError = {
        errorId,
        relatedProp,
      };
      errorData.push(temp);
    });
    return errorData;
  }
}
