import { AppStorageV2 } from "@kit.ArkUI";
import { BreakpointRule, BreakpointType, BreakpointValueOptions } from "./model/BreakpointModel";

/**
 * @file 全局断点状态,用于窗口尺寸响应式适配
 * @author Joker.X
 */

/**
 * AppStorageV2 键名
 */
export const BREAKPOINT_STATE_KEY: string = "breakpoint_state";

/**
 * 断点规则
 */
const BREAKPOINT_RULES: BreakpointRule[] = [
  /**
   * 超小断点
   */
  { name: BreakpointType.XS, maxWidthVp: 320 },
  /**
   * 小断点
   */
  { name: BreakpointType.SM, maxWidthVp: 600 },
  /**
   * 中断点
   */
  { name: BreakpointType.MD, maxWidthVp: 840 },
  /**
   * 大断点
   */
  { name: BreakpointType.LG, maxWidthVp: Number.POSITIVE_INFINITY }
];

/**
 * 断点顺序
 */
const BREAKPOINT_ORDER: BreakpointType[] = [
  BreakpointType.XS,
  BreakpointType.SM,
  BreakpointType.MD,
  BreakpointType.LG
];

/**
 * 全局断点状态
 */
@ObservedV2
export class BreakpointState {
  @Trace
  current: BreakpointType = BreakpointType.SM;
  @Trace
  windowWidthVp: number = 0;

  /**
   * 更新窗口宽度并计算断点
   * @param {number} windowWidthVp - 窗口宽度(vp)
   * @returns {void} 无返回值
   * @example
  * state.updateByWidth(360);
   */
  updateByWidth(windowWidthVp: number): void {
    this.windowWidthVp = windowWidthVp;
    const next = this.resolveBreakpoint(windowWidthVp);
    if (this.current !== next) {
      this.current = next;
    }
  }

  /**
   * 根据窗口宽度计算断点
   * @param {number} windowWidthVp - 窗口宽度(vp)
   * @returns {BreakpointType} 断点名称
   */
  resolveBreakpoint(windowWidthVp: number): BreakpointType {
    for (const rule of BREAKPOINT_RULES) {
      if (windowWidthVp < rule.maxWidthVp) {
        return rule.name;
      }
    }
    return BreakpointType.LG;
  }

  /**
   * 是否为超小断点
   * @returns {boolean} 是否超小断点
   */
  isXS(): boolean {
    return this.current === BreakpointType.XS;
  }

  /**
   * 是否为小断点
   * @returns {boolean} 是否小断点
   */
  isSM(): boolean {
    return this.current === BreakpointType.SM;
  }

  /**
   * 是否为中断点
   * @returns {boolean} 是否中断点
   */
  isMD(): boolean {
    return this.current === BreakpointType.MD;
  }

  /**
   * 是否为大断点
   * @returns {boolean} 是否大断点
   */
  isLG(): boolean {
    return this.current === BreakpointType.LG;
  }

  /**
   * 根据当前断点返回适配值
   * @param {BreakpointValueOptions<T>} options - 断点值配置
   * @param {T | undefined} defaultValue - 默认值
   * @returns {T} 适配值
   * @example
  * state.getValue({ sm: $r("app.float.small"), md: $r("app.float.medium") });
   */
  getValue<T>(options: BreakpointValueOptions<T>, defaultValue?: T): T {
    const index = BREAKPOINT_ORDER.indexOf(this.current);
    const fallbackValue = this.getFallbackValue(options, defaultValue);
    if (index < 0) {
      return fallbackValue;
    }

    // 优先取当前断点值
    const currentValue = getValueByName(options, BREAKPOINT_ORDER[index]);
    if (currentValue !== undefined) {
      return currentValue;
    }

    for (let i = index - 1; i >= 0; i--) {
      // 向下回退:找更小断点的兜底值
      const value = getValueByName(options, BREAKPOINT_ORDER[i]);
      if (value !== undefined) {
        return value;
      }
    }

    for (let i = index + 1; i < BREAKPOINT_ORDER.length; i++) {
      // 向上补齐:找更大断点的兜底值
      const value = getValueByName(options, BREAKPOINT_ORDER[i]);
      if (value !== undefined) {
        return value;
      }
    }

    return fallbackValue;
  }

  /**
   * 获取兜底值(优先默认值,其次取配置中第一个可用值)
   * @param {BreakpointValueOptions<T>} options - 断点值配置
   * @param {T | undefined} defaultValue - 默认值
   * @returns {T} 兜底值
   */
  private getFallbackValue<T>(options: BreakpointValueOptions<T>, defaultValue?: T): T {
    if (defaultValue !== undefined) {
      return defaultValue;
    }

    const orderedTypes: BreakpointType[] = [
      BreakpointType.XS,
      BreakpointType.SM,
      BreakpointType.MD,
      BreakpointType.LG
    ];

    for (const type of orderedTypes) {
      const value = getValueByName(options, type);
      if (value !== undefined) {
        return value;
      }
    }

    throw new Error("BreakpointValueOptions 不能为空,请至少提供一个断点值。");
  }
}

/**
 * 获取全局断点状态实例;若不存在则创建
 * @returns {BreakpointState} 全局断点状态
 * @example
* const state = getBreakpointState();
 */
export function getBreakpointState(): BreakpointState {
  return AppStorageV2.connect<BreakpointState>(
    BreakpointState,
    BREAKPOINT_STATE_KEY,
    () => new BreakpointState()
  )!;
}

/**
 * 根据全局断点状态返回适配值
 * @param {BreakpointValueOptions<T>} options - 断点值配置
 * @param {T | undefined} defaultValue - 默认值
 * @returns {T} 适配值
 * @example
* bp({ sm: $r("app.float.small"), md: $r("app.float.medium") });
 */
export function bp<T>(options: BreakpointValueOptions<T>, defaultValue?: T): T {
  return getBreakpointState().getValue(options, defaultValue);
}

/**
 * 根据断点名称获取值
 * @param {BreakpointValueOptions<T>} options - 断点值配置
 * @param {BreakpointType} type - 断点类型
 * @returns {T | undefined} 断点值
 */
function getValueByName<T>(options: BreakpointValueOptions<T>, type: BreakpointType): T | undefined {
  if (type === BreakpointType.XS) {
    return options.xs;
  }
  if (type === BreakpointType.SM) {
    return options.sm;
  }
  if (type === BreakpointType.MD) {
    return options.md;
  }
  return options.lg;
}