import type { DepData } from '@tmagic/schema';

import { DepTargetType, type IsTarget, type TargetOptions } from './types';

export interface DepUpdateOptions {
  id: string | number;
  name: string;
  key: string | number;
  data: Record<string, any>;
}

/**
 * 需要收集依赖的目标
 * 例如：一个代码块可以为一个目标
 */
export default class Target {
  /**
   * 如何识别目标
   */
  public isTarget: IsTarget;
  /**
   * 目标id，不可重复
   * 例如目标是代码块，则为代码块id
   */
  public id: string | number;
  /**
   * 目标名称，用于显示在依赖列表中
   */
  public name?: string;
  /**
   * 不同的目标可以进行分类，例如代码块，数据源可以为两个不同的type
   */
  public type: string = DepTargetType.DEFAULT;
  /**
   * 依赖详情
   * 实例：{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }
   */
  public deps: DepData = {};
  /**
   * 是否默认收集，默认为true，当值为false时需要传入type参数给collect方法才会被收集
   */
  public isCollectByDefault?: boolean;

  constructor(options: TargetOptions) {
    this.isTarget = options.isTarget;
    this.id = options.id;
    this.name = options.name;
    this.isCollectByDefault = options.isCollectByDefault ?? true;
    if (options.type) {
      this.type = options.type;
    }
    if (options.initialDeps) {
      this.deps = options.initialDeps;
    }
  }

  /**
   * 更新依赖
   * @param option 节点配置
   * @param key 哪个key配置了这个目标的id
   */
  public updateDep({ id, name, key, data }: DepUpdateOptions) {
    const dep = this.deps[id] || {
      name,
      keys: [],
    };

    dep.name = name;

    dep.data = data;

    this.deps[id] = dep;

    if (!dep.keys.includes(key)) {
      dep.keys.push(key);
    }
  }

  /**
   * 删除依赖
   * @param node 哪个节点的依赖需要移除，如果为空，则移除所有依赖
   * @param key 节点下哪个key需要移除，如果为空，则移除改节点下的所有依赖key
   * @returns void
   */
  public removeDep(id?: string | number, key?: string | number) {
    if (typeof id === 'undefined') {
      Object.keys(this.deps).forEach((depKey) => {
        delete this.deps[depKey];
      });
      return;
    }

    const dep = this.deps[id];

    if (!dep) return;

    if (key) {
      const index = dep.keys.indexOf(key);
      dep.keys.splice(index, 1);

      if (dep.keys.length === 0) {
        delete this.deps[id];
      }
    } else {
      delete this.deps[id];
    }
  }

  /**
   * 判断指定节点下的指定key是否存在在依赖列表中
   * @param node 哪个节点
   * @param key 哪个key
   * @returns boolean
   */
  public hasDep(id: string | number, key: string | number) {
    const dep = this.deps[id];

    return dep?.keys.includes(key) ?? false;
  }

  public destroy() {
    this.deps = {};
  }
}
