/*
 * Copyright (c) Huawei Technologies 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 wifiManager from '@ohos.wifiManager';
import taskpool from '@ohos.taskpool';
import { BusinessError } from '@ohos.base';
import { DeviceUtil } from '@ohos/settings.common/src/main/ets/utils/BaseUtils';
import { LogUtil } from '@ohos/settings.common/src/main/ets/utils/LogUtil';
import { EventBus } from '@ohos/settings.common/src/main/ets/framework/common/EventBus';
import {
  AccessPoint,
  ApMenu,
  CONNECT_RESULT_EVENT,
  WifiScanResult,
  WifiState
} from './model/WifiModel';
import { WifiUtils } from './WifiUtils';

/* instrument ignore file */
const TAG: string = 'WifiTracker : ';
const TAG_MANAGER: string = 'ApConnectManager : ';
const SCAN_RETRY_COUNT: number = 0;
const WIFI_SEC_TYPE_PSK: number = 3;
const WIFI_SEC_TYPE_SAE: number = 4;

enum ConnectResult {
  // 连接超时
  CONNECT_TIME_OUT,
  // 接口异常
  CONNECT_ERROR,
  // 短距上报连接失败
  CONNECT_FAILED,
  // 连接成功
  CONNECT_SUCCESS,
}

function getConnectResultName(reason: number): string {
  switch (reason) {
    case ConnectResult.CONNECT_TIME_OUT:
      return 'CONNECT_TIME_OUT';
    case ConnectResult.CONNECT_ERROR:
      return 'CONNECT_ERROR';
    case ConnectResult.CONNECT_FAILED:
      return 'CONNECT_FAILED';
    case ConnectResult.CONNECT_SUCCESS:
      return 'CONNECT_SUCCESS';
    default:
      return `UNKNOWN(${reason})`;
  }
}

@Concurrent
async function getScanInfo(): Promise<Array<wifiManager.WifiScanInfo>> {
  let res: wifiManager.WifiScanInfo[] = WifiUtils.getScanInfoList();
  return res;
}

@Concurrent
async function getLinkedInfo(): Promise<wifiManager.WifiLinkedInfo> {
  let res = await wifiManager.getLinkedInfo();
  return res;
}

@Concurrent
async function getMultiLinkedInfo(): Promise<Array<wifiManager.WifiLinkedInfo>> {
  let res = await wifiManager.getMultiLinkedInfo();
  return res;
}

@Concurrent
function scanWifi(): boolean {
  return WifiUtils.scanWifi();
}

@Concurrent
function getApConfigMap(): Map<string, Array<wifiManager.WifiDeviceConfig>> {
  let configs: wifiManager.WifiDeviceConfig[] = wifiManager.getDeviceConfigs();
  let configMap: Map<string, Array<wifiManager.WifiDeviceConfig>> =
  new Map<string, Array<wifiManager.WifiDeviceConfig>>();
  for (let config of configs) {
    let key: string = WifiUtils.getApKeyWithConfig(config) as string;
    let configList: wifiManager.WifiDeviceConfig[] = configMap.get(key) as Array<wifiManager.WifiDeviceConfig>;
    if (!configList) {
      configList = [];
    }
    configList.push(config);
    configMap.set(key, configList);
  }
  return configMap;
}

/**
 * 连接结果回调接口
 *
 * @since 2022-03-21
 */
export interface WifiConnectListener {
  /**
   * 连接结果回调
   *
   * @param result 1:连接成功,0:连接失败
   */
  onConnectResult(result: number): void;
}

/**
 * Ap加载回调
 *
 * @since 2022-03-21
 */
export interface WifiTrackerListener {
  /**
   * Ap菜单数据变化回调
   *
   * @param menu 数据变化的Ap菜单
   */
  onApMenuChanged(menu: ApMenu): void;

  /**
   * Ap数据列表加载回调
   *
   * @param menus 加载完的Ap菜单数据列表
   */
  onApMenusChanged(menus: Array<ApMenu>): void;
}

/**
 * 删除结果回调接口
 *
 * @since 2025-01-02
 */
export interface WifiRemoveListener {
  /**
   * 连接结果回调
   *
   * @param result 1:连接成功,0:连接失败
   */
  onRemoveResult(result: boolean): void;
}

/**
 * Wifi 加载后台管理类
 *
 * @since 2022-03-21
 */
export class WifiTracker {
  // Wifi最近一次扫描成功或者失败标记
  public scanActionResult: boolean = false;
  private lastLinkedInfo: wifiManager.WifiLinkedInfo | null = null;
  private lastMultiLinkedInfo: Array<wifiManager.WifiLinkedInfo> | null = null;
  private listeners: WifiTrackerListener[] = [];
  private isRegister: boolean = false;
  private scanResultCache: Map<string, WifiScanResult> = new Map<string, WifiScanResult>();
  private apCache: Map<string, AccessPoint> = new Map<string, AccessPoint>();
  private wifiState: number = 0;
  private nowTimestamp: number = 0;
  private updateCount: number = 0;
  private isListScrolling: boolean = false;
  private connectAccessPoint: AccessPoint | undefined;

  constructor() {
  }

  // Wifi开关状态变化回调
  public onWifiStateChange = (state: number): void => {
    if (!this.isRegister) {
      return;
    }
    LogUtil.info(`${TAG} wifi state change : ${state}`);
    AppStorage.SetOrCreate('wifi_state', state);
    if (state === WifiState.ACTIVE) { // 开启
      this.firstUpdateAndScan();
    }
  };

  // Wifi扫描结果状态变化回调
  public onWifiScanStateChange = (state: number): void => {
    if (!this.isRegister) {
      return;
    }
    LogUtil.info(`${TAG} wifi scan state change : ${state},scroll:${this.isListScrolling}`);
    if (state === 1 && !this.isListScrolling) { // 成功
      this.updateAll();
    }
  };

  //wifi 列表滑动期间不允许刷新页面
  public setListScrolling(isScroll: boolean): void {
    this.isListScrolling = isScroll;
  }

  // Wifi连接状态变化回调
  public onWifiConnectionChange = (state: number): void => {
    if (!this.isRegister) {
      return;
    }
    LogUtil.info(`${TAG} onWifiConnectionChange : ${state}`);
    this.scanAp();
  };

  public setListener(listener: WifiTrackerListener | null): void {
    if (listener) {
      let index = this.listeners.findIndex((item => listener === item));
      if (index === -1) {
        this.listeners.push(listener);
      }
    }
  }

  /**
   * 扫描一次
   *
   * @param retry 失败重试的次数
   */
  public scanAp(retry: number = SCAN_RETRY_COUNT): void {
    new Promise((resolve: (rs: boolean) => void) => {
      LogUtil.info(`${TAG} start scanAp`);
      let result: boolean = false;
      try {
        let scanTask: taskpool.Task = new taskpool.Task(scanWifi);
        taskpool.execute(scanTask, taskpool.Priority.MEDIUM).then((res: Object) => {
          result = res as boolean;
        });
      } catch (err) {
        LogUtil.error(`${TAG} catch exception when scanWifi, code: ${err.code} message: ${err.message}`);
      }
      resolve(result);
    }).then((result: boolean) => {
      LogUtil.info(`${TAG} scanAp result is : ${result}`);
      if (result) {
        this.scanActionResult = true;
        return result;
      }
      if (retry > 0) {
        this.scanAp(--retry);
      } else {
        this.scanActionResult = false;
      }
      return result;
    }).catch((err: BusinessError) => {
      LogUtil.error(`${TAG} scanAp error, error: ${err?.message}`);
    })
  }

  /**
   * 启动工作
   */
  public onStart(): void {
    LogUtil.info(`${TAG} onStart, isRegister: ${this.isRegister}`);
    if (!this.isRegister) {
      LogUtil.info(`${TAG} onStart listen wifi events`);
      WifiUtils.wifiStateChangeOn(TAG, this.onWifiStateChange);
      WifiUtils.wifiScanStateChangeOn(TAG, this.onWifiScanStateChange);
      WifiUtils.wifiConnectionChangeOn(TAG, this.onWifiConnectionChange);
      this.isRegister = true;
      this.firstUpdateAndScan();
    }
  }

  /**
   * 暂停工作
   */
  public onStop(): void {
    LogUtil.info(`${TAG} onStop, isRegister: ${this.isRegister}`);
    if (this.isRegister) {
      LogUtil.info(`${TAG} onStop cancel listen wifi events`);
      WifiUtils.wifiStateChangeOff(TAG, this.onWifiStateChange);
      WifiUtils.wifiScanStateChangeOff(TAG, this.onWifiScanStateChange);
      WifiUtils.wifiConnectionChangeOff(TAG, this.onWifiConnectionChange);
      this.isRegister = false;
    }
  }

  /**
   * 退出,清理数据
   */
  public onQuit(listener?: WifiTrackerListener): void {
    this.onStop();
    this.releaseResource(listener);
  }

  /**
   * 全量刷新:获取连接信息、扫描结果、配置集合来刷新Ap列表
   */
  public async updateAll(): Promise<void> {
    LogUtil.info(`${TAG} updateAll`);
    let scanInfos: wifiManager.WifiScanInfo[] = [];
    try {
      let scanTask: taskpool.Task = new taskpool.Task(getScanInfo);
      scanInfos = await taskpool.execute(scanTask, taskpool.Priority.MEDIUM) as Array<wifiManager.WifiScanInfo>;
    } catch (error) {
      LogUtil.error(`${TAG} catch exception when getScanInfo, errmsg: ${error?.message}`);
    }
    LogUtil.info(`${TAG} taskpool.scanTask success`);
    let scanResults = this.getScanResults(scanInfos);
    this.updateScanResultCache(scanResults);

    let linkedInfo = await this.getLinkedInfoByTaskPool();
    if (linkedInfo && linkedInfo.wifiLinkType && linkedInfo.wifiLinkType === WIFI7_EMLSR) {
      await this.getMultiLinkedInfoByTaskPool();
    }
    let scanResultsMap = this.getScanResultsMap(this.scanResultCache);
    this.updateApList(scanResultsMap);
    LogUtil.info(`${TAG} finished updateAll.`);
  }

  public async getLinkedInfoByTaskPool(): Promise<wifiManager.WifiLinkedInfo | null> {
    try {
      let getLinkTask: taskpool.Task = new taskpool.Task(getLinkedInfo);
      this.lastLinkedInfo = await taskpool.execute(getLinkTask, taskpool.Priority.MEDIUM) as wifiManager.WifiLinkedInfo;
    } catch (e) {
      LogUtil.error(`${TAG} catch exception when getLinkedInfo, errmsg: ${e?.message}`);
    }
    LogUtil.info(`${TAG} taskpool.getLinkTask success`);
    return this.lastLinkedInfo;
  }

  public async getMultiLinkedInfoByTaskPool(): Promise<Array<wifiManager.WifiLinkedInfo> | null> {
    try {
      let getMultiLinkTask: taskpool.Task = new taskpool.Task(getMultiLinkedInfo);
      this.lastMultiLinkedInfo = await taskpool.execute(getMultiLinkTask, taskpool.Priority.MEDIUM) as
      Array<wifiManager.WifiLinkedInfo>;
    } catch (e) {
      LogUtil.error(`${TAG} catch exception when getMultiLinkedInfo, errmsg: ${e?.message}`);
    }
    LogUtil.info(`${TAG} taskpool.getMultiLinkTask success`);
    return this.lastMultiLinkedInfo;
  }
  
  /**
   * 连接Ap
   *
   * @param config 需要连接的Ap信息
   * @param isConfigUpdate 是否需要更新已保存的Ap配置
   */
  public async connectToWifiDevice(config: wifiManager.WifiDeviceConfig, isConfigUpdate: boolean): Promise<boolean> {
    if (!config) {
      return false;
    }
    LogUtil.info(`${TAG} connectToWifiDevice: ${WifiUtils.getLogSsidString(config.ssid)} securityType:${config.securityType}`);

    // 获取已保存的config
    let wifiConfig = WifiUtils.getConfig(config.ssid, config.securityType) as wifiManager.WifiDeviceConfig;

    let networkId: number | undefined = undefined;
    if (!wifiConfig) {
      // 未保存,先保存config
      LogUtil.info(`${TAG} connectToWifiDevice addDeviceConfig.`);
      networkId = await wifiManager.addDeviceConfig(config);
    } else {
      LogUtil.info(`${TAG} connectToWifiDevice wifiConfig is saved isConfigUpdate: ${isConfigUpdate}`);
      if (isConfigUpdate) {
        // 已保存,config需要更新,先删除,再保存
        let result = wifiManager.removeDevice(wifiConfig.netId);
        LogUtil.info(`${TAG} connectToWifiDevice removeDevice result : ${result}`);
        networkId = await wifiManager.addDeviceConfig(config);
      } else {
        // config无需更新,直接用netId去连接
        networkId = wifiConfig.netId;
      }
    }
    LogUtil.info(`${TAG} connectToWifiDevice network is : ${networkId}`);
    if (!WifiUtils.isValidNetworkId(networkId)) {
      LogUtil.error(`${TAG} connectToWifiDevice, network is inValid.`);
      try {
        wifiManager.connectToDevice(config);
        return true;
      } catch (error) {
        LogUtil.error(`${TAG} connectToDevice occurs error: ${error?.message}`);
      }
      return false;
    }
    try {
      wifiManager.connectToNetwork(networkId);
      return true;
    } catch (error) {
      LogUtil.error(`${TAG} connectToWifiDevice connectToNetwork error: ${error?.message}`);
      return false;
    }
  }

  /**
   * 释放listener
   *
   * @param listener WifiTrackerListener对象
   */
  public releaseResource(listener?: WifiTrackerListener): void {
    if (this.listeners.length === 0) {
      return;
    }
    if (!listener) {
      this.listeners.splice(0, this.listeners.length);
      this.scanResultCache.clear();
      return;
    }
    let findIndex = this.listeners.findIndex(item => item === listener);
    if (findIndex !== -1) {
      this.listeners.splice(findIndex, 1);
      this.scanResultCache.clear();
    }
  }

  /**
   * 删除已保存的Ap
   *
   * @param config 待删除的Ap信息
   */
  public async removeWifiDevice(config: wifiManager.WifiDeviceConfig, isRefresh?: boolean,
    removeCallback?: WifiRemoveListener): Promise<void> {
    if (!config) {
      LogUtil.warn(`${TAG} removeWifiDevice, config is invalid!`);
      return;
    }
    let wifiConfig = config as wifiManager.WifiDeviceConfig;
    let removeNetId =
      (WifiUtils.getConfig(wifiConfig.ssid, wifiConfig.securityType) as wifiManager.WifiDeviceConfig)?.netId;
    if (!WifiUtils.isValidNetworkId(removeNetId)) {
      LogUtil.info(`${TAG} removeWifiDevice, config can not found, return`);
      return;
    }
    LogUtil.info(`${TAG} removeWifiDevice id: ${removeNetId}`);
    // 若删除的正是当前"正在连接/切换中"的网络,先复位连接状态机,
    // 否则其会卡在 DISCONNECTING 状态,导致 UI 一直显示"正在断开连接"。
    apConnectManager.cancelConnectIfRemoved(wifiConfig.ssid, wifiConfig.securityType, removeNetId as number);
    if (isRefresh === undefined) {
      // 默认需要刷新
      isRefresh = true;
    }
    if (wifiManager.isConnected() && isRefresh) {
      let linkedInfo = await wifiTracker.getLinkedInfoByTaskPool();
      LogUtil.info(`${TAG} linkedInfo id: ${linkedInfo?.networkId}`);
      if (linkedInfo?.networkId === removeNetId) {
        LogUtil.info(`${TAG} removeWifiDevice, wifi is Connected`);
        let result = WifiUtils.disconnect();
        LogUtil.info(`${TAG} removeWifiDevice, disconnect result is ${result}`);
      }
    }
    let result = await Promise.resolve(WifiUtils.removeDevice(removeNetId as number));
    LogUtil.info(`${TAG} removeDevice result is : ${result}`);
    if (removeCallback) {
      removeCallback.onRemoveResult(result);
    }
    if (isRefresh) {
      this.updateAll();
    }
    this.apCache.get(WifiUtils.getApKeyWithConfig(config) as string)?.setConfigs(null);
    if (isRefresh) {
      this.scanAp();
    }
  }

  /**
   * 删除已连接ap信息
   *
   * @param network 待删除的ap信息
   */
  public async removeWifiDeviceById(network: number): Promise<void> {
    if (!WifiUtils.isValidNetworkId(network)) {
      LogUtil.error(`${TAG} removeWifiDeviceById, network is ${network}.`);
      return;
    }
    // 若删除的正是当前"正在连接/切换中"的网络,先复位连接状态机,避免卡在 DISCONNECTING。
    apConnectManager.cancelConnectIfRemoved(undefined, undefined, network);
    if (wifiManager.isConnected()) {
      LogUtil.info(`${TAG} removeWifiDeviceById, wifi is Connected`);
      let result = WifiUtils.disconnect();
      LogUtil.info(`${TAG} removeWifiDeviceById, disconnect result is ${result}`);
    }
    let result = await Promise.resolve(WifiUtils.removeDevice(network));
    LogUtil.info(`${TAG} removeWifiDeviceById result is : ${result}, network is ${network}`);
    this.updateAll();
    this.scanAp();
  }

  private firstUpdateAndScan(): void {
    this.scanAp();
  }

  private getScanResults(scanInfos: Array<wifiManager.WifiScanInfo>): Array<WifiScanResult> {
    let wifiScanResults: WifiScanResult[] = [];
    for (const info of scanInfos) {
      wifiScanResults.push(new WifiScanResult(info));
    }
    LogUtil.info(`${TAG} get wifiScanResults.length: ${wifiScanResults.length}`);
    return wifiScanResults;
  }

  private filterScanResultsByCapabilities(scanResults: Array<WifiScanResult>): Array<WifiScanResult> {
    return scanResults;
  }

  private updateScanResultCache(scanResults: Array<WifiScanResult>): void {
    if (scanResults && scanResults.length === 0) {
      LogUtil.warn(`${TAG} updateScanResultCache ,scanResults is null `);
      return;
    }
    this.scanResultCache.clear();
    scanResults.forEach(scanResult => {
      if (scanResult && scanResult.ssid && scanResult.bssid) {
        this.scanResultCache.set(scanResult.bssid, scanResult);
      }
    });
    LogUtil.info(`${TAG} updateScanResultCache success ,scanResultCache size: ${this.scanResultCache.size} `);
  }

  private updateAccessPoints(): void {
    LogUtil.info(`${TAG} updateAccessPoints, getRealTime start`);
    this.filterOldScanResultCacheAndUpdate();
  }

  private async filterOldScanResultCacheAndUpdate(): Promise<void> {
    // 当前扫描结果中的时间戳都是一致的,暂没有过滤
    let scanResultsMap = await Promise.resolve(this.getScanResultsMap(this.scanResultCache));
    this.updateApList(scanResultsMap);
  }

  private getScanResultsMap(cache: Map<string, WifiScanResult>): Map<string, Array<WifiScanResult>> {
    let scanResultsMap: Map<string, Array<WifiScanResult>> = new Map<string, Array<WifiScanResult>>();
    for (let item of Array.from(cache.values())) {
      let apKey: string = WifiUtils.getApKeyWithScanResult(item as WifiScanResult) as string;
      let results: WifiScanResult[] = scanResultsMap.get(apKey) as Array<WifiScanResult>;
      if (!results) {
        results = [];
        scanResultsMap.set(apKey, results);
      }
      results.push(item as WifiScanResult);
    }
    LogUtil.info(`${TAG} getScanResultsMap success, scanResultsMap.size:${scanResultsMap.size}`);
    return scanResultsMap;
  }

  private async updateApList(scanResultsMap: Map<string, Array<WifiScanResult>>): Promise<void> {
    let configMap: Map<string, Array<wifiManager.WifiDeviceConfig>> =
    new Map<string, Array<wifiManager.WifiDeviceConfig>>();
    try {
      let getCmTask: taskpool.Task = new taskpool.Task(getApConfigMap);
      configMap = await taskpool.execute(getCmTask, taskpool.Priority.MEDIUM) as
      Map<string, Array<wifiManager.WifiDeviceConfig>>;
    } catch (error) {
      LogUtil.error(`${TAG} catch exception when getCmTask, errmsg: ${error?.message}`);
    }
    LogUtil.info(`${TAG} taskpool.getCmTask success`);
    let apList = this.updateApListInner(scanResultsMap, configMap);
    let menus = this.apListToMenuList(apList);
    // 直接保存最后一次的菜单数据
    AppStorage.setOrCreate('wifiMenusCache', menus);
    LogUtil.info(`${TAG} updateApList scanResultsMap.size:${scanResultsMap.size}, apList.length:${apList.length}`);
    if (this.listeners) {
      LogUtil.info(`${TAG} listener.onApMenusChanged menus.length:${menus.length}`);
      for (let listener of this.listeners) {
        listener.onApMenusChanged(menus);
      }
    }
  }

  /**
   * 获取连接wifi信息
   *
   * @returns AccessPoint 已连接wifi信息
   */
  public getConnectAccessPoint(): AccessPoint | undefined {
    return this.connectAccessPoint;
  }

  // 直接从进程级的缓存刷新菜单
  public loadCacheMenus(): void {
    const menus = AppStorage.get<Array<ApMenu>>('wifiMenusCache') || []
    if (menus.length === 0) {
      this.updateAll();
      return
    }
    if (this.listeners) {
      LogUtil.info(`${TAG} loadCacheMenus listener.onApMenusChanged : ${menus.length}`);
      for (let listener of this.listeners) {
        listener.onApMenusChanged(menus);
      }
    }
  }

  private updateApListInner(scanResultsMap: Map<string, WifiScanResult[]>,
                            configMap: Map<string, wifiManager.WifiDeviceConfig[]>): AccessPoint[] {
    let apList: AccessPoint[] = [];
    for (let scanResults of Array.from(scanResultsMap.values())) {
      let resultApKey: string = WifiUtils.getApKeyWithScanResult(scanResults[0]) as string;
      let ap: AccessPoint = this.apCache.get(resultApKey) as AccessPoint;
      if (!ap) {
        ap = new AccessPoint(scanResults);
        this.apCache.set(resultApKey, ap);
      } else {
        ap.setScanResults(scanResults);
      }
      // 更新
      ap.setConfigs(configMap.get(ap.apKey) as Array<wifiManager.WifiDeviceConfig>);

      // hota升级场景,同一个网络的securityType字段,scanInfo接口返回4 deviceConfig接口返回3,根据约定也判定为同一个网络。
      this.upgradeScene(ap, configMap);

      ap.updateLastLinkedInfo(this.lastLinkedInfo as wifiManager.WifiLinkedInfo);
      apList.push(ap);
    }
    apList.sort(AccessPoint.compare);
    let updateApCache = new Map<string, AccessPoint>();
    for (let ap of apList) {
      updateApCache.set(ap.apKey, ap)
    }
    this.apCache = updateApCache;

    LogUtil.info(`${TAG} updateApListInner success, apList.length:${apList.length} connState:${this.lastLinkedInfo?.connState} networkId: ${this.lastLinkedInfo?.networkId}`);
    return apList;
  }

  private upgradeScene(ap: AccessPoint, configMap: Map<string, wifiManager.WifiDeviceConfig[]>) {
    if (DeviceUtil.isWearable()) {
      return;
    }
    if (!ap || !configMap) {
      return;
    }
    if (!ap.isSaved() && ap.securityType === WIFI_SEC_TYPE_SAE && ap.capabilities.includes('WPA2-PSK')) {
      let config = configMap.get(WifiUtils.getApKey(ap.ssid, ap.bestBssid, WIFI_SEC_TYPE_PSK) ?? '');
      if (config && config.length > 0) {
        LogUtil.info(`${TAG} hota update scene, set configs: ${WifiUtils.getLogSsidString(ap.ssid)}`);
        config[0].securityType = WIFI_SEC_TYPE_SAE;
        ap.setConfigs(config as Array<wifiManager.WifiDeviceConfig>);
        WifiUtils.updateNetwork(config[0]);
      }
    }
  }

  private apListToMenuList(apList: Array<AccessPoint>): Array<ApMenu> {
    let menus: ApMenu[] = [];
    for (let index = 0; index < apList.length; index++) {
      let ap: AccessPoint = apList[index];
      ap.lastIndex = index;
      ap.lastTimestamp = this.nowTimestamp;
      LogUtil.info(`${TAG} apListToMenuList, ${ap?.toString()}`);
      if (ap.isConnected()) {
        this.connectAccessPoint = ap;
      }
      if (!ap.isReachable() || !ap.isValid() || ap.isConnected()) {
        LogUtil.info(`${TAG} apListToMenuList ${WifiUtils.getLogSsidString(ap?.ssid)} invalid`);
        continue;
      }
      let apMenu: ApMenu = this.apToMenu(ap) as ApMenu;
      if (apMenu) {
        menus.push(apMenu);
      }
    }
    LogUtil.info(`${TAG} apListToMenuList success, menus: ${menus.length}`);
    return menus;
  }

  private apToMenu(ap: AccessPoint): ApMenu | void {
    if (!ap.isReachable() || !ap.isValid() || ap.isConnected()) {
      return;
    }
    return new ApMenu({
      key: ap.apKey,
      index: ap.lastIndex,
      title: ap.getTitle(),
      summary: ap.getSummary(),
      stateIcon: ap.getStateIcon(),
      timestamp: ap.lastTimestamp,
      ssid: ap.ssid,
      bssid: ap.bestBssid,
      bssidType: ap.bssidType,
      level: ap.bestLevel,
      scanResults: ap.scanResults,
      securityType: ap.securityType,
      capabilities: ap.capabilities,
      config: ap.getBestConfig() as wifiManager.WifiDeviceConfig,
      supportedWifiCategory: ap.supportedWifiCategory,
      isHiLinkNetwork: ap.isHiLinkNetwork,
      isHiLinkProNetwork: ap.isHiLinkProNetwork
    });
  }
}

export let wifiTracker = new WifiTracker();

/**
 * AP连接管理类
 *
 * @since 2022-03-21
 */
class ApConnectManager {
  public static CONNECT_TIMEOUT: number = 60000;
  public static IDLE: number = 0;
  public static CONNECTING: number = 1;
  public static DISCONNECTING: number = -1;
  public isRegister: boolean = false;
  public isRetry: boolean = false;
  public wifiConnectState: number = ApConnectManager.IDLE;
  public connectConfig: wifiManager.WifiDeviceConfig | null = null;
  public connectApKey: string | null = null;
  public listener: WifiConnectListener | null = null;
  public timerId: number | null = null;
  public isConfigUpdate: boolean = false;

  public onWifiConnectionChange = (state: number): void => {
    LogUtil.info(`${TAG_MANAGER} onWifiConnectionChange state=${state} (1=connected else=disconnected/failed), wifiConnectState=${this.wifiConnectState}`);

    if (state === 1) { // 连接上
      LogUtil.info(`${TAG_MANAGER} wifi connected, notify CONNECT_SUCCESS`);
      this.onConnectResult(1, ConnectResult.CONNECT_SUCCESS);
    } else { // 连接断开、失败
      if (this.wifiConnectState === ApConnectManager.DISCONNECTING) {
        LogUtil.info(`${TAG_MANAGER} Disconnected after disconnect request, start connect to target network`);
        this.connectToWifiDevice(this.isConfigUpdate).catch((err: Error) => {
          LogUtil.error(`${TAG_MANAGER} connectToWifiDevice failed (CONNECT_ERROR): ${err?.message}`);
          this.onConnectResult(0, ConnectResult.CONNECT_ERROR);
        });
        return;
      }
      LogUtil.info(`${TAG_MANAGER} wifi disconnected/failed without disconnecting first, notify CONNECT_FAILED`);
      this.onConnectResult(0, ConnectResult.CONNECT_FAILED);
    }
  };

  /**
   * hiLink路由器自动连接
   *
   * @param listener 连接结果回调
   */
  public hiLinkConnecting(listener: WifiConnectListener): void {
    this.finishConnect();
    this.wifiConnectState = ApConnectManager.CONNECTING;
    this.listener = listener;
    this.register();
    this.timerId = setTimeout(() => {
      LogUtil.info(`${TAG_MANAGER} connect timeout (${ApConnectManager.CONNECT_TIMEOUT}ms) ssid=${WifiUtils.getLogSsidString(this.connectConfig?.ssid ?? '')}, reason=CONNECT_TIME_OUT`);
      this.timerId = null;
      this.onConnectResult(0, ConnectResult.CONNECT_TIME_OUT);
    }, ApConnectManager.CONNECT_TIMEOUT);
  }

  /**
   * 发起一次连接
   *
   * @param config 需要连接的Ap信息
   * @param isConfigUpdate 是否需要更新已保存的Ap配置(如重连、修改密码后需重新保存时传 true)
   * @param listener 连接结果回调
   * @param isRetry 是否重试
   * @return 是否启动连接
   */
  public startConnect(config: wifiManager.WifiDeviceConfig, isConfigUpdate: boolean, listener:
    WifiConnectListener, isRetry: boolean = false): boolean {
    LogUtil.info(`${TAG_MANAGER} startConnect ssid=${WifiUtils.getLogSsidString(config?.ssid ?? '')} isConfigUpdate=${isConfigUpdate} isRetry=${isRetry} netId=${config?.netId}`);
    if (!isRetry && this.isRepeatConnect(config)) {
      LogUtil.warn(`${TAG_MANAGER} repeat operation: the same network(${config?.netId}) is connecting, try later please.`);
      return false;
    }
    this.resetState(config, isConfigUpdate, listener, isRetry);
    try {
      if (wifiManager.isConnected()) {
        LogUtil.info(`${TAG_MANAGER} wifi is Connected, disconnect first then connect`);
        if (WifiUtils.disconnect()) {
          LogUtil.info(`${TAG_MANAGER} disconnect success, wait onWifiConnectionChange then connect`);
          this.wifiConnectState = ApConnectManager.DISCONNECTING;
          return true;
        } else {
          LogUtil.error(`${TAG_MANAGER} disconnect failed, cannot switch network (CONNECT_ERROR).`);
          this.onConnectResult(0, ConnectResult.CONNECT_ERROR);
          return false;
        }
      }
    } catch (error) {
      LogUtil.error(`${TAG_MANAGER} get wifi connect state failed: ${(error as Error)?.message}`);
    }
    LogUtil.info(`${TAG_MANAGER} startConnect direct connect (wifi not connected), wifiConnectState=CONNECTING`);
    this.wifiConnectState = ApConnectManager.CONNECTING;
    this.connectToWifiDevice(this.isConfigUpdate).catch((err: Error) => {
      LogUtil.error(`${TAG_MANAGER} connectToWifiDevice failed (CONNECT_ERROR): ${err?.message}`);
      this.onConnectResult(0, ConnectResult.CONNECT_ERROR);
    });
    return true;
  }

  /**
   * 发起一次连接(异步)
   *
   * @param config 需要连接的Ap信息
   * @param isConfigUpdate 是否需要更新已保存的Ap配置(如重连、修改密码后需重新保存时传 true)
   * @param listener 连接结果回调
   * @param isRetry 是否重试
   * @return 是否启动连接
   */
  public async startConnectAsync(config: wifiManager.WifiDeviceConfig, isConfigUpdate: boolean,
    listener: WifiConnectListener, isRetry: boolean = false): Promise<boolean> {
    LogUtil.info(`${TAG_MANAGER} startConnectAsync ssid=${WifiUtils.getLogSsidString(config?.ssid ?? '')} isConfigUpdate=${isConfigUpdate} isRetry=${isRetry} netId=${config?.netId}`);
    if (!isRetry && this.isRepeatConnect(config)) {
      LogUtil.warn(`${TAG_MANAGER} repeat operation: the same network(${config?.netId}) is connecting, try later please.`);
      return false;
    }
    this.resetState(config, isConfigUpdate, listener, isRetry);
    try {
      if (wifiManager.isConnected()) {
        LogUtil.info(`${TAG_MANAGER} wifi is Connected, disconnect first then connect`);
        if (WifiUtils.disconnect()) {
          LogUtil.info(`${TAG_MANAGER} disconnect success, wait onWifiConnectionChange then connect`);
          this.wifiConnectState = ApConnectManager.DISCONNECTING;
          return true;
        } else {
          LogUtil.error(`${TAG_MANAGER} disconnect failed, cannot switch network (CONNECT_ERROR).`);
          this.onConnectResult(0, ConnectResult.CONNECT_ERROR);
          return false;
        }
      }
    } catch (error) {
      LogUtil.error(`${TAG_MANAGER} get wifi connect state failed: ${(error as Error)?.message}`);
    }
    LogUtil.info(`${TAG_MANAGER} startConnectAsync direct connect (wifi not connected), wifiConnectState=CONNECTING`);
    this.wifiConnectState = ApConnectManager.CONNECTING;
    await this.connectToWifiDevice(this.isConfigUpdate);
    return true;
  }

  private resetState(config: wifiManager.WifiDeviceConfig, isConfigUpdate: boolean, listener:
    WifiConnectListener, isRetry: boolean = false): void {
    this.finishConnect();
    this.connectConfig = config as wifiManager.WifiDeviceConfig;
    this.connectApKey = WifiUtils.getApKey(this.connectConfig.ssid, this.connectConfig.bssid ?? '',
      this.connectConfig.securityType);
    this.isConfigUpdate = isConfigUpdate;
    this.listener = listener;
    this.isRetry = isRetry;
    this.register();
    this.timerId = setTimeout(() => {
      LogUtil.info(`${TAG_MANAGER} connect timeout (${ApConnectManager.CONNECT_TIMEOUT}ms) ssid=${WifiUtils.getLogSsidString(this.connectConfig?.ssid ?? '')}, reason=CONNECT_TIME_OUT`);
      this.timerId = null;
      this.onConnectResult(0, ConnectResult.CONNECT_TIME_OUT);
    }, ApConnectManager.CONNECT_TIMEOUT);
  }

  private isRepeatConnect(newConfig: wifiManager.WifiDeviceConfig): boolean {
    if (!this.connectConfig || !newConfig) {
      return false;
    }
    let oldKey = WifiUtils.getApKey(this.connectConfig.ssid, this.connectConfig.bssid ?? '',
      this.connectConfig.securityType);
    let newKey = WifiUtils.getApKey(newConfig.ssid, newConfig.bssid ?? '', newConfig.securityType);
    return this.wifiConnectState === ApConnectManager.CONNECTING && oldKey === newKey;
  }

  /**
   * 判断是否是上一个发起连接的的热点
   *
   * @param newConfig 需要判断的Ap信息
   * @returns 不是上一个发起连接的的热点 true
   */
  public isNewConnect(newConfig: wifiManager.WifiDeviceConfig): boolean {
    if (!this.connectConfig || !newConfig) {
      return false;
    }
    let oldKey = WifiUtils.getApKey(this.connectConfig.ssid, this.connectConfig.bssid ?? '',
      this.connectConfig.securityType);
    let newKey = WifiUtils.getApKey(newConfig.ssid, newConfig.bssid ?? '', newConfig.securityType);
    return oldKey !== newKey;
  }

  /**
   * 取消当前正在进行的连接/切换(仅当目标网络为被删除的网络时)。
   * 用于删除"正在连接/切换中"的网络场景:复位状态机、注销监听,
   * 避免 DISCONNECTING 状态卡死导致 UI 一直显示"正在断开连接",并避免断开后重连已删除的网络。
   *
   * @param ssid 被删除网络的ssid
   * @param securityType 被删除网络的安全类型
   * @param netId 被删除网络的netId(可选)
   * @returns 是否命中并复位
   */
  public cancelConnectIfRemoved(ssid?: string, securityType?: number, netId?: number): boolean {
    if (this.wifiConnectState === ApConnectManager.IDLE || !this.connectConfig) {
      return false;
    }
    let isMatch: boolean = false;
    if (ssid && this.connectConfig.ssid === ssid &&
      (securityType === undefined || this.connectConfig.securityType === securityType)) {
      isMatch = true;
    }
    if (!isMatch && WifiUtils.isValidNetworkId(netId as number) && this.connectConfig.netId === netId) {
      isMatch = true;
    }
    if (!isMatch) {
      return false;
    }
    LogUtil.info(`${TAG_MANAGER} cancelConnectIfRemoved: target network removed during connecting, reset state to IDLE.`);
    EventBus.getInstance().emit(CONNECT_RESULT_EVENT, false);
    this.finishConnect();
    return true;
  }

  private onConnectResult(result: number, reason: number): void {
    LogUtil.info(`${TAG_MANAGER} onConnectResult result=${result} (1=success 0=fail), reason=${getConnectResultName(reason)}`);
    this.listener?.onConnectResult(result);
    if (result === 1 || !WifiUtils.willReconnect(this.connectConfig) || this.isRetry ||
      reason === ConnectResult.CONNECT_ERROR || reason === ConnectResult.CONNECT_TIME_OUT) {
      EventBus.getInstance().emit(CONNECT_RESULT_EVENT, result === 1);
      this.finishConnect();
    }
  }

  private finishConnect(): void {
    LogUtil.info(`${TAG_MANAGER} finishConnect: clear timeout timer and reset state.`);
    if (this.timerId !== null) {
      clearTimeout(this.timerId);
      this.timerId = null;
    }
    this.unRegister();
    this.wifiConnectState = ApConnectManager.IDLE;
    this.connectConfig = null;
    this.isConfigUpdate = false;
    this.isRetry = false;
    this.listener = null;
  }

  private register(): void {
    if (!this.isRegister) {
      LogUtil.info(`${TAG_MANAGER} register`);
      WifiUtils.wifiConnectionChangeOn(TAG_MANAGER, this.onWifiConnectionChange);
      this.isRegister = true;
    }
  }

  private unRegister(): void {
    if (this.isRegister) {
      LogUtil.info(`${TAG_MANAGER} unRegister`);
      WifiUtils.wifiConnectionChangeOff(TAG_MANAGER, this.onWifiConnectionChange);
      this.isRegister = false;
    }
  }

  private async connectToWifiDevice(isConfigUpdate: boolean): Promise<void> {
    let ssidLog = WifiUtils.getLogSsidString(this.connectConfig?.ssid ?? '');
    LogUtil.info(`${TAG_MANAGER} connectToWifiDevice start ssid=${ssidLog} isConfigUpdate=${isConfigUpdate}`);
    let result = await wifiTracker.connectToWifiDevice(this.connectConfig as wifiManager.WifiDeviceConfig,
      isConfigUpdate);
    LogUtil.info(`${TAG_MANAGER} connectToWifiDevice end ssid=${ssidLog} result=${result} (true=api ok, false=api error)`);
    AppStorage.setOrCreate<Boolean>('isConnectCancel', false);
    if (result) {
      this.wifiConnectState = ApConnectManager.CONNECTING;
    } else {
      this.onConnectResult(0, ConnectResult.CONNECT_ERROR);
    }
  }
}

export let apConnectManager = new ApConnectManager();
export const WIFI7_EMLSR: number = 3;