/*
 * Copyright (c) 2026-2026 Huawei Device Co., Ltd.
 * 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 curves from '@ohos.curves';
import { common, EnvironmentCallback } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { LengthMetrics, SymbolGlyphModifier } from '@kit.ArkUI';
import { i18n } from '@kit.LocalizationKit';

const START_TIME = 250;
const END_TIME = 200;
const BORDER_RADIUS = 12;
const ZINDEX_NUM = 9;
const SYMBOL_SIZE: number = 24;
const MAX_SYMBOL_FONT_SCALE: number = 2;
const MIN_SYMBOL_FONT_SCALE: number = 1;
const DEFAULT_SYMBOL_FONT_SCALE: number = 1;

/**
 * Control margin status of ExceptionPromptV2.
 */
export enum MarginTypeV2 {
  DEFAULT_MARGIN = 0,
  FIT_MARGIN = 1,
}

/**
 * Configuration information interface for PromptOptionsV2.
 * Used to construct PromptOptionsV2 object.
 */
export interface PromptOptionsV2Config {
  marginType: MarginTypeV2;
  marginTop: Dimension;
  icon?: ResourceStr;
  symbolStyle?: SymbolGlyphModifier;
  tip?: ResourceStr;
  actionText?: ResourceStr;
  isShown?: boolean;
}

/**
 * Declare the callback function type to be called when clicking the text on the left.
 */
export type OnTipClickCallback = () => void;

/**
 * Declare the callback function type to be called when clicking the icon button.
 */
export type OnActionTextClickCallback = () => void;

/**
 * Configuration parameter of ExceptionPromptV2.
 * Use @ObservedV2 and @Trace to support deep observation and dynamic refresh of properties.
 */
@ObservedV2
export class PromptOptionsV2 {
  @Trace public icon?: ResourceStr;
  @Trace public symbolStyle?: SymbolGlyphModifier;
  @Trace public tip?: ResourceStr;
  @Trace public marginType: MarginTypeV2;
  @Trace public actionText?: ResourceStr;
  @Trace public marginTop: Dimension;
  @Trace public isShown?: boolean;

  constructor(config?: PromptOptionsV2Config) {
    if (config) {
      this.marginType = config.marginType;
      this.marginTop = config.marginTop;
      if (config.icon !== undefined) {
        this.icon = config.icon;
      }
      if (config.symbolStyle !== undefined) {
        this.symbolStyle = config.symbolStyle;
      }
      if (config.tip !== undefined) {
        this.tip = config.tip;
      }
      if (config.actionText !== undefined) {
        this.actionText = config.actionText;
      }
      if (config.isShown !== undefined) {
        this.isShown = config.isShown;
      }
    } else {
      this.marginType = MarginTypeV2.DEFAULT_MARGIN;
      this.marginTop = 0;
    }
  }
}

/**
 * Declare struct ExceptionPromptV2 higher-order component.
 * The exception prompt component is used to show an error message when an error arises.
 */
@ComponentV2
export struct ExceptionPromptV2 {
  @Param options: PromptOptionsV2 = new PromptOptionsV2({
    marginType: MarginTypeV2.DEFAULT_MARGIN,
    marginTop: 0
  });
  @Event onTipClick?: OnTipClickCallback = undefined;
  @Event onActionTextClick?: OnActionTextClickCallback = undefined;
  @Local fontSizeScale: number | undefined = undefined;
  @Local touchBackgroundColor: Resource = $r('sys.color.ohos_id_color_sub_background_transparent');
  @Local maxAppFontScale: number = 1;
  @Local isFollowingSystemFontScale: boolean = false;
  @Local private callbackId: number | undefined = undefined;
  private callbacks: EnvironmentCallback = {
    onConfigurationUpdated: (config) => {
      this.fontSizeScale = Math.min(this.updateFontScale(), MAX_SYMBOL_FONT_SCALE);
      this.fontSizeScale = Math.max(this.fontSizeScale, MIN_SYMBOL_FONT_SCALE);
    },
    onMemoryLevel() {
    }
  };

  @Builder
  TextBuilder() {
    Flex({
      justifyContent: FlexAlign.SpaceBetween,
      alignItems: ItemAlign.Center
    }) {
      Row() {
        if (this.options?.symbolStyle !== undefined) {
          SymbolGlyph()
            .fontColor([$r('sys.color.ohos_id_color_warning')])
            .attributeModifier(this.options?.symbolStyle)
            .effectStrategy(SymbolEffectStrategy.NONE)
            .symbolEffect(new SymbolEffect(), false)
            .fontSize(`${(this.fontSizeScale ?? DEFAULT_SYMBOL_FONT_SCALE) * SYMBOL_SIZE}vp`);
        } else {
          if (Util.isSymbolResource(this.options?.icon)) {
            SymbolGlyph((this.options?.icon as Resource | undefined) ?? $r('sys.symbol.exclamationmark_circle'))
              .fontColor([$r('sys.color.ohos_id_color_warning')])
              .fontSize(`${(this.fontSizeScale ?? DEFAULT_SYMBOL_FONT_SCALE) * SYMBOL_SIZE}vp`);
          } else {
            Image(this.options?.icon)
              .width('24vp')
              .height('24vp')
              .fillColor($r('sys.color.ohos_id_color_warning'));
          }
        }

        Text(this.options?.tip)
          .fontSize($r('sys.float.ohos_id_text_size_body1'))
          .minFontScale(1)
          .maxFontScale(Math.min(this.updateFontScale(), 2))
          .fontColor($r('sys.color.ohos_id_color_warning'))
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(2)
          .margin({
            start: LengthMetrics.resource($r('sys.float.ohos_id_dialog_margin_end'))
          })
          .flexShrink(1)
          .direction(i18n.isRTL(i18n.System.getSystemLanguage()) ? Direction.Rtl : Direction.Ltr);
      }
      .padding({
        right: $r('sys.float.ohos_id_default_padding_end')
      })
      .width('100%')
      .accessibilityDescription(this.onTipClick ? '' : ' ')
      .onClick(() => {
        this.onTipClick && this.onTipClick();
      });

      if (this.options?.actionText) {
        Button({
          stateEffect: false,
          type: ButtonType.Normal
        }) {
          Row() {
            Text(this.options?.actionText)
              .fontSize($r('sys.float.ohos_id_text_size_body2'))
              .minFontScale(1)
              .maxFontScale(Math.min(this.updateFontScale(), 2))
              .fontColor($r('sys.color.ohos_id_color_text_secondary'))
              .maxLines(2)
              .padding(0)
              .margin({
                end: LengthMetrics.resource($r('sys.float.ohos_id_text_paragraph_margin_s'))
              })
              .textOverflow({ overflow: TextOverflow.Ellipsis })
              .flexShrink(1)
              .textAlign(TextAlign.End)
              .direction(i18n.isRTL(i18n.System.getSystemLanguage()) ? Direction.Rtl : Direction.Ltr);

            SymbolGlyph($r('sys.symbol.chevron_right'))
              .fontSize(`${(this.fontSizeScale ?? DEFAULT_SYMBOL_FONT_SCALE) * SYMBOL_SIZE}vp`)
              .fontColor([$r('sys.color.ohos_id_color_tertiary')]);
          }
          .width('100%')
          .justifyContent(FlexAlign.End);
        }
        .backgroundColor(this.touchBackgroundColor)
        .width(this.options?.actionText ? 144 : 0)
        .borderRadius($r('sys.float.ohos_id_corner_radius_subtab'))
        .padding({
          right: $r('sys.float.padding_level2'),
        })
        .accessibilityDescription(this.onActionTextClick ? '' : ' ')
        .accessibilityRole(
          this.onActionTextClick ? AccessibilityRoleType.BUTTON : AccessibilityRoleType.ROLE_NONE
        )
        .onClick(() => {
          this.onActionTextClick && this.onActionTextClick();
        });
      }
    }
    .padding({
      left: $r('sys.float.ohos_id_notification_margin_start'),
      right: $r('sys.float.ohos_id_text_paragraph_margin_s'),
      top: $r('sys.float.ohos_id_default_padding_start'),
      bottom: $r('sys.float.ohos_id_default_padding_end')
    });
  }

  build() {
    Row() {
      Column() {
        Column() {
          this.TextBuilder();
        }
        .width('100%')
        .borderRadius(BORDER_RADIUS)
        .backgroundColor($r('sys.color.comp_background_warning_secondary'))
        .zIndex(ZINDEX_NUM);
      }
      .padding(
        this.options?.marginType === MarginTypeV2.DEFAULT_MARGIN
          ? {
            left: $r('sys.float.ohos_id_card_margin_start'),
            right: $r('sys.float.ohos_id_card_margin_end')
          }
          : {
            left: $r('sys.float.ohos_id_max_padding_start'),
            right: $r('sys.float.ohos_id_max_padding_end')
          }
      )
      .transition(
        TransitionEffect.OPACITY.animation({
          curve: curves.cubicBezierCurve(0.33, 0, 0.67, 1),
          duration: this.options?.isShown ? START_TIME : END_TIME
        })
      )
      .visibility(this.options?.isShown ? Visibility.Visible : Visibility.None);
    }
    .width('100%')
    .position({ y: this.options?.marginTop })
    .zIndex(ZINDEX_NUM);
  }

  aboutToAppear() {
    try {
      let uiContent: UIContext = this.getUIContext();
      this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale();
      this.maxAppFontScale = uiContent.getMaxFontScale();
      this.fontSizeScale = Math.min(this.updateFontScale(), MAX_SYMBOL_FONT_SCALE);
      this.fontSizeScale = Math.max(this.fontSizeScale, MIN_SYMBOL_FONT_SCALE);
      this.callbackId = uiContent.getHostContext()?.getApplicationContext()?.on('environment', this.callbacks);
    } catch (err) {
      let code: number = (err as BusinessError).code;
      let message: string = (err as BusinessError).message;
      hilog.error(0x3900, 'Ace', `Failed to init fontsizescale info, cause, code: ${code}, message: ${message}`);
    }
  }

  aboutToDisappear(): void {
    if (this.callbackId) {
      this.getUIContext().getHostContext()?.getApplicationContext()?.off('environment', this.callbackId);
      this.callbackId = void (0);
    }
  }

  updateFontScale(): number {
    let uiContent: UIContext = this.getUIContext();
    let systemFontScale: number =
      (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
    if (!this.isFollowingSystemFontScale) {
      return 1;
    }
    return Math.min(systemFontScale, this.maxAppFontScale);
  }
}

class Util {
  private static RESOURCE_TYPE_SYMBOL = 40000;

  public static isSymbolResource(resourceStr: ResourceStr | undefined): boolean {
    if (resourceStr === undefined) {
      return true;
    }
    if (!Util.isResourceType(resourceStr)) {
      return false;
    }
    let resource = resourceStr as Resource;
    return resource.type === Util.RESOURCE_TYPE_SYMBOL;
  }

  public static isResourceType(resource: ResourceStr): boolean {
    if (!resource) {
      return false;
    }
    return typeof resource !== 'string';
  }
}