/*
 * Copyright (c) Huawei Device Co., Ltd. 2024-2025. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { LogDomain, LogHelper } from '@ohos/basicutils';
import { ResUtils } from '@ohos/windowscene';
import { GlobalStatusCache } from '../base/GlobalStatusCache';
import { AbstractObserverManager } from '../base/AbstractObserverManager';
import { AuthType, AuthSubType } from '../utils/AccountHelper';
import { SlServerHelper } from '../utils/SlServerHelper';
import { VerifyDataManager } from '../manager/VerifyDataManager';
import { SystemSwitchUtils } from '../utils/SystemSwitchUtils';
import CommonConstants from '../constants/CommonConstants';
import { FaceUnlockConstants } from '../constants/FaceUnlockConstants';
import { FingerprintConstants } from '../constants/FingerprintConstants';
import accountsModelApi from '../api/AccountsModelApi';
import { ScreenLockApi } from '../api/ScreenLockApi';

const TAG = 'StrongAuthManager';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.KG, TAG);

export class StrongAuthConstants {
  public static readonly NO_NEED_STRONG_AUTH: number = 0x00000000;
  public static readonly USER_REBOOT: number = 0x00000001;
  public static readonly USER_TIMEOUT: number = 0x00000002;
  public static readonly USER_REQUEST: number = 0x00000004;

  public static readonly MAIN_USER_ID: number = 100;
  public static readonly STRONG_AUTH_CHANGED: string = 'strongAuthChanged';
  public static readonly STRONG_AUTH_MULTI_USER: string = 'multiUser';
  public static readonly STRONG_AUTH_SINGLE_USER: string = 'singleUser';
  public static readonly STRONG_AUTH_REASON: string = 'strongAuthReason';
}

/**
 * 强认证事件改变监听
 */
export interface StrongAuthListener {

  /**
   * 强认证事件改变时回调
   */
  onStrongAuthChange?: (strongAuthStatus: number) => void;
}

export class StrongAuthManager extends AbstractObserverManager<StrongAuthListener> {
  private static instance: StrongAuthManager;
  public static getInstance(): StrongAuthManager {
    if (!StrongAuthManager.instance) {
      StrongAuthManager.instance = new StrongAuthManager();
    }
    return StrongAuthManager.instance;
  }

  private strongAuthStatus: number = AppStorage.get(StrongAuthConstants.STRONG_AUTH_REASON) ??
  StrongAuthConstants.NO_NEED_STRONG_AUTH;
  private user: number | undefined = undefined;
  private strongAuthTipsPsd: Map<string, Resource> = this.strongAuthTipsInitPsd();
  private strongAuthTipsPin: Map<string, Resource> = this.strongAuthTipsInitPin();
  private fingerprintEnable: boolean = false;
  private needRequestUnlock: boolean = true;
  private authTypePinTables: AuthSubType[] = [AuthSubType.PIN_SIX, AuthSubType.PIN_NUMBER, AuthSubType.PIN_FOUR];

  private constructor() {
    super();
  }

  public setNeedRequestUnlock(needRequestUnlock: boolean): void {
    this.needRequestUnlock = needRequestUnlock;
  }

  public setAuthReason(reason: number): void {
    if (reason !== StrongAuthConstants.NO_NEED_STRONG_AUTH && !this.hasSetPasswd()) {
      log.showInfo('Strong Auth: password does not exist');
      return;
    }
    log.showInfo(`setAuthReason: ${reason}`);
    if (this.strongAuthStatus !== reason) {
      this.strongAuthStatus = reason;
      this.broadcastDataChange((observer) => observer.onStrongAuthChange?.(reason));
      AppStorage.setOrCreate(StrongAuthConstants.STRONG_AUTH_REASON, reason);
    }
  }

  public resetReason(): void {
    this.setAuthReason(StrongAuthConstants.NO_NEED_STRONG_AUTH);
  }

  private isMainUser(userId: number): boolean {
    return StrongAuthConstants.MAIN_USER_ID === userId;
  }

  public hasSetPasswd(): boolean {
    if (SystemSwitchUtils.isUseNewScreenLock()) {
      return !VerifyDataManager.getInstance().hasNoPwd;
    }
    const hasPwd: boolean =
      GlobalStatusCache.getInstance().getStatus(CommonConstants.SETTING_HAS_PASSWORD_KEY, false) as boolean;
    log.showInfo(`hasPwd: ${hasPwd}`);
    return hasPwd;
  }

  public hasFingerprint(): boolean {
    return !!AppStorage.get(FingerprintConstants.IS_CURRENT_USER_HAS_FINGERPRINT) && this.fingerprintEnable;
  }

  public setFingerprintEnable(fingerprintEnable: boolean): void {
    this.fingerprintEnable = fingerprintEnable;
    log.showInfo(`Strong Auth: fingerprintEnable ${this.fingerprintEnable}`);
  }

  private hasMultiAuthType(): boolean {
    if (SystemSwitchUtils.isUseNewScreenLock()) {
      if (VerifyDataManager.getInstance().hasNoPwd) {
        return false;
      }
      return VerifyDataManager.getInstance().getVerifyInfo(AuthType.FINGERPRINT).isSupportUnlock ||
        VerifyDataManager.getInstance().getVerifyInfo(AuthType.FACE).isSupportUnlock;
    }

    if (!this.hasSetPasswd()) {
      return false;
    }

    if (this.hasFingerprint()) {
      return true;
    }
    return !!AppStorage.get(FaceUnlockConstants.IS_FACE_UNLOCK_SWITCH_OPEN);
  }

  public getStrongAuthStatus(): number {
    return this.strongAuthStatus;
  }

  public getUserStrongAuth(userId: number): number {
    return SlServerHelper.getInstance().getStrongAuth(userId)
  }

  public triggerAuthByReboot(): void {
    if (this.user === undefined) {
      this.user = accountsModelApi.getCurrentUserId();
    }

    log.showInfo(`auth user userId: ${this.user}`);
    if (this.hasSetPasswd()) {
      const strongAuth = SlServerHelper.getInstance().getStrongAuth(this.user);

      if (strongAuth !== StrongAuthConstants.USER_REBOOT &&
        strongAuth !== StrongAuthConstants.USER_TIMEOUT) {
        return;
      }

      log.showInfo(`auth user reboot: ${this.user}`);
      let authType = StrongAuthConstants.USER_REBOOT;
      if (this.user !== undefined && !this.isMainUser(this.user) && this.needRequestUnlock) {
        if (strongAuth === StrongAuthConstants.USER_TIMEOUT) {
          authType = StrongAuthConstants.USER_TIMEOUT;
        } else {
          authType = StrongAuthConstants.USER_REQUEST;
        }
      } else if (strongAuth === StrongAuthConstants.USER_TIMEOUT) {
        this.setAuthReason(StrongAuthConstants.USER_TIMEOUT);
        return;
      }

      this.setAuthReason(authType);
      this.strongAuthRequestUnlock();
    }
  }

  public strongAuthRequestUnlock(): void {
    if (this.needRequestUnlock) {
      ScreenLockApi.requestUnlock({ success: (): void => { log.showInfo(`requestUnlock done: ${this.user}`); }});
    }
  }

  public isNeedStrongAuth(): boolean {
    return this.strongAuthStatus !== StrongAuthConstants.NO_NEED_STRONG_AUTH;
  }

  public isRebootStrongAuth(): boolean {
    return this.strongAuthStatus === StrongAuthConstants.USER_REBOOT;
  }

  private strongAuthTipsInitPsd(): Map<string, Resource> {
    const pleaseEnterPsdForSecurity = $r('app.string.please_enter_psd_for_security');
    const regularVerifyPsdForSecurity = $r('app.string.regular_verify_psd_for_security');
    const enterPsd = $r('app.string.psd_enter_psd_mix');
    const deviceRestartEnterPsd = $r('app.string.restart_enter_psd');
    return new Map<string, Resource>([
      [StrongAuthConstants.USER_REQUEST + StrongAuthConstants.STRONG_AUTH_MULTI_USER, pleaseEnterPsdForSecurity],
      [StrongAuthConstants.USER_REQUEST + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, enterPsd],
      [StrongAuthConstants.USER_TIMEOUT + StrongAuthConstants.STRONG_AUTH_MULTI_USER, regularVerifyPsdForSecurity],
      [StrongAuthConstants.USER_TIMEOUT + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, enterPsd],
      [StrongAuthConstants.USER_REBOOT + StrongAuthConstants.STRONG_AUTH_MULTI_USER, deviceRestartEnterPsd],
      [StrongAuthConstants.USER_REBOOT + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, deviceRestartEnterPsd]
    ]);
  }

  private strongAuthTipsInitPin(): Map<string, Resource> {
    const pleaseEnterPinForSecurity = $r('app.string.please_enter_pin_for_security');
    const regularVerifyPinForSecurity = $r('app.string.regular_verify_pin_for_security');
    const enterPin = $r('app.string.psd_enter_psd');
    const deviceRestartEntePin = $r('app.string.restart_enter_pin');
    return new Map<string, Resource>([
      [StrongAuthConstants.USER_REQUEST + StrongAuthConstants.STRONG_AUTH_MULTI_USER, pleaseEnterPinForSecurity],
      [StrongAuthConstants.USER_REQUEST + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, enterPin],
      [StrongAuthConstants.USER_TIMEOUT + StrongAuthConstants.STRONG_AUTH_MULTI_USER, regularVerifyPinForSecurity],
      [StrongAuthConstants.USER_TIMEOUT + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, enterPin],
      [StrongAuthConstants.USER_REBOOT + StrongAuthConstants.STRONG_AUTH_MULTI_USER, deviceRestartEntePin],
      [StrongAuthConstants.USER_REBOOT + StrongAuthConstants.STRONG_AUTH_SINGLE_USER, deviceRestartEntePin]
    ]);
  }

  public getStrongAuthPwdTip(): string {
    let key = `${this.strongAuthStatus}`;

    if (this.hasMultiAuthType()) {
      key += StrongAuthConstants.STRONG_AUTH_MULTI_USER;
    } else {
      key += StrongAuthConstants.STRONG_AUTH_SINGLE_USER;
    }

    let keyType = VerifyDataManager.getInstance().getVerifyInfo(AuthType.PIN)?.authSubType;
    if (keyType && this.authTypePinTables.includes(keyType) && this.strongAuthTipsPin.has(key)) {
      return ResUtils.getInnerString(this.strongAuthTipsPin.get(key));
    } else if (this.strongAuthTipsPsd.has(key)) {
      return ResUtils.getInnerString(this.strongAuthTipsPsd.get(key));
    }
    return '';
  }

  public getStrongAuthLockTip(): string {
    let keyType = VerifyDataManager.getInstance().getVerifyInfo(AuthType.PIN)?.authSubType;
    if (keyType && this.authTypePinTables.includes(keyType)) {
      return ResUtils.getInnerString($r('app.string.restart_enter_pin'));
    } else {
      return ResUtils.getInnerString($r('app.string.restart_enter_psd'));
    }
  }
}

export const strongAuthManager: StrongAuthManager = StrongAuthManager.getInstance();