/*
 * 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, Logger } from '@ohos/basicutils';
import { ResUtils } from '@ohos/windowscene';
import { AbstractObserverManager } from '../base/AbstractObserverManager';
import { AccountHelper, AuthType, AuthSubType } from '../utils/AccountHelper';
import { ScreenLockUnlockService } from '../services/ScreenLockUnLockService';
import { SlServerHelper } from '../utils/SlServerHelper';
import { VerifyDataManager } from '../manager/VerifyDataManager';
import screenLock from '@ohos.screenLock';

const TAG = 'StrongAuthStateManager';
const log: Logger = Logger.getLogHelper(LogDomain.KG);

export enum StrongAuthReasonFlags {
  NO_NEED_STRONG_AUTH = 0x00000000,
  USER_REBOOT = 0x00000001,
  USER_TIMEOUT = 0x00000002,
  USER_REQUEST = 0x00000004,
}

const MAIN_USER_ID: number = 100;
const STRONG_AUTH_MULTI_USER: string = 'strongAuthChanged';
const STRONG_AUTH_SINGLE_USER: string = 'singleUser';

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

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

export class StrongAuthStateManager extends AbstractObserverManager<StrongAuthStateListener> {
  private static instance: StrongAuthStateManager;

  public static getInstance(): StrongAuthStateManager {
    if (!StrongAuthStateManager.instance) {
      StrongAuthStateManager.instance = new StrongAuthStateManager();
    }
    return StrongAuthStateManager.instance;
  }

  private strongAuthStatus: StrongAuthReasonFlags = StrongAuthReasonFlags.NO_NEED_STRONG_AUTH;
  private user: number | undefined = undefined;
  private promptAuthStatus: StrongAuthReasonFlags = StrongAuthReasonFlags.NO_NEED_STRONG_AUTH;

  private strongAuthTipsPsd: Map<string, Resource> = this.strongAuthTipsInitPsd();
  private strongAuthTipsPin: Map<string, Resource> = this.strongAuthTipsInitPin();
  private authTypePinTables: AuthSubType[] = [AuthSubType.PIN_SIX, AuthSubType.PIN_NUMBER, AuthSubType.PIN_FOUR];

  private constructor() {
    super();
  }

  /**
   * 从锁屏管理服务更新强认证状态
   */
  public updateStrongAuth(): void {
    if (this.user === undefined) {
      this.user = AccountHelper.getInstance().currentLocalId;
    }
    const strongAuthState: number = SlServerHelper.getInstance().getStrongAuth(this.user);
    log.showWarn(TAG, `updateStrongAuth strongAuthState: ${strongAuthState}, user: ${this.user}`);
    this.setStrongAuth(strongAuthState);
  }

  /**
   * 获取强认证状态
   *
   * @return StrongAuthReasonFlags: 强认证状态
   */
  public getStrongAuthStatus(): StrongAuthReasonFlags {
    return this.strongAuthStatus;
  }

  /**
   * 获取提示语状态
   *
   * @return StrongAuthReasonFlags: 提示语状态
   */
  public getPromptAuthStatus(): StrongAuthReasonFlags {
    return this.promptAuthStatus;
  }

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

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

  /**
   * 重启触发强认证
   */
  public triggerAuthByReboot(): void {
    if (VerifyDataManager.getInstance().hasNoPwd) {
      return;
    }
    this.updateStrongAuth();
    if (this.isNeedStrongAuth()) {
      log.showWarn(TAG, `triggerAuthByReboot strongAuthStatus: ${this.strongAuthStatus}`);
      this.strongAuthRequestUnlock();
    }
  }

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

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

    return this.getTipFromPromptMap(key);
  }

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

  /**
   * c++侧强认证状态修改
   *
   * @param reasonFlag: 强认证标识
   * @param userId: 需要强认证的用户ID,默认为当前用户
   */
  public setServiceStrongAuth(reasonFlag: StrongAuthReasonFlags, userId?: number): void {
    if (VerifyDataManager.getInstance().hasNoPwd) {
      return;
    }
    try {
      screenLock.requestStrongAuth(reasonFlag, userId ?? AccountHelper.getInstance().currentLocalId);
    } catch (err) {
      log.showError(TAG, `setServiceStrongAuth failed, err: ${err.code}`);
    }
  }

  private strongAuthRequestUnlock(): void {
    ScreenLockUnlockService.getInstance().requestUnlock({
      success: (): void => {
        log.showInfo(TAG, `requestUnlock done: ${this.user}`);
      }
    });
  }

  /**
   * 应用层强认证状态修改,该方法不会改变底层强认证状态
   *
   * @param strongAuthState: 强认证状态
   */
  private setStrongAuth(strongAuthState: number): void {
    if (!Object.values(StrongAuthReasonFlags).includes(strongAuthState)) {
      log.showError(TAG, `setStrongAuth wrong state:${strongAuthState}`);
      return;
    }
    this.setStrongAuthStatus(strongAuthState);
  }

  /**
   * 本地强认证状态修改
   */
  private setStrongAuthStatus(strongAuthStatus: StrongAuthReasonFlags): void {
    log.showInfo(TAG, `old strongAuthStatus: ${this.strongAuthStatus}, new strongAuthStatus: ${strongAuthStatus}`);
    if (this.strongAuthStatus !== strongAuthStatus) {
      this.strongAuthStatus = strongAuthStatus;
      this.updatePromptAuthStatus();
      this.broadcastDataChange((observer) => observer.onStrongAuthStateChange?.(strongAuthStatus));
    }
  }

  /**
   * 更新提示语
   */
  private updatePromptAuthStatus(): void {
    if (!Object.values(StrongAuthReasonFlags).includes(this.strongAuthStatus)) {
      this.promptAuthStatus = StrongAuthReasonFlags.USER_REQUEST;
      return;
    }

    if (this.user !== MAIN_USER_ID && this.strongAuthStatus !== StrongAuthReasonFlags.USER_TIMEOUT) {
      // 隐私空间重启时,打印USER_REQUEST提示语
      this.promptAuthStatus = StrongAuthReasonFlags.USER_REQUEST;
      return;
    }

    this.promptAuthStatus = this.strongAuthStatus;
  }

  private hasMultiAuthType(): boolean {
    if (VerifyDataManager.getInstance().hasNoPwd) {
      return false;
    }

    return VerifyDataManager.getInstance().getVerifyInfo(AuthType.FINGERPRINT).isSupportUnlock ||
    VerifyDataManager.getInstance().getVerifyInfo(AuthType.FACE).isSupportUnlock;
  }

  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>([
      [StrongAuthReasonFlags.USER_REQUEST + STRONG_AUTH_MULTI_USER, pleaseEnterPsdForSecurity],
      [StrongAuthReasonFlags.USER_REQUEST + STRONG_AUTH_SINGLE_USER, enterPsd],
      [StrongAuthReasonFlags.USER_TIMEOUT + STRONG_AUTH_MULTI_USER, regularVerifyPsdForSecurity],
      [StrongAuthReasonFlags.USER_TIMEOUT + STRONG_AUTH_SINGLE_USER, enterPsd],
      [StrongAuthReasonFlags.USER_REBOOT + STRONG_AUTH_MULTI_USER, deviceRestartEnterPsd],
      [StrongAuthReasonFlags.USER_REBOOT + 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>([
      [StrongAuthReasonFlags.USER_REQUEST + STRONG_AUTH_MULTI_USER, pleaseEnterPinForSecurity],
      [StrongAuthReasonFlags.USER_REQUEST + STRONG_AUTH_SINGLE_USER, enterPin],
      [StrongAuthReasonFlags.USER_TIMEOUT + STRONG_AUTH_MULTI_USER, regularVerifyPinForSecurity],
      [StrongAuthReasonFlags.USER_TIMEOUT + STRONG_AUTH_SINGLE_USER, enterPin],
      [StrongAuthReasonFlags.USER_REBOOT + STRONG_AUTH_MULTI_USER, deviceRestartEntePin],
      [StrongAuthReasonFlags.USER_REBOOT + STRONG_AUTH_SINGLE_USER, deviceRestartEntePin]
    ]);
  }

  private getTipFromPromptMap(key: string): string {
    let keyType = VerifyDataManager.getInstance().getVerifyInfo(AuthType.PIN).authSubType;
    if (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 '';
  }
}