/*
 * 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 {
  CellularDataVm,
  DeviceInfoVm,
  DropdownVm,
  INotificationPanelVm,
  NotificationBaseVm,
  PanelVmEvent
} from '@ohos/systemuicommon/newIndex';
import { DropDownPanelManager, EventBroadcast, SysUiWindowType } from '@ohos/systemuicommon/Index';
import { AccessibilityVm } from '@ohos/systemuicommon/src/main/ets/vm/AccessibilityVm';
import { LogDomain, LogHelper } from '@ohos/basicutils/src/main/ets/TsIndex';
import {
  DropDownPanelManagerWrapper,
  EventConstants,
  sEventManager,
  TargetChangeState,
  TargetPanel,
  DropDownEvent,
  DeviceHelper,
  ViewType,
} from '@ohos/frameworkwrapper/src/main/ets/TsIndex';
import { NotificationItemAppearAnimation } from '../animation/NotificationItemAppearAnimation';
import { ArkUIAdapter } from '@ohos/systemuicommon/src/main/ets/utils/ArkUIAdapter';
import {
  EventEmitter,
  LiveCardShowAnimationEventUtil, NotificationDataManager,
  InnerEventUtil } from '@ohos/systemuicommon/newTsIndex';
import { uiEffect } from '@kit.ArkGraphics2D';
import { ResUtils } from '@ohos/windowscene';
import {
  NotificationPanelSlideParams,
  NotificationSysEventReporter,
} from '@ohos/systemuicommon/src/main/ets/utils/NotificationSysEventReporter';
import { NtfCenterDropDownEvent } from '@ohos/systemuicommon/src/main/ets/event/NotificationEvent';
import { ViewManagerAdapter } from '@ohos/systemuicommon';

const TAG = 'NotificationPanelVm';
const log = LogHelper.getLogHelper(LogDomain.NC, TAG);

/**
 * item出场动画时延
 */
const APPEAR_DELAY: number = 32;

/**
 * list整体y轴下拉跟手系数(权重1.05 * 跟手系数2/3)
 */
const LIST_FOLLOW_COEFFICIENT = 0.7;

/**
 * 动效开始前,通知item初始的缩放比例。
 */
const INIT_ITEM_SCALE: number = 0.8;

/**
 * 下拉通知中心,不同位置卡片向上位移距离,取固定值,大于6行通知取112。
 */
const UPWARDS_TRANS_Y_ARRAY: number[] = [26, 24, 44, 54, 75, 112];

const NTF_HEADER_TIME_TEXT_ID: string = 'NotificationHeaderView_TimeView_Text_timeText';
/**
 * 模糊区域高度及向上偏移(-40)
 * 横屏布局 47 + 40的向上偏移, 防止双中心切换时透出
 * 非横屏布局 75 + 40的向上偏移, 防止双中心切换时透出
 */
const BLUR_AREA_HEIGHT_LANDSCAPE_LAYOUT = 87;
const BLUR_AREA_HEIGHT = 115;
const BLUR_AREA_TRANSY = -40;
const LANDSCAPE_DIVISOR = 1.6;

@ObservedV2
export class NotificationPanelVm extends NotificationBaseVm implements INotificationPanelVm {
  @Trace public isVisible: boolean = false;
  @Trace public cardWidth: number = 0;
  @Trace public opacity: number = 1;
  @Trace public scale: number = 1;
  /**
   * 通知面板是否显示
   */
  @Trace public isShow: boolean = false;
  /**
   * 面板触摸down点,全局Y轴点,-1无触摸
   */
  @Trace public panelDownWindowY: number = -1;
  /**
   * 是否可响应事件
   */
  @Trace public isResponsive: boolean = true;
  /**
   * 是否需要展示模糊层
   */
  @Trace public isBlurAreaVisible: boolean = false;
  /**
   * 模糊层高度
   */
  @Trace public blurAreaHeight: number = BLUR_AREA_HEIGHT;
  /**
   * 模糊层TransY
   */
  @Trace public blurAreaTransY: number = BLUR_AREA_TRANSY;
  /**
   * 当前下拉进度
   */
  private lastProgress = 0;
  /**
   * 当前下拉面板
   */
  private lastTarget: TargetPanel = TargetPanel.NONE;
  /**
   * 通知列表vm
   */
  protected listVm = this.vmInjector.getListVm()!;
  /**
   * 通知面板头部VM
   */
  protected headerVm = this.vmInjector.getHeaderVm()!;
  /**
   * 更多通知头部VM
   */
  private moreHeaderVm = this.vmInjector.getNotificationMoreHeaderVm()!;
  /**
   * 通知列表滑动VM
   */
  private listScrollerVm = this.vmInjector.getListScrollerVm();
  /**
   * item依次出现动效
   */
  private itemAppearAnimation = new NotificationItemAppearAnimation(this.listScrollerVm, this.moreHeaderVm);
  /**
   * 是否有无效的下拉跟手位移。(列表滚动条滑动过程中,又触发下拉跟手)
   */
  private hasInvalidMoveY = false;
  /**
   * 面板触摸down点,全局X轴点,-1无触摸
   */
  private panelDownWindowX: number = -1;
  /**
   * 是否允许滑动切换双中心
   */
  private allowPanHorizontal: boolean = true;
  /**
   * 面板广播事件
   */
  public emitter: EventEmitter<PanelVmEvent> = new EventEmitter<PanelVmEvent>();

  /**
   * 出场动效是否已执行
   */
  private isAppearAnimExecuted: boolean = false;

  /**
   * 通知中心是否需要横屏布局
   */
  @Computed
  get isLandscapeLayout(): boolean {
    if (DeviceInfoVm.instance.isLandscape) {
      // 大大屏幕机,展开或半展开
      if (DeviceHelper.isFold()) {
        return !DeviceInfoVm.instance.isExpand;
      }
      if (DeviceHelper.isPad()) {
        return false;
      }
      // 直板机跟随屏方向
      return true;
    } else {
      return false;
    }
  }

  @Trace public layoutOptions: FlexOptions = {
    direction: FlexDirection.Column,
    alignItems: ItemAlign.Center,
  }

  @Computed
  get portraitHorizontalDropPanelPosition(): number {
    const deviceWidth = ArkUIAdapter.px2vp(DeviceInfoVm.instance.width);
    if (this.listVm?.width.endsWith('vp')) {
      return (parseInt(this.listVm.width) + deviceWidth) / 2;
    }
    return deviceWidth;
  }

  @Computed
  get landscapeHorizontalDropPanelPosition(): number {
    let horizontalDropPanelPosition = 0;
    const deviceWidth = ArkUIAdapter.px2vp(DeviceInfoVm.instance.width);
    if (this.listVm?.width.endsWith('vp')) {
      horizontalDropPanelPosition += parseInt(this.listVm.width);
    } else {
      horizontalDropPanelPosition += deviceWidth / LANDSCAPE_DIVISOR;
    }
    // 加上时间组件宽度
    horizontalDropPanelPosition += parseInt(this.headerVm?.width ?? '0');
    if (this.vmInjector.getClearButtonVm()?.isShow) {
      // 加上删除按钮的宽度
      horizontalDropPanelPosition += (deviceWidth - horizontalDropPanelPosition +
      ResUtils.getNumber($r('app.float.ntf_clear_button_icon_size'))) / 2;
    }
    return horizontalDropPanelPosition;
  }

  @Computed
  get horizontalDropPanelPosition(): number {
    if (this.isLandscapeLayout) {
      return this.landscapeHorizontalDropPanelPosition
    }
    return this.getDefaultHorizontalDropPanelPosition();
  }

  protected getAccessibilityCompId(): string {
    // 下拉通知中心聚焦时间组件
    return this.vmInjector.getId(NTF_HEADER_TIME_TEXT_ID);
  }

  protected getDefaultHorizontalDropPanelPosition(): number {
    return this.portraitHorizontalDropPanelPosition;
  }

  public onDropdownChange(dropDownEvent: DropDownEvent): void {
    if (!this.isResponsive && dropDownEvent.moveY <= 0) {
      this.isResponsive = true;
    }
    // 面板收起时,滚动条滑动至顶部
    if (this.lastTarget !== dropDownEvent.target && dropDownEvent.target !== TargetPanel.NOTIFICATION_PANEL) {
      this.listScrollerVm?.scrollToTop();
    }
    // 通知中心面板收起时打点
    this.reportPanelHide(dropDownEvent);

    // 处理松手回弹
    if (dropDownEvent.isEndAnimRunning) {
      this.hasInvalidMoveY = false;
      this.isResponsive = true;
      NotificationDataManager.instance.unfreeze(NotificationDataManager.REASON.DROPDOWN_MOVE_DOWN)
      // 回弹动效
      this.listReboundAnimation(dropDownEvent);
      log.showInfo('onDropdownChange isEndAnimRunning');
      return;
    }

    // 刷新数据
    this.listVm?.flushCacheMap(dropDownEvent);

    // item依次出场动效
    if (this.isItemAppear(dropDownEvent)) {
      this.itemAppearAnimate();
    }

    if (dropDownEvent.progress < 1) {
      // 重置出场动效执行标记
      this.isAppearAnimExecuted = false;
    }

    // 判断是否需要处理下拉跟手参数
    if (!DropdownVm.instance.isNeedUpdateMoveY || this.hasInvalidMoveY) {
      // 已经设置了不更新下拉跟手参数时,仍然触发了下拉位移,则认为该次位移无效。
      log.showInfo(`onDropdownChange isNeedUpdateMoveY: ${DropdownVm.instance.isNeedUpdateMoveY} \
 hasInvalidMoveY: ${this.hasInvalidMoveY} moveY: ${dropDownEvent.moveY}`);
      if (dropDownEvent.moveY > 0) {
        this.hasInvalidMoveY = true;
      }
      return;
    }

    // 通知面板下拉跟手动效
    if (dropDownEvent.moveY > 0 && this.lastTarget === TargetPanel.NOTIFICATION_PANEL) {
      this.isResponsive = false;
      NotificationDataManager.instance.freeze(NotificationDataManager.REASON.DROPDOWN_MOVE_DOWN, 2000)
      this.followAnimate(dropDownEvent);
    }

    this.lastProgress = dropDownEvent.progress;
    this.lastTarget = dropDownEvent.target;
  }

  /**
   * 面板显示隐藏事件
   */
  public onPanelVisibleChange = (isVisible: boolean, currentRatio: number): void => {
    log.showInfo(`On visible change: ${this.isVisible} -> ${isVisible}  ${currentRatio}`);
    if (isVisible && !this.isVisible && currentRatio >= 0.9) {
      // 广播通知中心显示
      EventBroadcast.broadcastEvent(SysUiWindowType.WINDWO_NTF_CENTER, true);
      // 下拉通知中心打断铃声和振动
      InnerEventUtil.post(NtfCenterDropDownEvent, new NtfCenterDropDownEvent());
      this.isShow = true;
      this.isVisible = isVisible;
      log.showInfo(`${SysUiWindowType.WINDWO_NTF_CENTER} show`);
      LiveCardShowAnimationEventUtil.setLiveCardAnimation();
      // 重置响应,避免控制中心下拉时,通知中心存在下拉跟手但没有松手导致的无法响应
      this.isResponsive = true;
      if (DropdownVm.instance.dropdownEvent.target === TargetPanel.CONTROL_CENTER_PANEL) {
        this.headerVm.showTitleText();
      }
    } else if (!isVisible && this.isVisible) {
      if (currentRatio <= 0.1) {
        EventBroadcast.broadcastEvent(SysUiWindowType.WINDWO_NTF_CENTER, false);
        this.isShow = false;
        this.isBlurAreaVisible = false;
        log.showInfo(`${SysUiWindowType.WINDWO_NTF_CENTER} hide`);
        // 重置响应,避免控制中心下拉时,通知中心存在下拉跟手但没有松手导致的无法响应
        this.isResponsive = true;
        // 面板收起时,重置下拉跟手位移。
        this.resetFollowAnimate();
        // 面板收起时,清理出场动效,避免内存残留。
        this.itemAppearAnimation.clearAnimations();
        this.isVisible = isVisible;
        this.hideDropdownPanle();
        log.showInfo('ExitNC end');
      }
    }
  };

  /**
   * item卡片依次出场动效
   */
  private itemAppearAnimate(): void {
    if (!this.listVm) {
      return;
    }

    if (this.isAppearAnimExecuted) {
      return;
    }
    this.isAppearAnimExecuted = true;
    log.showWarn('do itemAppearAnimate!');

    const startIndex = this.listScrollerVm?.startIndex ?? 0;
    const endIndex = this.listScrollerVm?.endIndex ?? 0;

    // 添加动效参数前执行清理
    this.itemAppearAnimation.clearAnimations();
    for (let i = startIndex; i <= endIndex; i++) {
      const ntf = this.listVm.getListItemByIndex(i, true);
      if (!ntf) {
        this.addMoreHeaderAnimation(i, startIndex, endIndex);
        continue;
      }

      const index = i > this.listVm.mainNtfList.length ? i - 1 : i;
      // 设置初始的位移、缩放比例和透明度
      this.vmInjector.getCardVm(ntf).isInAppearAnim = true;
      if (i !== endIndex) {
        this.vmInjector.getCardVm(ntf).itemAppearReset();
      }
      this.vmInjector.getCardVm(ntf).itemAppearAnimation(INIT_ITEM_SCALE, 0, UPWARDS_TRANS_Y_ARRAY[Math.min(index, 5)]);
      // 添加依次出现动效
      this.itemAppearAnimation.addListItemAnimation({
        // 需要传入实际index,用于进行最后一条通知的堆叠处理
        index: i,
        firstIndex: startIndex,
        lastIndex: endIndex,
        itemId: this.listVm.getItemId(ntf),
        cardVm: this.vmInjector.getCardVm(ntf)
      });
    }

    if (this.listScrollerVm) {
      this.listScrollerVm.startItemTranslateY = 0;
    }

    // 下拉面板场景,延迟32ms,与其他动效错峰执行。
    setTimeout(() => {
      this.itemAppearAnimation.startAnimation().then(() => {
        // 下面的日志不要修改,有测试用例关联
        log.showInfo('ClearNtAni/IntoNC All notification animation finish, mark renderGroup false.');
        this.vmInjector.getListScrollerVm()?.transform();
      });
    }, APPEAR_DELAY);
  }

  /**
   * 增加更多通知标题下拉动效
   */
  private addMoreHeaderAnimation(index: number, startIndex: number, endIndex: number): void {
    if (this.listVm.isMoreHeaderIndex(index)) {
      // 设置初始的缩放比例和透明度
      const firstMoreNtf = this.listVm.getListItemByIndex(index + 1, true);
      this.moreHeaderVm.moreHeaderAppearAnimation(INIT_ITEM_SCALE, 0, UPWARDS_TRANS_Y_ARRAY[Math.min(index, 5)])
      let scale = 1;
      let opacity = 1;
      let transY = 0;
      if (index === endIndex - 1 && firstMoreNtf) {
        // 第一条更多通知下拉后是堆叠态,
        const firstMoreCardVm = this.vmInjector.getCardVm(firstMoreNtf);
        scale = firstMoreCardVm.scale;
        opacity = firstMoreCardVm.opacity;
        transY = this.listScrollerVm?.getEndItemTranslateY() ?? firstMoreCardVm.translateY;
      }
      // 添加出现动效
      this.itemAppearAnimation.addMoreHeaderAnimation({
        index: index,
        firstIndex: startIndex,
        lastIndex: endIndex,
        itemId: firstMoreNtf ? this.listVm.getItemId(firstMoreNtf) : '',
        scale: scale,
        opacity: opacity,
        transY: transY,
      });
    }
  }

  /**
   * list以及item卡片下拉跟手
   */
  protected portraitFollowAnimate(dropDownEvent: DropDownEvent): void {
    if (DropdownVm.instance.dropdownEvent.target === TargetPanel.NOTIFICATION_PANEL) {
      this.updateListTransY(dropDownEvent.moveY);
      this.updateHeaderTransY();
    }
  }

  private landscapeFollowAnimate(dropDownEvent: DropDownEvent): void {
    if (DropdownVm.instance.dropdownEvent.target === TargetPanel.NOTIFICATION_PANEL) {
      this.updateListTransY(dropDownEvent.moveY);
    }
  }

  protected followAnimate(dropDownEvent: DropDownEvent): void {
    if (this.isLandscapeLayout) {
      this.landscapeFollowAnimate(dropDownEvent);
    } else {
      this.portraitFollowAnimate(dropDownEvent);
    }
  }

  /**
   * 重置跟手位移
   */
  private resetFollowAnimate() {
    this.updateListTransY(0);
    this.updateHeaderTransY();
    this.headerVm.resetFollowHandAnim();
  }

  protected updateListTransY(moveY: number) {
    if (!this.listVm) {
      return;
    }
    // 更新List列表整体位移
    this.listVm.reboundAnim = DropDownPanelManager.getEndAnim();
    this.listVm.translateY = moveY * LIST_FOLLOW_COEFFICIENT;
  }

  protected updateHeaderTransY(): void {
    // 更新面板顶部时间/日历header位移
    this.headerVm.followUpdateTransY(this.listVm.translateY);
  }

  /**
   * 是否是面板显示场景触发item依次出现
   * @returns
   */
  private isItemAppear(dropDownEvent: DropDownEvent) {
    return dropDownEvent.target === TargetPanel.NOTIFICATION_PANEL &&
      dropDownEvent.targetChanged === TargetChangeState.NONE &&
      dropDownEvent.progress === 1 && !DropdownVm.instance.isFromCCToNC;
  }

  /**
   * 松手回弹动效
   */
  private listReboundAnimation(dropDownEvent: DropDownEvent) {
    if (!this.listVm) {
      return;
    }
    // 避免重复触发
    dropDownEvent.isEndAnimRunning = false;

    // 设置list回弹动效
    this.listVm.reboundAnim = DropDownPanelManager.getEndAnim();
    dropDownEvent.moveY = 0;
    DropDownPanelManagerWrapper.getInstance().dropDownEventProxy.updateSilent(dropDownEvent);
    this.listVm.translateY = 0;
    // 顶部header回弹
    this.headerVm?.followUpdateTransY(0);
  }

  /**
   * 下拉通知中心播报无障碍语音
   */
  @Monitor('isVisible')
  async onPanelVisibleChanged(): Promise<void> {
    if (this.isVisible) {
      await AccessibilityVm.instance.sendEventByResource($r('app.string.ntf_center_title'));
      AccessibilityVm.instance.requestFocusAccessibility(this.getAccessibilityCompId(), true);
      this.refreshDataPlanInfo();
    } else {
      this.resetClearAllStatus();
      this.resetDataPlanInfo();
    }
  }

  private refreshDataPlanInfo(): void {
    if (CellularDataVm.instance.isShowData) {
      log.showInfo(`refreshDataPlanInfo`);
      CellularDataVm.instance.refreshDataPlanInfo();
    }
  }

  private resetDataPlanInfo(): void {
    this.vmInjector.getDataPlanVm()?.reset();
  }

  /**
   * 通知面板触摸事件
   */
  public onTouchEvent = (event: TouchEvent): void => {
    if (event) {
      if (event.type === TouchType.Down) {
        this.panelDownWindowY = event.touches[0].windowY;
        this.panelDownWindowX = event.touches[0].windowX;
        return;
      }
      if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
        this.panelDownWindowY = -1;
        this.panelDownWindowX = -1;
        return;
      }
    }
  }

  private resetClearAllStatus(): void {
    const clearButtonVm = this.vmInjector.getClearButtonVm();
    if (clearButtonVm) {
      clearButtonVm.isClearingAll = false
    }
  }

  /**
   * 监听状态栏触摸事件
   */
  public listenEvent = (): void => {
    sEventManager.subscribe(EventConstants.STATUS_BAR_DROPDOWN_TOUCH, (event: TouchEvent) => {
      this.onTouchEvent(event);
    });
  }
  /**
   * 横滑开始事件
   * @param event 手势
   */
  public onPanHorizontalStart = (event: GestureEvent): void => {
    this.allowPanHorizontal = this.listVm.isAllowPanHorizontal(this.panelDownWindowY, this.panelDownWindowX);
    if (this.allowPanHorizontal) {
      DropdownVm.instance.onPanHorizontalStart(event);
    }
  }
  /**
   * 横滑事件
   * @param event 手势
   */
  public onPanHorizontalMove = (event: GestureEvent): void => {
    if (this.allowPanHorizontal) {
      DropdownVm.instance.onPanHorizontalUpdate(event);
    }
  }
  /**
   * 横滑结束事件
   * @param event 手势
   */
  public onPanHorizontalEnd = (event: GestureEvent): void => {
    if (this.allowPanHorizontal) {
      DropdownVm.instance.onPanHorizontalEnd(event, true);
    }
    this.allowPanHorizontal = true;
  }
  /**
   * 横滑取消事件
   */
  public onPanHorizontalCancel = (): void => {
    if (this.allowPanHorizontal) {
      DropdownVm.instance.touchEndCheckAnim();
    }
    this.allowPanHorizontal = true;
  }
  /**
   * 设置渐变模糊合成滤镜效果
   *
   * @returns filter | undefined 挂载了模糊效果的Filter实例,异常时返回undefined
   */
  public getBlurCompositingFilter(): Filter | undefined {
    try {
      const filter = uiEffect.createFilter();
      return filter.radiusGradientBlur(40, {
        fractionStops: [[1, 0], [0, 1]],
        direction: GradientDirection.Bottom
      });
    } catch (e) {
      log.showError(`get HeadBlurArea radiusGradientBlur failed, err: ${e}`);
      return undefined;
    }
  }

  @Monitor('isLandscapeLayout')
  public onIsLandscapeLayoutChange(): void {
    if (this.isLandscapeLayout) {
      this.layoutOptions = {
        direction: FlexDirection.Row,
        alignItems: ItemAlign.Center,
      }
      this.blurAreaHeight = BLUR_AREA_HEIGHT_LANDSCAPE_LAYOUT;
    } else {
      this.layoutOptions = {
        direction: FlexDirection.Column,
        alignItems: ItemAlign.Center,
      }
      this.blurAreaHeight = BLUR_AREA_HEIGHT;
    }
    this.emitter.emit('isLandscapeLayout', this.isLandscapeLayout);
    log.showInfo(`isLandscapeLayout changed ${this.isLandscapeLayout}`);
  }

  private reportPanelHide(dropDownEvent: DropDownEvent): void {
    if (dropDownEvent.target !== TargetPanel.NONE || !this.listScrollerVm) {
      return;
    }
    if (this.lastTarget === TargetPanel.NOTIFICATION_PANEL) {
      const dropDownCount = Math.floor(this.listScrollerVm.maxDropDownDis / this.calculateNtfListHeight());
      const params: NotificationPanelSlideParams = {
        TIME_STAMP: `${DropDownPanelManager.getLastDropdownTime()}`,
        SLIDE_DISTANCE: dropDownCount < 3 ? dropDownCount : 3
      }
      NotificationSysEventReporter.notificationPanelSlide(params);
    }
    if (this.lastTarget !== TargetPanel.NONE) {
      this.listScrollerVm.maxDropDownDis = 0;
    }
  }

  private calculateNtfListHeight(): number {
    const phoneNtfCenterTitleHeight =
      ResUtils.getNumberFromLength(this.isLandscapeLayout ? $r('app.float.ntf_center_header_phone_land_height') :
      $r('app.float.ntf_center_header_phone_height'));
    const statusBarHeight = ResUtils.getNumberFromLength($r('app.float.status_bar_setting_status_bar_height'));
    return ArkUIAdapter.px2vp(DeviceInfoVm.instance.height) - phoneNtfCenterTitleHeight - statusBarHeight;
  }

  private async hideDropdownPanle(): Promise<void> {
    // 如果收起通知中心面板但面板窗口未隐藏则隐藏窗口
    if (DropdownVm.instance.dropdownEvent.target === TargetPanel.NONE &&
      await ViewManagerAdapter.isViewShowing(ViewType.DROPDOWN)) {
      ViewManagerAdapter.hideView(ViewType.DROPDOWN);
      log.showInfo('Dropdown panel is not hided, hide the dropdown panel.');
    }
  }
}