import {
  IPublicTypeTitleContent,
  IPublicTypeLocationChildrenDetail,
  IPublicModelNode,
  IPublicModelPluginContext,
} from '@alilc/lowcode-types';
import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils';
import { Tree } from './tree';

/**
 * 大纲树过滤结果
 */
export interface FilterResult {
  // 过滤条件是否生效
  filterWorking: boolean;
  // 命中子节点
  matchChild: boolean;
  // 命中本节点
  matchSelf: boolean;
  // 关键字
  keywords: string;
}

export default class TreeNode {
  readonly pluginContext: IPublicModelPluginContext;
  onFilterResultChanged: () => void;
  onExpandedChanged: (expanded: boolean) => void;
  onHiddenChanged: (hidden: boolean) => void;
  onLockedChanged: (locked: boolean) => void;
  onTitleLabelChanged: (treeNode: TreeNode) => void;
  onExpandableChanged: (expandable: boolean) => void;

  get id(): string {
    return this.node.id;
  }

  /**
   * 是否可以展开
   */
  get expandable(): boolean {
    if (this.locked) return false;
    return this.hasChildren() || this.hasSlots() || this.dropDetail?.index != null;
  }

  /**
   * 触发 onExpandableChanged 回调
   */
  notifyExpandableChanged(): void {
    this.onExpandableChanged && this.onExpandableChanged(this.expandable);
  }

  /**
   * 插入"线"位置信息
   */
  get dropDetail(): IPublicTypeLocationChildrenDetail | undefined | null {
    const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
    return loc && this.isResponseDropping() && isLocationChildrenDetail(loc.detail) ? loc.detail : null;
  }

  get depth(): number {
    return this.node.zLevel;
  }

  isRoot(includeOriginalRoot = false) {
    const rootNode = this.pluginContext.project.getCurrentDocument()?.root;
    return this.tree.root === this || (includeOriginalRoot && rootNode === this.node);
  }

  /**
   * 是否是响应投放区
   */
  isResponseDropping(): boolean {
    const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
    if (!loc) {
      return false;
    }
    return loc.target?.id === this.id;
  }

  isFocusingNode(): boolean {
    const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
    if (!loc) {
      return false;
    }
    return (
      isLocationChildrenDetail(loc.detail) && loc.detail.focus?.type === 'node' && loc.detail?.focus?.node.id === this.id
    );
  }

  /**
   * 默认为折叠状态
   * 在初始化根节点时，设置为展开状态
   */
  private _expanded = false;

  get expanded(): boolean {
    return this.isRoot(true) || (this.expandable && this._expanded);
  }

  setExpanded(value: boolean) {
    this._expanded = value;
    this.onExpandedChanged && this.onExpandedChanged(value);
  }

  get detecting() {
    const doc = this.pluginContext.project.currentDocument;
    return !!(doc?.isDetectingNode(this.node));
  }

  get hidden(): boolean {
    const cv = this.node.isConditionalVisible();
    if (cv == null) {
      return !this.node.visible;
    }
    return !cv;
  }

  setHidden(flag: boolean) {
    if (this.node.conditionGroup) {
      return;
    }
    this.node.visible = !flag;
    this.onHiddenChanged && this.onHiddenChanged(flag);
  }

  get locked(): boolean {
    return this.node.isLocked;
  }

  setLocked(flag: boolean) {
    this.node.lock(flag);
    this.onLockedChanged && this.onLockedChanged(flag);
  }

  get selected(): boolean {
    // TODO: check is dragging
    const selection = this.pluginContext.project.getCurrentDocument()?.selection;
    if (!selection) {
      return false;
    }
    return selection?.has(this.node.id);
  }

  get title(): IPublicTypeTitleContent {
    return this.node.title;
  }

  get titleLabel() {
    let { title } = this;
    if (!title) {
      return '';
    }
    if ((title as any).label) {
      title = (title as any).label;
    }
    if (typeof title === 'string') {
      return title;
    }
    if (isI18nData(title)) {
      const currentLocale = this.pluginContext.getLocale();
      const currentTitle = title[currentLocale];
      return currentTitle;
    }
    return this.node.componentName;
  }

  setTitleLabel(label: string) {
    const origLabel = this.titleLabel;
    if (label === origLabel) {
      return;
    }
    if (label === '') {
      this.node.getExtraProp('title', false)?.remove();
    } else {
      this.node.getExtraProp('title', true)?.setValue(label);
    }
    this.onTitleLabelChanged && this.onTitleLabelChanged(this);
  }

  get icon() {
    return this.node.componentMeta.icon;
  }

  get parent(): TreeNode | null {
    const { parent } = this.node;
    if (parent) {
      return this.tree.getTreeNode(parent);
    }
    return null;
  }

  get slots(): TreeNode[] {
    // todo: shallowEqual
    return this.node.slots.map((node) => this.tree.getTreeNode(node));
  }

  get children(): TreeNode[] | null {
    return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null;
  }

  /**
   * 是否是容器，允许子节点拖入
   */
  isContainer(): boolean {
    return this.node.isContainerNode;
  }

  /**
   * 判断是否有"插槽"
   */
  hasSlots(): boolean {
    return this.node.hasSlots();
  }

  hasChildren(): boolean {
    return !!(this.isContainer() && this.node.children?.notEmptyNode);
  }

  select(isMulti: boolean) {
    const { node } = this;

    const selection = this.pluginContext.project.getCurrentDocument()?.selection;
    if (isMulti) {
      selection?.add(node.id);
    } else {
      selection?.select(node.id);
    }
  }

  /**
   * 展开节点，支持依次展开父节点
   */
  expand(tryExpandParents = false) {
    // 这边不能直接使用 expanded，需要额外判断是否可以展开
    // 如果只使用 expanded，会漏掉不可以展开的情况，即在不可以展开的情况下，会触发展开
    if (this.expandable && !this._expanded) {
      this.setExpanded(true);
    }
    if (tryExpandParents) {
      this.expandParents();
    }
  }

  expandParents() {
    let p = this.node.parent;
    while (p) {
      this.tree.getTreeNode(p).setExpanded(true);
      p = p.parent;
    }
  }

  private _node: IPublicModelNode;

  get node(): IPublicModelNode {
    return this._node;
  }

  readonly tree: Tree;

  constructor(tree: Tree, node: IPublicModelNode, pluginContext: IPublicModelPluginContext) {
    this.tree = tree;
    this.pluginContext = pluginContext;
    this._node = node;
  }

  setNode(node: IPublicModelNode) {
    if (this._node !== node) {
      this._node = node;
    }
  }

  private _filterResult: FilterResult = {
    filterWorking: false,
    matchChild: false,
    matchSelf: false,
    keywords: '',
  };

  get filterReult(): FilterResult {
    return this._filterResult;
  }

  setFilterReult(val: FilterResult) {
    this._filterResult = val;
    if (this.onFilterResultChanged) {
      this.onFilterResultChanged();
    }
  }
}
