import {
  BlockSchema,
  DocumentSummarySchema,
  JSONValue,
  JSExpression,
  JSFunction,
  ComponentDescription,
  ComponentDefine,
  IFileDataSource,
  WatchSchema,
  NodeSchema
} from '../types';
import { Node, NodeNormalPropNames } from './Node';
import { Assets } from '../Assets';
import { BLOCK_NAME } from '../constants';
import { emitter, EVENT_NODE_CHANGE } from './../emitter';

export type MoveType = 'after' | 'before' | 'inner';

export const BlockNormalPropNames = [
  ...NodeNormalPropNames,
  'fileName',
  'state',
  'methods',
  'lifeCycles',
  'css',
  'define',
  'release',
  'componentMap',
  'dataSources',
  'computed',
  'watch',
  'title'
];

export const BlockSummaryPropNames: string[] = [
  'id',
  'name',
  'fileName',
  'title'
];

export class Block extends Node {
  __id__: string = '';
  name: string = BLOCK_NAME;
  title?: string;
  fileName?: string;
  state: Record<string, JSONValue | JSExpression> = {};
  methods: Record<string, JSFunction> = {};
  lifeCycles: Record<string, JSFunction> = {};
  css?: string;
  componentMap: Record<string, ComponentDescription> = {};
  dataSources: Record<string, IFileDataSource> = {};
  computed: Record<string, JSFunction> = {};
  watch: Record<string, WatchSchema> = {};
  // todo 待实现
  define?: ComponentDefine;
  // todo 待实现
  release: any;

  normal: string[] = BlockNormalPropNames;

  summary: string[] = BlockSummaryPropNames;

  // 节点id 映射
  nodeMap: Record<string, Node> = {};
  // 组件名称引用记录表，用作处理输出
  nodeNameRefs: Record<string, string[]> = {};

  // Vue子组件实例映射
  instanceMap: Record<string, any> = {};

  // SchemaRenderer 实例
  renderer: any;

  constructor(schema: BlockSchema, parent?: Block | Document) {
    super(schema, parent as any);
    // todo：需要优化
    this.load(schema);
    this.nodeMap[this.id] = this;
  }

  /**
   * 记录组件信息
   * @param assets
   * @param name 组件名
   */
  registerComponent(assets: Assets, schema: NodeSchema) {
    // todo: 实现递归注册子组件
    const component = assets.componentMap[schema.name];
    if (component) {
      this.componentMap[schema.name] = component;
    }
    if (Array.isArray(schema.children)) {
      schema.children.forEach((item) => {
        this.registerComponent(assets, item);
      });
    }
  }

  addNodeNameRef(node: Node) {
    let refs = this.nodeNameRefs[node.name];
    if (!refs) {
      refs = this.nodeNameRefs[node.name] = [];
    }
    refs.push(node.id);
  }

  removeNodeNameRef(node: Node) {
    const refs = this.nodeNameRefs[node.name] || [];
    const index = refs.findIndex((v) => v === node.id);
    if (index > -1) {
      refs.splice(index, 1);
    }
    // 如果无引用项，删除componentMap
    if (refs.length === 0) {
      delete this.componentMap[node.name];
    }
  }

  toSummarySchema(): DocumentSummarySchema {
    return this.summary.reduce((result, key) => {
      result[key] = (this as any)[key];
      return result;
    }, {} as Record<string, any>) as DocumentSummarySchema;
  }

  move(node: Node, target: Node, type: MoveType) {
    if (type === 'after') {
      node.parent?.removeChild(node);
      target.parent?.insertAfter(node, target);
    } else if (type === 'before') {
      node.parent?.removeChild(node);
      target.parent?.insertBefore(node, target);
    } else if (type === 'inner') {
      node.parent?.removeChild(node);
      target.appendChild(node);
    }
  }

  setState(key: string, value: JSONValue | JSExpression) {
    if (!this.state) {
      this.state = {};
    }
    this.state[key] = value;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeState(key: string) {
    if (!this.state) {
      return;
    }
    delete this.state[key];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }

  setMethod(key: string, value: JSFunction) {
    if (!this.methods) {
      this.methods = {};
    }
    this.methods[key] = value;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeMethod(key: string) {
    if (!this.methods) {
      return;
    }
    delete this.methods[key];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  setComputed(key: string, value: JSFunction) {
    if (!this.computed) {
      this.computed = {};
    }
    this.computed[key] = value;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeComputed(key: string) {
    if (!this.computed) {
      return;
    }
    delete this.computed[key];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  setWatch(key: string, value: WatchSchema) {
    if (!this.watch) {
      this.watch = {};
    }
    this.watch[key] = value;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeWatch(key: string) {
    if (!this.watch) {
      return;
    }
    delete this.watch[key];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  setLifeCycle(key: string, value: JSFunction) {
    if (!this.lifeCycles) {
      this.lifeCycles = {};
    }
    this.lifeCycles[key] = value;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeLifeCycle(key: string) {
    if (!this.lifeCycles) {
      return;
    }
    delete this.lifeCycles[key];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  setDataSource(id: string, item: IFileDataSource) {
    if (!this.dataSources) {
      this.dataSources = {};
    }
    this.dataSources[id] = item;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
  removeDataSource(id: string) {
    if (!this.dataSources) {
      return;
    }
    delete this.dataSources[id];
    emitter.emit(EVENT_NODE_CHANGE, this);
  }

  setCss(css: string) {
    this.css = css;
    emitter.emit(EVENT_NODE_CHANGE, this);
  }
}
