import {
  DocumentSchema,
  BlockSchema,
  IFileSchema,
  IFileSummarySchema,
  PorjectSchema,
  DataSourceItem,
  DataSourceType
} from '../types';
import { Document } from './Document';
import { Block } from './Block';
import { Node } from './Node';
import { DataSource } from './DataSource';
import {
  emitter,
  EVENT_PROJECT_LOAD,
  EVENT_PROJECT_UPDATE,
  EVENT_PROJECT_CREATE_FILE,
  EVENT_PROJECT_LOAD_FILE,
  EVENT_PROJECT_CHANGE_FILE,
  EVENT_PROJECT_REMOVE_FILE,
  EVENT_PROJECT_UPDATE_FILE,
  EVENT_PROJECT_DATASOURCES_CHANGE
} from '../emitter';
import { isUndefined, merge } from '../../utils';
import { PAGE_NAME, DEFAULT_FILE_SCHEMA } from '../constants';

export type IFile = Document | Block;
export type INode = Document | Block | Node;

export class Project {
  public files: Array<Document | Block> = [];
  public dataSources: Array<DataSource> = [];
  public current: Document | Block | null = null;
  name: string = '';
  title: string = '';
  homePageId: string = '';
  constructor(schema?: PorjectSchema) {
    this.load(schema);
  }

  load(schema?: PorjectSchema) {
    const {
      name,
      title,
      files = [],
      dataSources = [],
      homePageId
    } = schema || {};
    this.name = name || '';
    this.title = title || '';
    this.homePageId = homePageId || '';
    this.files = files.map((file) => {
      return file.name == PAGE_NAME
        ? new Document(file)
        : new Block(file as BlockSchema);
    });
    this.dataSources = dataSources.map((src) => new DataSource(src));
    emitter.emit(EVENT_PROJECT_LOAD, this);
  }

  update(schema?: PorjectSchema) {
    const { name, title, files, dataSources, homePageId } = schema || {};
    if (!isUndefined(name)) {
      this.name = name as string;
    }
    if (!isUndefined(title)) {
      this.title = title as string;
    }
    if (!isUndefined(homePageId)) {
      this.homePageId = homePageId as string;
    }
    if (files) {
      this.files = files.map((file) => {
        return file.name == PAGE_NAME
          ? new Document(file)
          : new Block(file as BlockSchema);
      });
    }
    if (dataSources) {
      this.dataSources = dataSources.map((src) => new DataSource(src));
    }
    emitter.emit(EVENT_PROJECT_UPDATE, this);
  }

  loadFile(schema: IFileSchema) {
    const file = this.files.find((d) => d.id === schema.id);
    if (file) {
      file.load(schema);
      emitter.emit(EVENT_PROJECT_LOAD_FILE, file);
      this.changeFile(file);
    }
  }

  createFile(schema: IFileSchema) {
    const fileSchema: IFileSchema = merge({}, DEFAULT_FILE_SCHEMA, schema);
    const file =
      schema.name === PAGE_NAME
        ? new Document(fileSchema as DocumentSchema)
        : new Block(fileSchema as BlockSchema);
    this.files.push(file);
    // 如果未选择文档，则切换到新建的文档
    if (!this.current) {
      this.current = file;
    }
    emitter.emit(EVENT_PROJECT_CREATE_FILE, file);
    return file;
  }

  changeFile(file: IFile) {
    this.current = file;
    emitter.emit(EVENT_PROJECT_CHANGE_FILE, file);
  }

  removeFile(file: IFile) {
    const index = this.files.indexOf(file);
    if (index > -1) {
      this.files.splice(index, 1);
      // 如果删除的是当前文档，则清除当前文档
      if (this.current === file) {
        this.current = null;
      }
    }
    emitter.emit(EVENT_PROJECT_REMOVE_FILE, file);
  }

  updateFile(schema: IFileSchema) {
    const file = this.files.find((d) => d.id === schema.id);
    if (file) {
      file.update(schema as any, true);
      emitter.emit(EVENT_PROJECT_UPDATE_FILE, file);
    }
  }

  validateFile(schema: IFileSummarySchema, file: IFile | null = null) {
    return !this.files.find((f) => {
      return (
        file !== f &&
        (f.fileName === schema.fileName ||
          (f.name === PAGE_NAME &&
            (f as any).routePath === (schema as any).routePath))
      );
    });
  }

  createDataSource(type: DataSourceType, items: DataSourceItem[] = []) {
    const exist = this.dataSources.find((n) => n.type === type);
    if (!exist) {
      const source = new DataSource({ type, items });
      this.dataSources.push(source);
      emitter.emit(EVENT_PROJECT_DATASOURCES_CHANGE, this.toSchema());
      return source;
    }
    return exist;
  }

  getDataSourceByType(type: DataSourceType) {
    return this.createDataSource(type);
  }

  addDataSource(source: DataSource, item: DataSourceItem) {
    source.add(item);
    emitter.emit(EVENT_PROJECT_DATASOURCES_CHANGE, this.toSchema());
  }

  updateDataSource(source: DataSource, item: DataSourceItem) {
    source.update(item);
    emitter.emit(EVENT_PROJECT_DATASOURCES_CHANGE, this.toSchema());
  }

  replaceDataSource(source: DataSource, id: string, item: DataSourceItem) {
    source.replace(id, item);
    emitter.emit(EVENT_PROJECT_DATASOURCES_CHANGE, this.toSchema());
  }

  removeDataSource(source: DataSource, id: string) {
    source.remove(id);
    emitter.emit(EVENT_PROJECT_DATASOURCES_CHANGE, this.toSchema());
  }

  toSchema() {
    const { name, title, files, dataSources, homePageId } = this;
    return {
      name,
      title,
      files: files.map((n) => n.toSummarySchema()),
      dataSources: dataSources.map((n) => n.toSchema()),
      homePageId
    };
  }
}
