/*
 * 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,
  SingletonHelper,
} from '@ohos/basicutils';
import {
  DeviceHelper,
  EvtBus,
  sSettingsUtil,
  ConfigurationEvent,
  EventManager,
  PluginSlot
} from '@ohos/frameworkwrapper';
import BatteryInfo from '@ohos.batteryInfo';
import charger from '@ohos.charger';
import commonEventManager from '@ohos.commonEventManager';
import { BatterySocEvent } from '@ohos/systemuicommon';
import { ShowBatteryDialogEvent } from '@ohos/systemuicommon/src/main/ets/event/ShowBatteryDialogEvent';
import power from '@ohos.power';
import batteryInfo from '@ohos.batteryInfo';
import Constants from './common/constants';
import { CommonEventData } from 'commonEvent/commonEventData';
import type { CommonEventSubscriber } from 'commonEvent/commonEventSubscriber';
import PhoneNotificationController from '../phone/PhoneNotificationController';
import StyleConfiguration, { BatteryComponentStyle } from './common/StyleConfiguration';
import lazy NotificationController from '../pc/NotificationController';
import { PluginWidthChangeEvent } from '@ohos/systemuicommon/Index';
import { LogWithHa } from '@ohos/systemuicommon';

const TAG = 'BatteryComponent-batteryModel';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.SYS_UI, TAG);
const CHARGING_ENABLE: number = 1;

enum ChargeTypeEnum {
  CHARGE_TYPE_NONE = '0',
  CHARGE_TYPE_WIRED_NORMAL = '1',
  CHARGE_TYPE_WIRED_QUICK = '2',
  CHARGE_TYPE_WIRED_SUPER_QUICK = '3',
  CHARGE_TYPE_WIRELESS_NORMAL = '4',
  CHARGE_TYPE_WIRELESS_QUICK = '5',
  CHARGE_TYPE_WIRELESS_SUPER_QUICK = '6'
}

const BATTERY_CONFIG_SCENENAME = 'charger_type';

const DEFAULT_PROGRESS = 100;
const SUBSCRIBE_INFO: commonEventManager.CommonEventSubscribeInfo = {
  events: [
    commonEventManager.Support.COMMON_EVENT_BATTERY_CHANGED,
    commonEventManager.Support.COMMON_EVENT_CHARGE_TYPE_CHANGED,
    commonEventManager.Support.COMMON_EVENT_POWER_SAVE_MODE_CHANGED
  ]
};

interface ComponentCallback {
  onBatterySoc(isEnable: boolean): void
}

// 发送是否充电切换广播事件
export class BatteryChargingEvent {

  public static readonly eventTypeName = 'BatteryChargingEvent';
  // true充电中 false未充电
  public charging: boolean = false;
}

// 电池电量百分比VM
export class BatterySocVm {
  /**
   * 多事件管理器
   */
  private eventMgr: EventManager = EvtBus.createEventManager();
  private callback?: ComponentCallback;

  init(callback: ComponentCallback): void {
    this.callback = callback;
    this.eventMgr.on(BatterySocEvent, this.onBatterySocEvent.bind(this));
  }

  destroy(): void {
    this.eventMgr.offAll();
    this.callback = undefined;
  }

  private onBatterySocEvent(event: BatterySocEvent): void {
    const isEnable: boolean = event.isBatterySocEnable();
    log.showInfo(`onBatterySocEvent isEnable: ${isEnable} ${typeof isEnable}`);

    if (this.callback) {
      this.callback.onBatterySoc(isEnable);
    }
  }
}

export class BatteryModel {
  private mBatterySoc?: AbstractProperty<number>;
  private mBatteryCapacityLevel?: AbstractProperty<BatteryInfo.BatteryCapacityLevel>;
  private mBatteryCharging?: AbstractProperty<boolean>;
  private mPluggedType?: AbstractProperty<BatteryInfo.BatteryPluggedType>;
  private mChargerType?: AbstractProperty<charger.ChargeType>;
  private mSubscriber?: CommonEventSubscriber | null;
  private mIsReleased: boolean = true;
  private batteryChargingEvent: BatteryChargingEvent = new BatteryChargingEvent;
  private mPowerSaveMode?: AbstractProperty<number>;
  private mShowBatterySoc: boolean = false;
  private style: BatteryComponentStyle = StyleConfiguration.getBatteryComponentStyle();
  private pluginWidthChangeEvent: PluginWidthChangeEvent = PluginWidthChangeEvent.create(PluginSlot.SLOT_STATUS_BATTERY,
    this.style.statusBarBatteryWidth as number);

  /**
   * PC低电通知回调函数
   */
  private checkPcLowBattery?: (batterySoc: number, oldBatterySoc: number,
    oldBatteryCapacityLevel: BatteryInfo.BatteryCapacityLevel) => void;

  private eventMgr: EventManager = EvtBus.createEventManager();
  private language: string | undefined = undefined;
  private initEventMgr(): void {
    this.eventMgr.on(ConfigurationEvent, this.onConfigurationEvent)
      .on(BatterySocEvent, this.onBatterySocEvent);
  }

  private onBatterySocEvent = (event: BatterySocEvent): void => {
    this.mShowBatterySoc = event.isBatterySocEnable();
    this.updatePluginWidthChangeEvent();
  };

  /**
   * 切换语言更新弹窗内容
   */
  private onConfigurationEvent = (event: ConfigurationEvent): void => {
    try {
      log.showInfo(`onConfigurationEvent, config.language: ${event.config?.language}, ${this.language}`);
      if (this.language === event.config?.language) {
        return;
      }
      this.language = event.config?.language;
      const hasNotification = PhoneNotificationController.getInstance().hasNotification();
      if (hasNotification) {
        const batterySoc = BatteryInfo.batterySOC ?? DEFAULT_PROGRESS;
        log.showInfo(`onConfigurationEvent publishNotification batterySoc: ${batterySoc}`);
        PhoneNotificationController.getInstance().publishNotification(batterySoc, true);
      }
    } catch (error) {
      log.error('onConfigurationEvent error', error);
    }
  };

  async initBatteryModel(): Promise<void> {
    if (!this.mIsReleased) {
      log.showInfo('already init');
      return;
    }
    log.showInfo('initBatteryModel start');
    this.mIsReleased = false;
    this.style.initStyle();
    this.mBatterySoc = AppStorage.setAndRef('batterySoc', 0);
    this.mBatteryCapacityLevel =
      AppStorage.setAndRef('batteryCapacityLevel', BatteryInfo.BatteryCapacityLevel.LEVEL_FULL);
    this.mBatteryCharging = AppStorage.setAndRef('batteryCharging', false);
    this.mChargerType = AppStorage.setAndRef('chargerType', charger.ChargeType.NONE);
    this.mPluggedType = AppStorage.setAndRef('batteryPluggedType', 0);
    this.mPowerSaveMode = AppStorage.setAndRef('powerSaveMode', Constants.POWER_SAVE_MODE_OFF);
    // 开关机,切换用户场景下,基于settingsdata数据矫正电池模式参数
    this.mPowerSaveMode?.set(Number.parseInt(sSettingsUtil.getValue(Constants.POWER_MODE_STATUS_KET)));
    this.setBatteryStatus();

    try {
      const subscriber: CommonEventSubscriber = await commonEventManager.createSubscriber(SUBSCRIBE_INFO);
      log.showInfo('init createSubscriber end');
      if (this.mIsReleased) {
        log.showInfo('init createSubscriber but model released');
        return;
      }
      this.mSubscriber = subscriber;
      commonEventManager.subscribe(subscriber, (err, data) => {
        log.showInfo('initBatteryModel start');
        if (err && err.code !== 0) {
          log.showError(`Can't handle common event, err: ${err.message}`);
          return;
        }
        this.updateBatteryStatus(data);
      });
      this.setBatteryStatus();
      log.showInfo('setBatteryStatus in initBatteryModel');
    } catch (error) {
      log.error('commonEventManager error', error);
    }

    this.getBatteryConfig(); // 获取电池配置

    if (!DeviceHelper.isPC()) {
      PhoneNotificationController.getInstance().init();
    }
    this.initEventMgr();
  }

  unInitBatteryModel(): void {
    log.showDebug('unInitBatteryModel');
    this.mIsReleased = true;
    if (this.mSubscriber) {
      try {
        commonEventManager.unsubscribe(this.mSubscriber);
        this.mSubscriber = null;
      } catch (error) {
        log.error('commonEventManager unsubscribe error', error);
      }
    }
    if (!DeviceHelper.isPC()) {
      PhoneNotificationController.getInstance().unInit();
    }
    this.eventMgr.offAll();
  }

  getBatteryConfig(): void {
    let result: string = '';
    try {
      result = BatteryInfo.getBatteryConfig(BATTERY_CONFIG_SCENENAME);
      log.showInfo('batteryInfo.getBatteryConfig ' + result);
      if (!result) {
        return;
      }
    } catch (error) {
      log.error('getBatteryConfig error', error);
      return;
    }
    let chargerType: charger.ChargeType = charger.ChargeType.NONE;
    switch (result) {
      case ChargeTypeEnum.CHARGE_TYPE_NONE:
        chargerType = charger.ChargeType.NONE;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRED_NORMAL:
        chargerType = charger.ChargeType.WIRED_NORMAL;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRED_QUICK:
        chargerType = charger.ChargeType.WIRED_QUICK;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRED_SUPER_QUICK:
        chargerType = charger.ChargeType.WIRED_SUPER_QUICK;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRELESS_NORMAL:
        chargerType = charger.ChargeType.WIRELESS_NORMAL;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRELESS_QUICK:
        chargerType = charger.ChargeType.WIRELESS_QUICK;
        break;
      case ChargeTypeEnum.CHARGE_TYPE_WIRELESS_SUPER_QUICK:
        chargerType = charger.ChargeType.WIRELESS_SUPER_QUICK;
        break;
      default:
        chargerType = charger.ChargeType.NONE;
        break;
    }
    this.mChargerType?.set(chargerType);
  }

  /**
   * 注册PC低电通知回调函数
   *
   * @param checkPcLowBattery PC低电通知回调函数
   */
  registerCheckPcLowBatteryCallBack(checkPcLowBattery: (batterySoc: number, oldBatterySoc: number,
    oldBatteryCapacityLevel: BatteryInfo.BatteryCapacityLevel) => void): void {
    this.checkPcLowBattery = checkPcLowBattery;
  }

  /**
   * 反注册PC低电通知回调函数
   */
  unregisterCheckPcLowBatteryCallBack(): void {
    this.checkPcLowBattery = undefined;
  }

  /**
   * Get battery status and remaining power
   */
  private updateBatteryStatus(data: CommonEventData): void {
    if (data && data.event === commonEventManager.Support.COMMON_EVENT_CHARGE_TYPE_CHANGED) {
      this.mChargerType?.set(data.code);
      log.showInfo(`charger type changed to ${this.mChargerType?.get()}`);
      return;
    }
    if (data && data.event === commonEventManager.Support.COMMON_EVENT_POWER_SAVE_MODE_CHANGED) {
      this.mPowerSaveMode?.set(data.code);
      this.updatePluginWidthChangeEvent();
      return;
    }
    this.setBatteryStatus();
  }

  private setBatteryStatus(): void {
    let oldBatterySoc = this.mBatterySoc?.get() ?? DEFAULT_PROGRESS;
    let batterySoc = BatteryInfo.batterySOC ?? DEFAULT_PROGRESS;
    let batteryCharging = BatteryInfo.chargingStatus;
    let oldBatteryCapacityLevel = this.mBatteryCapacityLevel?.get() ?? BatteryInfo.BatteryCapacityLevel.LEVEL_FULL;
    let batteryCapacityLevel = batteryInfo.batteryCapacityLevel ?? BatteryInfo.BatteryCapacityLevel.LEVEL_FULL;
    if (batterySoc <= 0) {
      // If the result is a negative number, set it as positive number.
      batterySoc = Math.abs(batterySoc) * Constants.PERCENT_NUMBER;
    }
    if (oldBatterySoc !== batterySoc) {
      LogWithHa.warn(log, `batterySoc refresh ${batterySoc}`);
    }
    // Set the battery status as charging when there is no battery hardware
    this.mBatterySoc?.set(batterySoc);
    this.mBatteryCapacityLevel?.set(batteryCapacityLevel);
    this.mPluggedType?.set(BatteryInfo.pluggedType);

    // 优化this.mBatteryCharging.set,减少广播到组件的无效次数
    const chargingStatus = DeviceHelper.isPC() ? this.getPcChargingStatus(batteryCharging)
      : this.getChargingStatus(batteryCharging);
    if (chargingStatus !== this.mBatteryCharging?.get()) {
      this.mBatteryCharging?.set(chargingStatus);
      this.updatePluginWidthChangeEvent();
      this.batteryChargingEvent.charging = chargingStatus;
      EvtBus.post(BatteryChargingEvent, this.batteryChargingEvent);
    }

    // 检测低电量弹框提醒
    this.checkShowOrHideDialog();
    // 检测PC低电量通知&重启后oldBatterySoc获取不到导致默认100%电量
    if (this.checkPcLowBattery && oldBatterySoc !== DEFAULT_PROGRESS &&
      oldBatteryCapacityLevel !== BatteryInfo.BatteryCapacityLevel.LEVEL_FULL) {
      this.checkPcLowBattery(batterySoc, oldBatterySoc, oldBatteryCapacityLevel);
    }
    this.checkPhoneLowBatteryNtf(batterySoc, oldBatterySoc);
  }

  private updatePluginWidthChangeEvent(): void {
    let iconWidth = this.style.statusBarBatteryWidth as number;
    if (this.mShowBatterySoc) {
      if (this.mBatteryCharging?.get()) {
        iconWidth += (this.style.statusBarBatteryChargingWidth as number);
      } else if (this.mPowerSaveMode && this.mPowerSaveMode?.get() === Constants.POWER_SAVE_MODE_ON) {
        iconWidth += (this.style.statusBarPowerSaveWidth as number);
      }
    }
    this.pluginWidthChangeEvent.setWidth(iconWidth);
    if (this.pluginWidthChangeEvent.getChangeDelta() > 0 &&
      this.pluginWidthChangeEvent.getChangeDelta() < this.style.statusBarBatteryWidth) {
      EvtBus.post(PluginWidthChangeEvent, this.pluginWidthChangeEvent);
    }
  }

  private async checkPhoneLowBatteryNtf(batterySoc: number, oldBatterySoc: number): Promise<void> {
    log.showInfo(`checkPhoneLowBatteryNtf: ${batterySoc}, ${oldBatterySoc}, ${this.batteryChargingEvent.charging}`);
    try {
      const powerMode = this.getPowerMode();
      const isCancelNotification = this.batteryChargingEvent.charging || batterySoc > Constants.BATTERY_LEVEL_LOW ||
        power.DevicePowerMode.MODE_POWER_SAVE === powerMode;
      if (isCancelNotification) {
        // 关闭通知
        await PhoneNotificationController.getInstance().cancelNotification();
        return;
      }

      const isBatteryDown: boolean = batterySoc - oldBatterySoc < 0;
      // 电量降至 20% 或 10% 需重新发送通知,电量变化则更新剩余电量显示
      const isRePublishNtf: boolean = isBatteryDown &&
        (batterySoc === Constants.BATTERY_LEVEL_LOW || batterySoc === Constants.BATTERY_LEVEL_VERY_LOW);
      // 低电量通知手动删除后,不再发送通知
      const hasNotification = PhoneNotificationController.getInstance().hasNotification();
      if (hasNotification || isRePublishNtf) {
        log.showInfo(`checkPhoneLowBatteryNtf publishNotification: ${hasNotification}, ${isRePublishNtf}`);
        await PhoneNotificationController.getInstance().publishNotification(batterySoc, !isRePublishNtf);
      }
    } catch (err) {
      log.error(`checkPhoneLowBatteryNtf request failed. ${err?.message}`);
    };
  }

  private getPowerMode(): power.DevicePowerMode {
    let curPowerMode: power.DevicePowerMode = power.DevicePowerMode.MODE_NORMAL;
    try {
      curPowerMode = power.getPowerMode();
    } catch (err) {
      log.showError(`Get device power mode failed , err: ${err.code}, errMessage: ${err.message}`);
    }
    return curPowerMode;
  }

  private getChargingStatus(state: BatteryInfo.BatteryChargeState): boolean {
    let batteryStatus = false;
    switch (state) {
      case BatteryInfo.BatteryChargeState.ENABLE:
      case BatteryInfo.BatteryChargeState.FULL:
        batteryStatus = true;
        break;
      case BatteryInfo.BatteryChargeState.DISABLE:
        batteryStatus = false;
        break;
      default:
        batteryStatus = false;
        break;
    }
    return batteryStatus;
  }

  private getPcChargingStatus(state: BatteryInfo.BatteryChargeState): boolean {
    let batteryStatus = false;
    switch (state) {
      case BatteryInfo.BatteryChargeState.ENABLE:
      case BatteryInfo.BatteryChargeState.FULL:
      case BatteryInfo.BatteryChargeState.DISABLE:
        batteryStatus = true;
        break;
      default:
        batteryStatus = false;
        break;
    }
    return batteryStatus;
  }

  /**
   * 检测低电量关机提醒弹框显示/隐藏
   */
  private checkShowOrHideDialog(): void {
    // 当前电量
    let batterySoc = this.mBatterySoc?.get() ?? DEFAULT_PROGRESS;
    // 当前是否正在充电
    let isCharging = this.batteryChargingEvent.charging ?? false;
    EvtBus.post(ShowBatteryDialogEvent, new ShowBatteryDialogEvent(isCharging, batterySoc));
  }
}

let mBatteryModel = SingletonHelper.getInstance(BatteryModel, TAG);

export default mBatteryModel as BatteryModel;