/*
 * 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 fs from '@ohos.file.fs';
import { notificationManager } from '@kit.NotificationKit';
import { commonEventManager, configPolicy } from '@kit.BasicServicesKit';
import bundleManager from '@ohos.bundle.bundleManager';
import { JSON } from '@kit.ArkTS';
import { EventBus } from '@ohos/settings.common/src/main/ets/framework/common/EventBus';
import { DynamicDataSource } from '@ohos/settings.common/src/main/ets/framework/model/DynamicDataSource';
import {
  CompCtrlParam,
  ComponentControl,
} from '@ohos/settings.common/src/main/ets/framework/model/SettingBaseModel';
import { LogUtil } from '@ohos/settings.common/src/main/ets/utils/LogUtil';
import { PageRouter } from '@ohos/settings.common/src/main/ets/framework/common/PageRouter';
import { NavEntryKey } from '@ohos/settings.common/src/main/ets/utils/Consts';
import { DeviceUtil } from '@ohos/settings.common/src/main/ets/utils/BaseUtils';
import { PushParam } from '@ohos/settings.common/src/main/ets/core/controller/Controller';
import { ResourceUtil } from '@ohos/settings.common/src/main/ets/utils/ResourceUtil';
import {
  ItemDetailType,
  ItemResultType,
  ItemType, SettingItemModel } from '@ohos/settings.common/src/main/ets/framework/model/SettingItemModel';
import {
  notifyCompStateChange,
  SettingBaseState,
  SettingCompState,
  SettingContentState,
  SettingStateType
} from '@ohos/settings.common/src/main/ets/framework/model/SettingStateModel';
import {
  INotificationConfig,
  NotifyDataModel,
  SettingAppGroupModel,
  SettingAppItemModel
} from '../model/SettingAppItemModel';
import { AppLiveViewController } from './AppLiveViewController';
import { AppEntry } from '../AppModel'
import { AppManager } from '../util/AppManager';
import { AppListLoader } from '../AppListLoader';

const TAG = 'AppAllowAccessController: ';

export class AppAllowAccessController implements ComponentControl {
  private compId: string = '';
  private dataSource: DynamicDataSource = new DynamicDataSource();
  private entry: AppEntry | null = null;
  private bundleName: string = '';
  private isLiveViewSwitchEnabled: boolean = false;
  private isNotificationEnabled: boolean = true;
  private commonEventSubscriber?: commonEventManager.CommonEventSubscriber;
  private isAppEnable: boolean = true;
  private appPrivileges?: Record<string, string>;

  private appEnableSwitchEventCb = (data: object) => {
    let result = Boolean(data).valueOf() ?? false;
    LogUtil.info(`${TAG} appEnableSwitch result is ${result}`);
    this.isAppEnable = result;
    this.refreshHeadState();
    this.refreshGroupState();
    this.updateHeaderTitle();
  };

  init(compParam: CompCtrlParam): void {
    LogUtil.showInfo(TAG, 'onInit');
    if (!compParam || !compParam.compId) {
      LogUtil.error(`${TAG} init fail, compParam is invalid`);
      return;
    }
    this.compId = compParam.compId;
    this.dataSource = compParam.dataSource as DynamicDataSource;
    let appItem: SettingAppGroupModel = compParam.component as SettingAppGroupModel;
    this.entry = appItem.entry as AppEntry;
    if (this.entry) {
      LogUtil.info(`${TAG} init app action entry: ${this.entry.name}, ${this.entry.appIndex}`);
      this.bundleName = this.entry.name;
      this.initAllowAccess();
    }
    this.subscribeSlotChangeEvent();
    EventBus.getInstance().on('EVENT_ID_APP_ENABLE_SWITCH_EVENTS', this.appEnableSwitchEventCb);
  }

  destroy(): void {
    LogUtil.showInfo(TAG, 'onDestroy');
    EventBus.getInstance().detach('EVENT_ID_APP_ENABLE_SWITCH_EVENTS', this.appEnableSwitchEventCb);
  }

  private async subscribeSlotChangeEvent(): Promise<void> {
    try {
      this.commonEventSubscriber = await commonEventManager.createSubscriber({
        events: ['usual.event.SLOT_CHANGE']
      });
      commonEventManager.subscribe(this.commonEventSubscriber, async (err, event) => {
        /* instrument ignore if*/
        if (err) {
          LogUtil.error(`${TAG} Receive usual.event.SLOT_CHANGE error: ${err?.code} ${err?.message}`);
          return;
        }
        /* instrument ignore if*/
        if (event.bundleName) {
          // 收到实况通知广播时,若实况开关未显示,则需要刷新界面显示开关,如果已显示,则刷新开关状态
          let status = await notificationManager.isNotificationSlotEnabled(
            { bundle: event.bundleName, uid: event?.parameters?.uid },
            notificationManager.SlotType.LIVE_VIEW
          );
          if (this.isLiveViewSwitchEnabled) {
            EventBus.getInstance().emit('EVENT_ID_APP_LIVE_SWITCH_CHANGE', status);
          } else {
            this.isLiveViewSwitchEnabled = status;
            this.initAllowAccess();
          }
        }
      });
      LogUtil.info(`${TAG} Subscribe usual.event.SLOT_CHANGE success`);
    } catch (e) {
      LogUtil.error(`${TAG} Subscribe usual.event.SLOT_CHANGE error: ${e?.message}`);
    }
  }

  private async initAllowAccess(): Promise<void> {
    if (!this.entry) {
      LogUtil.error(`${TAG} entry invalid`);
      return;
    }
    LogUtil.info(`${TAG} entry allow access: ${this.entry?.name}, ${this.entry?.appIndex}, ${this.entry?.appInfo?.uid}`);
    let bundleType: number = this.entry.appInfo?.bundleType as number;
    this.isNotificationEnabled = await this.isShowNtfSwitch(this.entry?.name as string);
    if (bundleType === bundleManager.BundleType.APP) {
      this.initAppAllowAccess();
    } else if (bundleType === bundleManager.BundleType.ATOMIC_SERVICE) {
      this.initServiceAllowAccess();
    }
    this.isAppEnable = await AppManager.getInstance()
      .getIsApplicationEnabled(this.entry?.name as string, this.entry?.appIndex ?? 0,
        AppListLoader.getInstance().getAppType(this.entry));
    this.refreshHeadState();
    this.refreshGroupState();
  }

  /**
   * 是否显示通知开关配置,一些应用可以通过CCM配置隐藏通知开关配置,即应用管理页面
   * @returns
   */
  private async isShowNtfSwitch(bundleName: string): Promise<boolean> {
    await this.initAppPrivileges();
    if (this.appPrivileges?.[bundleName] && this.appPrivileges[bundleName][3] === '1') {
      return false;
    }
    return true;
  }

  private async initAppPrivileges(): Promise<void> {
    if (!this.appPrivileges) {
      try {
        const notificationConfigPaths = await configPolicy.getCfgFiles('etc/notification/notification_config.json');
        if (notificationConfigPaths.length === 0) {
          LogUtil.showWarn(TAG, 'not get any valid config path');
          return;
        }
        let configs: INotificationConfig[] = [];
        for (let path of notificationConfigPaths) {
          const notificationConfig = JSON.parse(await fs.readText(path)) as INotificationConfig;
          configs.push(notificationConfig);
        }
        let mergeRes = this.mergeConfigs(configs);
        this.appPrivileges = mergeRes.appPrivileges;
        LogUtil.showInfo(TAG, 'appPrivileges init end');
      } catch (e) {
        LogUtil.error(`${TAG}Read notification_config.json failed: ${e?.message}`);
      }
    }
  }

  private mergeConfigs(configs: INotificationConfig[]): INotificationConfig {
    let mergeRes = configs[0]?.appPrivileges ?? {};
    for (let i = 1; i <= configs.length; i++) {
      const privilege = configs[i]?.appPrivileges ?? {};
      Object.keys(privilege).forEach((key) => {
        mergeRes[key] = privilege[key];
      });
    }
    return { appPrivileges: mergeRes };
  }

  private async initAppLiveViewSwitch(bundleName: string, uid: number): Promise<void> {
    if (DeviceUtil.isDevicePc()) {
      return;
    }
    let bundle: notificationManager.BundleOption = {
      bundle: bundleName,
      uid: uid
    };
    try {
      await this.initAppPrivileges();
      if (this.appPrivileges?.[bundleName] && this.appPrivileges?.[bundleName][0] === '1') {
        this.isLiveViewSwitchEnabled = false;
        return;
      }
      // 若历史发过实况通知,则需要显示实况菜单(有历史结论则接口能正常调用)
      await notificationManager.isNotificationSlotEnabled(bundle, notificationManager.SlotType.LIVE_VIEW);
      this.isLiveViewSwitchEnabled = true;
      LogUtil.info(`${TAG} initAppLiveViewSwitch: ${this.isLiveViewSwitchEnabled}`);
    } catch (err) {
      LogUtil.error(`${TAG} Get ${bundleName} live view switch fail: ${err?.message}`);
    }
  }

  private async initAppAllowAccess(): Promise<void> {
    this.dataSource?.splice(0, this.dataSource.length);
    if (this.isNotificationEnabled) {
      this.dataSource.push(this.createAppNotificationItem());
    }
    await this.initAppLiveViewSwitch(this.entry?.name as string, this.entry?.appInfo?.uid as number);
    LogUtil.info(`${TAG} initAppAllowAccess: ${this.isLiveViewSwitchEnabled}`);
    if (this.isLiveViewSwitchEnabled) {
      this.dataSource.push(this.createLiveViewItem());
    }
  }

  /* instrument ignore next */
  private initServiceAllowAccess(): void {
    this.dataSource?.splice(0, this.dataSource.length);
    if (this.isNotificationEnabled) {
      this.dataSource.push(this.createAppNotificationItem());
    }
  }

  private notificationMenuClick(): void {
    LogUtil.info(`${TAG} entry application_networking_entry: ${this.entry?.name}, ${this.entry?.appIndex}`);
    PageRouter.push(NavEntryKey.NOTIFICATION_SETTINGS_DETAIL_ENTRY, new PushParam({
      config: {
        bundleName: this.entry?.name,
        appIndex: this.entry?.appIndex,
      }
    } as NotifyDataModel, undefined, this.bundleName));
  }

  private getNotificationHeader(label: string): string {
    return ResourceUtil.getFormatStringSync($r('app.string.app_info_allow_access'), label as string);
  }

  private updateHeaderTitle() {
    notifyCompStateChange(`${this.compId}.header`,
      new Map<SettingStateType, SettingBaseState>([[SettingStateType.STATE_TYPE_GROUP_HEADER_TITLE,
        { content: this.getNotificationHeader(this.entry?.label as string) } as SettingContentState]]
      ));
  }

  // head根据通知显示隐藏
  private async refreshHeadState(): Promise<void> {
    notifyCompStateChange(`${this.compId}.header`,
      new Map<SettingStateType, SettingBaseState>([[SettingStateType.STATE_TYPE_GROUP_HEADER_VISIBLE,
        { state: this.isNotificationEnabled && this.isAppEnable } as SettingCompState]]));
  }

  // Group根据通知显示隐藏
  private async refreshGroupState(): Promise<void> {
    notifyCompStateChange(this.compId, new Map<SettingStateType, SettingBaseState>(
      [[SettingStateType.STATE_TYPE_GROUP_VISIBLE, { state: this.isAppEnable } as SettingCompState]]
    ));
  }

  private createAppNotificationItem(): SettingItemModel {
    return {
      id: 'Notification',
      type: ItemType.ITEM_TYPE_STANDARD,
      title: { content: $r('app.string.app_info_inform') },
      detail: {
        type: ItemDetailType.DETAIL_TYPE_CUSTOM,
        icon: $r('sys.symbol.chevron_right'),
        isSymbolIcon: true,
        onItemClick: () => {
          this.notificationMenuClick();
        }
      }
    }
  }

  private createLiveViewItem(): SettingAppItemModel {
    return {
      id: 'AppLiveView',
      type: ItemType.ITEM_TYPE_STANDARD,
      title: { content: $r('app.string.app_info_live_view') },
      result: {
        type: ItemResultType.RESULT_TYPE_TOGGLE,
        result: {
          state: true
        }
      },
      compControl: new AppLiveViewController(),
      layout: { noNeedDivider: true },
      entry: this.entry
    } as SettingAppItemModel
  }
}