/*
 * 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 observer from '@ohos.telephony.observer';
import sim from '@ohos.telephony.sim';
import { LogDomain, LogHelper } from '@ohos/basicutils';
import { EvtBus, SimPinVerifyEvent, sSettingsUtil } from '@ohos/frameworkwrapper';
import { SettingsKeyConstants } from '@ohos/commonconstants';
import { AbstractObserverManager, ObserverAble } from '../base/AbstractObserverManager';
import { SimCardUtils } from '../utils/SimCardUtils';
import { SystemParamUtils } from '../utils/SystemParamUtils';
import { CapsuleQueue } from '../base/CapsuleQueue';
import { getSimLabelSync, SimLabel, SimType } from '@ohos/windowsceneinterfaces/src/main/ets/interfaces/stub/StubSimGetLabel';

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

const DEFAULT_PIN_REMAIN_TIMES = 3;
const DEFAULT_PUK_REMAIN_TIMES = 10;
const BROADCAST_DELAY_MILLIS = 500;

/**
 * 定义锁定类型
 */
export enum LockType {
  UNLOCK = '',
  PIN_LOCK = 'pin',
  PUK_LOCK = 'puk',
}

/**
 * Sim卡信息,属性只支持读取
 */
export class SimCard {
  /**
   * [原始指标]slotID
   */
  private readonly _slotId: number = 0;

  /**
   * [原始指标]SIM卡状态
   */
  private _simState: sim.SimState = sim.SimState.SIM_STATE_UNKNOWN;

  /**
   * 锁定类型
   */
  private _lockType: LockType = LockType.UNLOCK;

  /**
   * pin验证剩余次数
   */
  private _pinRemainTimes: number = DEFAULT_PIN_REMAIN_TIMES;

  /**
   * puk验证剩余次数
   */
  private _pukRemainTimes: number = DEFAULT_PUK_REMAIN_TIMES;

  /**
   * 是否为ESIM卡(一种内置SIM卡,无法卸载)
   */
  private _isEsim: boolean = false;

  /**
   * Sim卡名称索引(1/2/N)
   */
  private _labelIndex: number = 0;

  /**
   * 能否跳过验证(当存在一张卡已验证时,另一张卡状态为可跳过)
   */
  private _isCanSkipVerify: boolean = false;

  /**
   * 是否已跳过验证
   */
  private _hasSkipVerify: boolean = false;

  constructor(slotId: number) {
    this._slotId = slotId;
  }

  public get slotId(): number {
    return this._slotId;
  }

  public get simState(): sim.SimState {
    return this._simState;
  }

  protected set simState(simState: sim.SimState) {
    this._simState = simState;
    if (!this.isPresent) {
      // SIM不在位时,属性恢复
      this._lockType = LockType.UNLOCK;
      this._pinRemainTimes = DEFAULT_PIN_REMAIN_TIMES;
      this._pukRemainTimes = DEFAULT_PUK_REMAIN_TIMES;
      this._isEsim = false;
      this._isCanSkipVerify = false;
      this._hasSkipVerify = false;
      this._labelIndex = 0;
    }
  }

  public get lockType(): LockType {
    return this._lockType;
  }

  protected set lockType(lockType: LockType) {
    this._lockType = lockType;
  }

  public get isEsim(): boolean {
    return this._isEsim;
  }

  protected set isEsim(isEsim: boolean) {
    this._isEsim = isEsim;
  }

  public get labelIndex(): number {
    return this._labelIndex;
  }

  protected set labelIndex(labelIndex: number) {
    this._labelIndex = labelIndex;
  }

  public get pinRemainTimes(): number {
    return this._pinRemainTimes;
  }

  protected set pinRemainTimes(pinRemainTimes: number) {
    this._pinRemainTimes = pinRemainTimes;
  }

  public get pukRemainTimes(): number {
    return this._pukRemainTimes;
  }

  protected set pukRemainTimes(pukRemainTimes: number) {
    this._pukRemainTimes = pukRemainTimes;
  }

  /**
   * 能否跳过验证
   *
   * @returns true能/false否
   */
  public get isCanSkipVerify(): boolean {
    log.showInfo(`soltId ${this._slotId}, isEsim: ${this._isEsim}, pukRemainTimes: ${this._pukRemainTimes}`);
    return (this.isEsim && this._pukRemainTimes <= 0) || this._isCanSkipVerify;
  }

  protected set isCanSkipVerify(isCanSkipVerify: boolean) {
    this._isCanSkipVerify = isCanSkipVerify;
  }

  public get hasSkipVerify(): boolean {
    return this._hasSkipVerify;
  }

  protected set hasSkipVerify(hasSkipVerify: boolean) {
    this._hasSkipVerify = hasSkipVerify;
  }

  /**
   * [派生属性]SIM卡是否在位
   */
  public get isPresent(): boolean {
    return this._simState !== sim.SimState.SIM_STATE_UNKNOWN && this._simState !== sim.SimState.SIM_STATE_NOT_PRESENT;
  }

  /**
   * [派生属性]判断是否锁定
   *
   * @returns 是否锁定
   */
  public get isLocked(): boolean {
    return this.isPresent && this._lockType !== LockType.UNLOCK;
  }
}

/**
 * 内部Sim卡信息,支持修改数据
 */
export class InnerSimCard extends SimCard {
  /**
   * SIM卡定时器id
   */
  private _timerId: number = 0;

  /**
   * SIM卡插拔变更
   */
  private _postionChange: boolean = false;

  public get simState(): sim.SimState {
    return super.simState;
  }

  public set simState(simState: sim.SimState) {
    super.simState = simState;
  }

  public get lockType(): LockType {
    return super.lockType;
  }

  public set lockType(lockType: LockType) {
    super.lockType = lockType;
  }

  public get isEsim(): boolean {
    return super.isEsim;
  }

  public set isEsim(isEsim: boolean) {
    super.isEsim = isEsim;
  }

  public get labelIndex(): number {
    return super.labelIndex;
  }

  public set labelIndex(labelIndex: number) {
    super.labelIndex = labelIndex;
  }

  public get pinRemainTimes(): number {
    return super.pinRemainTimes;
  }

  public set pinRemainTimes(pinRemainTimes: number) {
    super.pinRemainTimes = pinRemainTimes;
  }

  public get pukRemainTimes(): number {
    return super.pukRemainTimes;
  }

  public set pukRemainTimes(pukRemainTimes: number) {
    super.pukRemainTimes = pukRemainTimes;
  }

  public set isCanSkipVerify(isCanSkipVerify: boolean) {
    super.isCanSkipVerify = isCanSkipVerify;
  }

  public get hasSkipVerify(): boolean {
    return super.hasSkipVerify;
  }

  public set hasSkipVerify(hasSkipVerify: boolean) {
    super.hasSkipVerify = hasSkipVerify;
  }

  public get timerId(): number {
    return this._timerId;
  }

  public set timerId(id: number) {
    this._timerId = id;
  }

  public get isPositionChange(): boolean {
    return this._postionChange;
  }

  public set isPositionChange(isChange: boolean) {
    this._postionChange = isChange;
  }
}

/**
 * 锁定SIM卡数据
 */
export class SimCardData {
  protected readonly _simCards: Array<SimCard>;

  constructor(simCards: Array<SimCard>) {
    this._simCards = simCards;
  }

  public simCards(): Array<SimCard> {
    return this._simCards;
  }

  public get count(): number {
    return this._simCards.length;
  }

  /**
   * 是否存在需要验证的SIM卡
   *
   * @returns 是否
   */
  public get hasVerifySimCard(): boolean {
    return this._simCards.filter(simCard => simCard.isLocked && !simCard.hasSkipVerify).length > 0;
  }
}

/**
 * SIM卡状态变更通知
 */
export interface SimCardStateChangeListener extends ObserverAble {
  /**
   * Sim卡变更通知(插卡,拔卡)
   *
   * @param simCardData sim卡数据
   * @param isAddCard 插卡还是拔卡
   */
  onSimCardCountChange?: (simCardData: SimCardData, isAddCard: boolean) => void;

  /**
   * SIM卡状态变更通知(解锁、锁定类型、可尝试解锁次数变更)
   *
   * @param slotId slotID
   * @param simCard SIM卡信息
   */
  onSimCardStateChange?: (slotId: number, simCard: SimCard) => void;

  /**
   * SIM卡自动校验失败,进入锁屏态
   *
   * @param slotId slotId
   */
  onSimCardAutoVerifyFail?: (slotId: number) => void;

  /**
   * SIM卡锁定状态变化回调
   *
   * @param isSimLocked Sim卡是否锁定
   */
  onSimCardLockChanged?: (isSimLocked: boolean) => void;
}

/**
 * Sim卡状态管理器
 */
export class SimCardStateManager extends AbstractObserverManager<SimCardStateChangeListener> {
  private static sInstance: SimCardStateManager;

  public static getInstance(): SimCardStateManager {
    if (SimCardStateManager.sInstance == null) {
      SimCardStateManager.sInstance = new SimCardStateManager();
      log.showInfo('create SimCardStateManager instance');
    }
    return SimCardStateManager.sInstance;
  }

  private readonly _simCards: InnerSimCard[] = [];
  private readonly _registeredSimCardCallback: Callback<observer.SimStateData>[] = [];
  private readonly _registeredIccChangeCallback = (): void => this.handleIccChange();
  private _lockedQueue: CapsuleQueue<number> = new CapsuleQueue();
  private _isSimLocked: boolean = false;
  private _pinVerifyEvent = async (data: SimPinVerifyEvent): Promise<void> => {
    let isVerifySuccess: boolean | undefined = data?.parameters?.result;
    let num: number = data?.parameters?.slotId ?? 0;
    if (num < 0 || num >= this._simCards.length) {
      log.showError(`SimPinVerifyEvent slot id ${num} is invalid`);
      return;
    }
    log.showInfo(`sim card auto verified, slotId: ${num}, result: ${isVerifySuccess}`);
    let simCard: InnerSimCard = this._simCards[num];
    if (!isVerifySuccess) {
      // 记住Pin码自动校验不成功,需要拉起Pin码验证界面
      await this.syncSimCardLockInfo(simCard);
      this.updateQueue(num, simCard.lockType, false);
      this.broadcastDataChange(callback => callback?.onSimCardAutoVerifyFail?.(simCard.slotId));
    }
  };

  private constructor() {
    super();
  }

  /**
   * 生成SIM卡数据
   *
   * @returns SIM卡数据
   */
  public get simCardData(): SimCardData {
    let simCards: SimCard[] = [];
    let hasUnlocked: boolean = this._simCards.some(simCard => simCard.isPresent && !simCard.isLocked);
    this._simCards.forEach(simCard => {
      if (simCard.isPresent) {
        simCard.isCanSkipVerify = hasUnlocked;
        simCards.push(simCard);
      }
    });
    return new SimCardData(simCards);
  }

  public getLockQueue(): CapsuleQueue<number> {
    return this._lockedQueue;
  }

  protected isSupport(): boolean {
    return SimCardUtils.isSimCardSupport();
  }

  protected doInit(): void {
    log.showInfo('doInit');
    this.initSimCards();
    SimCardUtils.registerIccChangeListener(this._registeredIccChangeCallback);
    EvtBus.on(SimPinVerifyEvent, this._pinVerifyEvent);
  }

  protected doRelease(): void {
    log.showInfo('doRelease');
    this._registeredSimCardCallback.forEach(callback => SimCardUtils.unRegisterSimStateChangeListener(callback));
    this._registeredSimCardCallback.length = 0;
    SimCardUtils.unRegisterIccChangeListener(this._registeredIccChangeCallback);
    EvtBus.off(SimPinVerifyEvent, this._pinVerifyEvent);
  }

  private handleIccChange(): void {
    SimCardUtils.getActiveSimAccountInfoList((err, data: Array<sim.IccAccountInfo>) => {
      if (err) {
        log.showError(`getActiveSimAccountInfoList error. code ${err.code}`);
        return;
      }

      log.showInfo(`getActiveSimAccountInfoList success, total count=${data?.length}`);
      for (let simIccInfo of data) {
        log.showInfo(`sim slotId: ${simIccInfo.slotIndex}, isEsim: ${simIccInfo.isEsim}`);
        let simCard: InnerSimCard = this._simCards[simIccInfo.slotIndex];
        if (simCard) {
          simCard.isEsim = simIccInfo.isEsim;
          simCard.labelIndex = getSimLabelSync(simCard.slotId).index;
        }
      }
    });
  }

  private async initSimCards(): Promise<void> {
    log.showInfo('initSimCards');
    let maxCount: number = SimCardUtils.getMaxSimCount();
    for (let slotId = 0; slotId < maxCount; slotId++) {
      let simCard: InnerSimCard = new InnerSimCard(slotId);
      this._simCards.push(simCard);
      await this.updateSimCardState(simCard, await SimCardUtils.getSimState(slotId), true);
      this.registerSimStateChangeListener(slotId);
    }
  }

  private registerSimStateChangeListener(slotId: number): void {
    let callback: Callback<observer.SimStateData> = (data: observer.SimStateData) => {
      log.showInfo(`on simStateChange solt ${slotId}, state: ${data?.state}`);
      let innerSimCard: InnerSimCard = this._simCards[slotId];
      if (innerSimCard && data) {
        this.updateSimCardState(innerSimCard, data.state, true);
      }
    };

    if (SimCardUtils.registerSimStateChangeListener(slotId, callback)) {
      log.showInfo(`registerSimStateChangeListener for slotId ${slotId} success.`);
      this._registeredSimCardCallback.push(callback);
    }
  }

  private async updateSimCardState(simCard: InnerSimCard, simState: sim.SimState, isNotify: boolean): Promise<void> {
    // 分布式通信导致的虚拟卡状态变化,不更新SIM卡状态
    if (sSettingsUtil.getValue(SettingsKeyConstants.DISTRIBUTED_MODEM_STATE, '') === '1_sink') {
      log.showInfo(`distributed virtrual simCard change, do not updateSimCardState`);
      return;
    }
    if (simCard.timerId !== 0) {
      clearTimeout(simCard.timerId);
      simCard.timerId = 0;
    }

    if (simState === sim.SimState.SIM_STATE_UNKNOWN) {
      log.showWarn(`slot ${simCard.slotId} encounter unknown state, ignore.`);
      return;
    }

    log.showInfo(`slot ${simCard.slotId} update simState ${simState}.`);
    let lastIsPresent: boolean = simCard.isPresent;
    simCard.simState = simState;
    let newIsPresent: boolean = simCard.isPresent;
    if (newIsPresent !== lastIsPresent) {
      simCard.isPositionChange = true;
    }

    let isPinSavingEnabled: boolean = await this.isCurPinSavingSupported(simCard.slotId);
    if (newIsPresent) {
      log.showInfo(`slot ${simCard.slotId} begin sync info.`);
      this.syncSimAccountInfo(simCard);
      await this.syncSimCardLockInfo(simCard);
      this.updateQueue(simCard.slotId, simCard.lockType, isPinSavingEnabled);
    } else {
      this._simCards.forEach((simCard) => {
        this.updateQueue(simCard.slotId, simCard.lockType, isPinSavingEnabled);
        simCard.hasSkipVerify = false;
      });
    }

    if (!isNotify) {
      return;
    }
    this.sendBroadCast(simCard, isPinSavingEnabled, isPinSavingEnabled ? 0 : BROADCAST_DELAY_MILLIS);
  }

  private sendBroadCast(simCard: InnerSimCard, isPinSavingEnabled: boolean, delay: number): void {
    simCard.timerId = setTimeout(() => {
      simCard.timerId = 0;
      if (simCard.isPositionChange) {
        simCard.isPositionChange = false;
        let simCardData = this.simCardData;
        log.showInfo(`slot ${simCard.slotId} onSimCardCountChange. ${JSON.stringify(simCardData)}`);
        this.broadcastDataChange(callback => callback?.onSimCardCountChange?.(simCardData, simCard.isPresent));
      } else {
        log.showInfo(`slot ${simCard.slotId} onSimCardStateChange. ${JSON.stringify(simCard)}`);
        this.broadcastDataChange(callback => callback?.onSimCardStateChange?.(simCard.slotId, simCard));
      }
    }, delay);
  }

  private syncSimAccountInfo(simCard: InnerSimCard): void {
    // 更新数据
    SimCardUtils.getSimAccountInfo(simCard.slotId, (err, data) => {
      if (err) {
        log.showError(`getSimAccountInfo slot ${simCard.slotId} error. code ${err.code}`);
        return;
      }
      if (!data) {
        log.showError(`getSimAccountInfo slot ${simCard.slotId} encounter invalid data.`);
        return;
      }
      simCard.isEsim = data.isEsim;
      simCard.labelIndex = getSimLabelSync(simCard.slotId).index;
    });
  }

  private async syncSimCardLockInfo(simCard: InnerSimCard): Promise<void> {
    if (simCard.simState !== sim.SimState.SIM_STATE_LOCKED) {
      log.showInfo(`slotId ${simCard.slotId} is unlocked.`);
      simCard.lockType = LockType.UNLOCK;
      return;
    }
    let simLockType: sim.LockType = await SimCardUtils.getSimLockType(simCard.slotId);
    log.showInfo(`sim lock type: ${simLockType}.`);
    simCard.pinRemainTimes = this.getRemainTimes(simCard.slotId, LockType.PIN_LOCK, simLockType);
    simCard.pukRemainTimes = this.getRemainTimes(simCard.slotId, LockType.PUK_LOCK, simLockType);
    if (simCard.pinRemainTimes <= 0 || simCard.pukRemainTimes <= 0) {
      simCard.lockType = LockType.PUK_LOCK;
    } else {
      simCard.lockType = LockType.PIN_LOCK;
    }
  }

  private getRemainTimes(slotId: number, lockType: LockType, simLockType: sim.LockType): number {
    const simSlotId: number = slotId + 1;
    const propKey: string = `ril.gsm.slot${simSlotId}.num.${lockType}${simLockType}`;
    const defaultVal: string = LockType.PIN_LOCK === lockType ? '3' : '10';

    const availableStr: string = SystemParamUtils.getSystemParam(propKey, defaultVal);
    log.showInfo(`soltId=${slotId},propKey=${propKey},available=${availableStr}`);
    return Number.parseInt(availableStr);
  }

  private updateQueue(slotId: number, lockType: LockType, isPinSaved: boolean): void {
    log.showInfo(`updateQueue slotId: ${slotId}, lockType: ${lockType}, isPinSaved: ${isPinSaved}`);
    if ((lockType === LockType.PIN_LOCK && !isPinSaved) || lockType === LockType.PUK_LOCK) {
      if (!this._lockedQueue.hasItem(slotId)) {
        log.showInfo('updateQueue add queue');
        this._lockedQueue.enqueue(slotId);
      }
    } else {
      if (this._lockedQueue.hasItem(slotId)) {
        log.showInfo('updateQueue remove queue');
        this._lockedQueue.remove(slotId);
      }
    }
    this.onLockedQueueChanged();
  }

  private onLockedQueueChanged(): void {
    let isSimLocked = this.isSimLocked();
    if (this._isSimLocked !== isSimLocked) {
      this._isSimLocked = isSimLocked;
      this.broadcastDataChange(callback => callback?.onSimCardLockChanged?.(isSimLocked));
    }
  }

  private async isCurPinSavingSupported(slotId: number): Promise<boolean> {
    let isCurrentPinSavingEnabled: boolean = false;
    if (SimCardUtils.getPinSavingSupported()) {
      isCurrentPinSavingEnabled = await SimCardUtils.isPinSavingEnabled(slotId);
    }
    return isCurrentPinSavingEnabled;
  }

  public isPukLocked(): boolean {
    return this._lockedQueue.length() > 0 &&
      this._simCards[(this._lockedQueue.getFirst() ?? 0) as number].lockType === LockType.PUK_LOCK;
  }

  public isCurrentEsim(): boolean {
    return this._lockedQueue.length() > 0 &&
      this._simCards[(this._lockedQueue.getFirst() ?? 0) as number].isEsim;
  }

  public isPinLocked(): boolean {
    return this._lockedQueue.length() > 0 &&
      this._simCards[(this._lockedQueue.getFirst() ?? 0) as number].lockType === LockType.PIN_LOCK;
  }

  public isSimLocked(): boolean {
    return this._lockedQueue.length() > 0 &&
      !this._simCards[(this._lockedQueue.getFirst() ?? 0) as number].isCanSkipVerify;
  }

  public isNeedAddToQueue(slotId: number): boolean {
    if (this._simCards[slotId].hasSkipVerify) {
      log.showInfo(`slot ${slotId} has cancel auth by customer, no need add to locked queue`);
      return false;
    }

    if (this._lockedQueue.length() === 0) {
      return true;
    }

    if (this._lockedQueue.hasItem(slotId)) {
      log.showInfo(`slot ${slotId} has added to locked queue`);
      return false;
    }
    return true;
  }

  public getActiveSimNumber(): number {
    let count: number = 0;
    this._simCards.forEach((simCard) => {
      if (simCard.simState !== sim.SimState.SIM_STATE_UNKNOWN &&
        simCard.simState !== sim.SimState.SIM_STATE_NOT_PRESENT) {
        count++;
      }
    });
    return count;
  }

  public setSkipAuth(isSkip: boolean): void {
    if (this._lockedQueue.length() !== 0) {
      this._simCards[(this._lockedQueue.getFirst() ?? 0) as number].hasSkipVerify = isSkip;
    }
  }

  public getCurSimCard(): SimCard | undefined {
    if (this._lockedQueue.length() > 0) {
      return this._simCards[(this._lockedQueue.getFirst() ?? 0) as number];
    }
    return undefined;
  }

  /**
   * 判断是否跳过了至少一张卡
   *
   * @returns true: 已经跳过了至少一张卡
   */
  public isAlreadySkipAuth(): boolean {
    for (let i = 0; i < this._simCards.length; i++) {
      if (this._simCards[i].hasSkipVerify) {
        return true;
      }
    }
    return false;
  }

  public async updateCardState(slotId: number, simState: sim.SimState): Promise<void> {
    if (slotId < 0 || slotId >= this._simCards.length) {
      log.showWarn(`updateCardState error, slotid ${slotId} invalid`);
      return;
    }
    await this.updateSimCardState(this._simCards[slotId], simState, false);
  }

  public getAvailableTimes(): string {
    if (this.isSimLocked()) {
      let simCard: SimCard | undefined = this.getCurSimCard();
      if (simCard?.lockType === LockType.PIN_LOCK) {
        return simCard?.pinRemainTimes + '';
      } else {
        return simCard?.pukRemainTimes + '';
      }
    }
    return '';
  }
}