/*
 * 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 util from '@ohos.util';
// import hdsDrawable from '@hms.hds.hdsDrawable';
import { image } from '@kit.ImageKit';
import bundle from '@ohos.bundle.bundleManager';
import bundleResourceManager from '@ohos.bundle.bundleResourceManager';
import { DeviceUtil } from '@ohos/settings.common/src/main/ets/utils/BaseUtils';
import { LogUtil } from '@ohos/settings.common/src/main/ets/utils/LogUtil';
import { BaseEntryMenu } from '@ohos/settings.common/src/main/ets/core/model/menu/BaseMenu';
import { AppUtils } from './AppUtils';

const TAG: string = 'AppModel : ';
const UID_MARK: number = 200000;
const DEFAULT_ACTIVATED_ID: number = 100;
const ICON_DEFAULT_SIZE: number = 48;
const APP_ICON_STANDARD_WIDTH: number = vp2px(ICON_DEFAULT_SIZE);
const APP_ICON_STANDARD_HEIGHT: number = vp2px(ICON_DEFAULT_SIZE);
const DEFAULT_LENGTH: number = 64;

export const enum AppType {
  SYSTEM_APP = 'systemApp',
  UN_SYSTEM_APP = 'unSystemApp',
  SERVICE_APP = 'meetaApp',
  CONTAINER_APP = 'containerApp',
  UNKNOWN_TYPE = 'unknownApp'
}

/**
 * @name Indicates the custom metadata
 * @since 7
 * @syscap SystemCapability.BundleManager.BundleFramework
 * @permission NA
 *
 */
export interface CustomizeData {
  /**
   * @default Indicates the custom metadata name
   * @since 7
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  name: string;

  /**
   * @default Indicates the custom metadata value
   * @since 7
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  value: string;

  /**
   * @default Indicates the custom metadata resource
   * @since 8
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  extra: string;
}

/**
 * @name Indicates the Metadata
 * @since 9
 * @syscap SystemCapability.BundleManager.BundleFramework
 * @permission NA
 *
 */
export interface Metadata {
  /**
   * @default Indicates the metadata name
   * @since 9
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  name: string;

  /**
   * @default Indicates the metadata value
   * @since 9
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  value: string;

  /**
   * @default Indicates the metadata resource
   * @since 9
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  resource: string;
}

/**
 * @name Stores module information about an application.
 * @since 7
 * @syscap SystemCapability.BundleManager.BundleFramework
 * @permission NA
 *
 */
export interface ModuleInfo {
  /**
   * The module name.
   *
   * @default Indicates the name of the .hap package to which the capability belongs
   *
   * @since 7
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  readonly moduleName: string;

  /**
   * The module source path.
   *
   * @default Indicates the module source dir of this module
   *
   * @since 7
   * @syscap SystemCapability.BundleManager.BundleFramework
   */
  readonly moduleSourceDir: string;
}

// /**
//  * HDS处理的数据集合
//  *
//  * @since 2024-12-24
//  */
// export interface HdsHandleIcons {
//   /**
//    * bundle name and pixelMap array
//    *
//    * @description drawableDescriptor icon
//    */
//   pixelMapIcons: hdsDrawable.Icon[];
//
//   /**
//    * bundle name and LayeredDrawableDescriptor array
//    *
//    * @description LayeredDrawableDescriptor icon
//    */
//   layeredIcons: hdsDrawable.LayeredIcon[];
// }

export class BasicAppEntry {
  public name: string = '';
  public codePath: string = '';
  public uid: number = 0;
  public bundleType: number = 0;
  public systemApp: boolean = false;
  public enabled: boolean = true;
  public dataUnclearable: boolean = false;
  public multiAppModeMaxCount?: number;
}

/**
 * 应用数据类
 *
 * @since 2022-05-26
 */
export class AppEntry {
  public appInfo?: BasicAppEntry;
  public name: string;
  public removable: boolean = false;
  public userDataClearable: boolean = true;
  public label?: string;
  public labelId: number = 0;
  public icon?: ResourceStr | PixelMap;
  public iconId = 0;
  public appSize: string = '';
  public cacheSize: string = '';
  public dataSize: string = '';
  public totalSize: string = '';
  public versionName: string;
  public updateTime: number;
  public locale: string = '';
  public currentActivatedId: number = DEFAULT_ACTIVATED_ID;
  public moduleName?: string = '';
  public appIndex: number = 0;
  public installSource: string = '';

  constructor(bundleInfo?: bundle.BundleInfo) {
    if (bundleInfo) {
      this.name = bundleInfo.name;
      this.versionName = bundleInfo.versionName;
      this.updateTime = bundleInfo.updateTime;
      this.updateAppInfo(bundleInfo.appInfo);
    } else {
      this.name = '';
      this.versionName = '';
      this.updateTime = 0;
    }
  }

  /**
   * 深拷贝应用数据对象
   *
   * @param entry 原应用数据对象
   * @returns 返回深拷贝后的新应用数据对象
   */
  public static copy(entry: AppEntry): AppEntry {
    let result: AppEntry = new AppEntry();
    if (!entry) {
      return result;
    }
    if (entry.appInfo) {
      let appInfo: BasicAppEntry = new BasicAppEntry();
      appInfo.name = entry.appInfo.name;
      appInfo.codePath = entry.appInfo.codePath;
      appInfo.uid = entry.appInfo.uid;
      appInfo.systemApp = entry.appInfo.systemApp;
      appInfo.bundleType = entry.appInfo.bundleType;
      appInfo.multiAppModeMaxCount = entry.appInfo.multiAppModeMaxCount;
      appInfo.enabled = entry.appInfo.enabled;
      appInfo.dataUnclearable = entry.appInfo.dataUnclearable;
      result.appInfo = appInfo;
    }
    result.name = entry.name;
    result.removable = entry.removable;
    result.userDataClearable = entry.userDataClearable;
    result.label = entry.label;
    result.labelId = entry.labelId;
    result.icon = entry.icon;
    result.iconId = entry.iconId;
    result.appSize = entry.appSize;
    result.cacheSize = entry.cacheSize;
    result.dataSize = entry.dataSize;
    result.totalSize = entry.totalSize;
    result.versionName = entry.versionName;
    result.updateTime = entry.updateTime;
    result.locale = entry.locale;
    result.currentActivatedId = entry.currentActivatedId;
    result.moduleName = entry.moduleName;
    result.appIndex = entry.appIndex;
    result.installSource = entry.installSource;
    return result;
  }

  /**
   * updateAppInfo 更新应用信息数据
   *
   * @param appInfo 应用信息数据
   */
  public updateAppInfo(appInfo: bundle.ApplicationInfo): void {
    if (!appInfo) {
      LogUtil.info(`${TAG} updateAppInfo return`);
      return;
    }

    this.appInfo = new BasicAppEntry();
    this.appInfo.name = appInfo.name;
    this.appInfo.codePath = appInfo.codePath;
    this.appInfo.uid = appInfo.uid;
    this.appInfo.systemApp = appInfo.systemApp;
    this.appInfo.bundleType = appInfo.bundleType;
    this.appInfo.multiAppModeMaxCount = appInfo.multiAppMode?.maxCount;
    this.appInfo.enabled = appInfo.enabled;
    this.appInfo.dataUnclearable = appInfo.dataUnclearable;

    this.removable = appInfo.removable;
    this.labelId = appInfo.labelId;
    this.label = appInfo.label;
    this.iconId = appInfo.iconId;
    this.icon = appInfo.icon;
    this.moduleName = appInfo?.labelResource?.moduleName ?? '';
    this.appIndex = appInfo.appIndex;
    this.installSource = appInfo.installSource;
  }

  /**
   * 获取激活状态的系统帐号的ID列表
   */
  public getCurrentActivatedId(): number {
    LogUtil.info(`${TAG} currentActivatedId is ${this.currentActivatedId}`);
    return this.currentActivatedId;
  }

  /**
   * 设置激活状态的系统帐号的ID列表
   *
   * @param currentActivatedId 激活状态的系统帐号的ID列表
   */
  public setCurrentActivatedId(currentActivatedId: number): void {
    if (!currentActivatedId || this.currentActivatedId === currentActivatedId) {
      LogUtil.info(`${TAG} setCurrentActivatedId return`);
      return;
    }
    this.currentActivatedId = currentActivatedId;
  }

  public isLocaleMatch(locale: string): boolean {
    return this.locale === locale;
  }

  public isBundleMatch(bundleInfo: bundle.BundleInfo): boolean {
    if (!bundleInfo) {
      return false;
    }
    if (!this.isVersionMatch(bundleInfo.versionName)) {
      LogUtil.info(`${TAG} version change`);
      return false;
    }
    if (this.updateTime !== bundleInfo.updateTime) {
      LogUtil.info(`${TAG} updateTime change`);
      return false;
    }
    return true;
  }

  private isVersionMatch(versionName: string): boolean {
    if (!this.versionName || !versionName) {
      return false;
    }
    return this.versionName.localeCompare(versionName) === 0;
  }

  private isResourceIdValid(id: number): boolean {
    return !Number.isNaN(id) && id > 0;
  }

  public getBundleResourceInfo(bundleName: string, bundleFlags: number, appIndex: number):
    bundleResourceManager.BundleResourceInfo | undefined {
    if (bundleName.length == 0) {
      return undefined;
    }
    let bundleResourceLabelList: bundleResourceManager.BundleResourceInfo | undefined = undefined;
    try {
      bundleResourceLabelList = bundleResourceManager.getBundleResourceInfo(bundleName, bundleFlags, appIndex);
    } catch (error) {
      LogUtil.error(`${TAG} getBundleResourceInfo error, errmsg: ${error?.message}, bundleName: ${bundleName}, appIndex: ${appIndex}`);
    }
    LogUtil.info(`${TAG} getBundleResourceInfo label: ${bundleResourceLabelList?.label},bundleName: ${bundleName}, appIndex: ${appIndex}`);
    return bundleResourceLabelList;
  }

  public async base64ToPixelMap(base64Helper: util.Base64Helper,
    bundleInfo: bundleResourceManager.BundleResourceInfo | undefined): Promise<ResourceStr | PixelMap | undefined> {
    if (!bundleInfo || !bundleInfo.icon) {
      return undefined;
    }
    LogUtil.info(`${TAG} ${bundleInfo.bundleName} pixelMap create`);
    return AppEntry.createPixelMap(base64Helper, bundleInfo.icon, bundleInfo.bundleName);
  }

  public static async createPixelMap(base64Helper: util.Base64Helper,
    icon: string, bundleName: string): Promise<ResourceStr | PixelMap | undefined> {
    if (!icon) {
      return undefined;
    }
    // 解码前先去掉前缀
    const pngPrefix: string = 'data:image/png;base64,';
    const jpegPrefix: string = 'data:image/jpeg;base64,';
    let newStr: string = icon;
    if (newStr.startsWith(pngPrefix)) {
      newStr = newStr.substring(pngPrefix.length);
    } else if (newStr.startsWith(jpegPrefix)) {
      newStr = newStr.substring(jpegPrefix.length);
    }
    let length: number = (newStr.length - (newStr.length / 8) * 2) / 1024;
    LogUtil.info(`${TAG} pixelMap create ${length}`);
    let imageSource: image.ImageSource | undefined = undefined;
    try {
      const data = base64Helper.decodeSync(newStr, util.Type.BASIC);
      let sourceOptions: image.SourceOptions = {
        sourceDensity: 120,
        sourceSize: { width: APP_ICON_STANDARD_WIDTH, height: APP_ICON_STANDARD_HEIGHT }
      };
      imageSource = image.createImageSource(data.buffer, sourceOptions);
      let pixelMap: PixelMap = await imageSource.createPixelMap({
        desiredSize: { width: APP_ICON_STANDARD_WIDTH, height: APP_ICON_STANDARD_HEIGHT }
      });
      const size: Size = pixelMap.getImageInfoSync().size;
      LogUtil.info(`${TAG} pixelMap size is ${size.width} x ${size.height}, bundle is ${bundleName}`);
      return pixelMap;
    } catch (err) {
      LogUtil.error(`${TAG} decode base64 failed message is ${err?.message}`);
    } finally {
      if (imageSource) {
        imageSource.release();
      }
    }
    return icon;
  }

  /**
   * 异步加载应用名
   */
  public async loadLabel(): Promise<boolean | undefined> {
    if (this.label) {
      LogUtil.info(`${TAG} loadLabel return`);
      return;
    }

    if (!this.isResourceIdValid(this.labelId)) {
      LogUtil.info(`${TAG} labelId invalid: ${this.labelId}`);
      this.label = this.name;
      return false;
    }

    let bundleFlags = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_WITH_LABEL;
    let bundleResourceInfo: bundleResourceManager.BundleResourceInfo | undefined =
      this.getBundleResourceInfo(this.name, bundleFlags, this.appIndex);

    this.label = bundleResourceInfo?.label;
    if (!this.label) {
      LogUtil.error(`${TAG} get label invalid`);
      this.icon = AppEntry.getDefaultIcon();
    }

    return true;
  }

  /**
   * 异步加载图标
   */
  public async loadIcon(): Promise<boolean> {
    if (this.icon) {
      LogUtil.info(`${TAG} loadIcon return`);
      return false;
    }
    if (!this.isResourceIdValid(this.iconId)) {
      LogUtil.info(`${TAG} iconId invalid: ${this.iconId}`);
      this.icon = AppEntry.getDefaultIcon();
      return true;
    }

    let bundleFlags = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_WITH_ICON;
    let bundleResourceInfo: bundleResourceManager.BundleResourceInfo | undefined =
      this.getBundleResourceInfo(this.name, bundleFlags, this.appIndex);

    this.icon = bundleResourceInfo?.icon;
    if (!this.icon) {
      LogUtil.error(`${TAG} get icon invalid`);
      this.icon = AppEntry.getDefaultIcon();
    }

    return true;
  }

  public static getDefaultIcon(): Resource {
    return $r('sys.media.ohos_app_icon');
  }

  /**
   * 异步加载存储大小
   */
  public async loadSize(): Promise<boolean> {
    if (this.appInfo?.codePath === '2') {
      return false;
    }
    let storage = await import('@ohos.file.storageStatistics');
    let bundleStats = await (DeviceUtil.isWearable() ? storage.default.getBundleStats(this.name) :
      storage.default.getBundleStats(this.name, this.appIndex));
    if (!bundleStats) {
      LogUtil.warn(`${TAG} get bundleStats invalid`);
      return false;
    }

    let isUpdate: boolean = false;
    let appSize = AppUtils.formatSize(bundleStats.appSize);
    if (appSize !== this.appSize) {
      this.appSize = appSize;
      isUpdate = true;
    }
    let cacheSize = AppUtils.formatSize(bundleStats.cacheSize);
    if (cacheSize !== this.cacheSize) {
      this.cacheSize = cacheSize;
      isUpdate = true;
    }
    let dataSize = AppUtils.formatSize(bundleStats.dataSize);
    if (dataSize !== this.dataSize) {
      this.dataSize = dataSize;
      isUpdate = true;
    }
    let totalSize = AppUtils.formatSize(bundleStats.appSize + bundleStats.cacheSize + bundleStats.dataSize);
    if (totalSize !== this.totalSize) {
      this.totalSize = totalSize;
      isUpdate = true;
    }
    return isUpdate;
  }

  public isRemovable(): boolean {
    return this.removable !== false;
  }

  public isUserDataClearable(): boolean {
    LogUtil.info(`${TAG} ${this.name}`);
    LogUtil.info(`${TAG} isUserDataClearable uid: ${(this.appInfo as BasicAppEntry).uid}`);
    LogUtil.info(`${TAG} isUserDataClearable this.currentActivatedId: ${this.currentActivatedId}`);
    return Math.floor((this.appInfo as BasicAppEntry).uid / UID_MARK) === this.currentActivatedId &&
      this.dataSize !== '0B' && this.userDataClearable !== false;
  }

  public hasCache(): boolean {
    LogUtil.info(`${TAG} ${this.appInfo?.name}`);
    LogUtil.info(`${TAG} hasCache uid: ${this.appInfo?.uid}`);
    LogUtil.info(`${TAG} hasCache this.currentActivatedId: ${this.currentActivatedId}`);
    return Math.floor((this.appInfo as BasicAppEntry).uid / UID_MARK) === this.currentActivatedId && this.cacheSize !== '0B';
  }
}

/**
 * 应用菜单数据类
 *
 * @since 2022-05-26
 */
export class AppMenu extends BaseEntryMenu {
  public appEntry: AppEntry | undefined = undefined;

  /**
   * 应用分身索引
   */
  public appIndex: number = 0;

  constructor(appEntry: AppEntry) {
    super({
      key: appEntry.name,
    });
    this.updateEntry(appEntry);
  }

  public updateEntry(appEntry: AppEntry): void {
    this.appEntry = appEntry;
    this.appType = this.getAppType(appEntry);
    this.appIndex = appEntry.appIndex;
    this.title = appEntry.label as string;
    this.icon = appEntry.icon as ResourceStr | PixelMap;
    this.timestamp = new Date().getTime();
    this.summary = appEntry.totalSize || ' ';
  }

  public getAppType(appEntry: AppEntry): string {
    if (appEntry!.appInfo!.bundleType === 0) {
      return appEntry!.appInfo!.codePath!.includes('/') ?
        (appEntry!.appInfo!.systemApp ? 'systemApp' : 'unSystemApp') : 'containerApp';
    }
    if (appEntry!.appInfo!.bundleType === 1) {
      return 'metaApp';
    }
    return '';
  }
}

/**
 * 应用菜单数据类
 *
 * @since 2022-05-26
 */
export class InputMenu extends BaseEntryMenu {
  public appEntry: AppEntry | undefined = undefined;
  /**
   * 应用分身索引
   */
  public appIndex: number = 0;

  constructor(appEntry: AppEntry) {
    super({
      key: appEntry.name,
    });
    this.updateEntry(appEntry);
  }

  public updateEntry(appEntry: AppEntry): void {
    this.appEntry = appEntry;
    this.appIndex = appEntry.appIndex;
    this.title = appEntry.label as string;
    this.icon = appEntry.icon as ResourceStr | PixelMap;
    this.timestamp = new Date().getTime();
  }
}

export class RecentAppMenu extends AppMenu {
  public updateEntry(appEntry: AppEntry): void {
    this.appEntry = appEntry;
    this.appIndex = appEntry.appIndex;
    this.title = appEntry.label as string;
    this.icon = appEntry.icon as ResourceStr | PixelMap;
    this.timestamp = new Date().getTime();
  }
}