/**
 * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
 * SPDX-License-Identifier: MIT
 */

// traverse value type - 遍历值类型
export type TraverseValue = any;

// traverse node interface - 遍历节点接口
export interface TraverseNode {
  value: TraverseValue; // node value - 节点值
  container?: TraverseValue; // parent container - 父容器
  parent?: TraverseNode; // parent node - 父节点
  key?: string; // object key - 对象键名
  index?: number; // array index - 数组索引
}

// traverse context interface - 遍历上下文接口
export interface TraverseContext {
  node: TraverseNode; // current node - 当前节点
  setValue: (value: TraverseValue) => void; // set value function - 设置值函数
  getParents: () => TraverseNode[]; // get parents function - 获取父节点函数
  getPath: () => Array<string | number>; // get path function - 获取路径函数
  getStringifyPath: () => string; // get string path function - 获取字符串路径函数
  deleteSelf: () => void; // delete self function - 删除自身函数
}

// traverse handler type - 遍历处理器类型
export type TraverseHandler = (context: TraverseContext) => void;

/**
 * traverse object deeply and handle each value - 深度遍历对象并处理每个值
 * @param value traverse target - 遍历目标
 * @param handle handler function - 处理函数
 */
export const traverse = <T extends TraverseValue = TraverseValue>(
  value: T,
  handler: TraverseHandler | TraverseHandler[]
): T => {
  const traverseHandler: TraverseHandler = Array.isArray(handler)
    ? (context: TraverseContext) => {
        handler.forEach((handlerFn) => handlerFn(context));
      }
    : handler;
  TraverseUtils.traverseNodes({ value }, traverseHandler);
  return value;
};

namespace TraverseUtils {
  /**
   * traverse nodes deeply and handle each value - 深度遍历节点并处理每个值
   * @param node traverse node - 遍历节点
   * @param handle handler function - 处理函数
   */
  export const traverseNodes = (node: TraverseNode, handle: TraverseHandler): void => {
    const { value } = node;
    if (!value) {
      // handle null value - 处理空值
      return;
    }
    if (Object.prototype.toString.call(value) === '[object Object]') {
      // traverse object properties - 遍历对象属性
      Object.entries(value).forEach(([key, item]) =>
        traverseNodes(
          {
            value: item,
            container: value,
            key,
            parent: node,
          },
          handle
        )
      );
    } else if (Array.isArray(value)) {
      // traverse array elements from end to start - 从末尾开始遍历数组元素
      for (let index = value.length - 1; index >= 0; index--) {
        const item: string = value[index];
        traverseNodes(
          {
            value: item,
            container: value,
            index,
            parent: node,
          },
          handle
        );
      }
    }
    const context: TraverseContext = createContext({ node });
    handle(context);
  };

  /**
   * create traverse context - 创建遍历上下文
   * @param node traverse node - 遍历节点
   */
  const createContext = ({ node }: { node: TraverseNode }): TraverseContext => ({
    node,
    setValue: (value: unknown) => setValue(node, value),
    getParents: () => getParents(node),
    getPath: () => getPath(node),
    getStringifyPath: () => getStringifyPath(node),
    deleteSelf: () => deleteSelf(node),
  });

  /**
   * set node value - 设置节点值
   * @param node traverse node - 遍历节点
   * @param value new value - 新值
   */
  const setValue = (node: TraverseNode, value: unknown) => {
    // handle empty value - 处理空值
    if (!value || !node) {
      return;
    }
    node.value = value;
    // get container info from parent scope - 从父作用域获取容器信息
    const { container, key, index } = node;
    if (key && container) {
      container[key] = value;
    } else if (typeof index === 'number') {
      container[index] = value;
    }
  };

  /**
   * get parent nodes - 获取父节点列表
   * @param node traverse node - 遍历节点
   */
  const getParents = (node: TraverseNode): TraverseNode[] => {
    const parents: TraverseNode[] = [];
    let currentNode: TraverseNode | undefined = node;
    while (currentNode) {
      parents.unshift(currentNode);
      currentNode = currentNode.parent;
    }
    return parents;
  };

  /**
   * get node path - 获取节点路径
   * @param node traverse node - 遍历节点
   */
  const getPath = (node: TraverseNode): Array<string | number> => {
    const path: Array<string | number> = [];
    const parents = getParents(node);
    parents.forEach((parent) => {
      if (parent.key) {
        path.unshift(parent.key);
      } else if (parent.index) {
        path.unshift(parent.index);
      }
    });
    return path;
  };

  /**
   * get stringify path - 获取字符串路径
   * @param node traverse node - 遍历节点
   */
  const getStringifyPath = (node: TraverseNode): string => {
    const path = getPath(node);
    return path.reduce((stringifyPath: string, pathItem: string | number) => {
      if (typeof pathItem === 'string') {
        const re = /\W/g;
        if (re.test(pathItem)) {
          // handle special characters - 处理特殊字符
          return `${stringifyPath}["${pathItem}"]`;
        }
        return `${stringifyPath}.${pathItem}`;
      } else {
        return `${stringifyPath}[${pathItem}]`;
      }
    }, '');
  };

  /**
   * delete current node - 删除当前节点
   * @param node traverse node - 遍历节点
   */
  const deleteSelf = (node: TraverseNode): void => {
    const { container, key, index } = node;
    if (key && container) {
      delete container[key];
    } else if (typeof index === 'number') {
      container.splice(index, 1);
    }
  };
}
