import { uniqueId } from '@felce/lowcode-utils';
import { createModuleEventBus, Editor, IEngineConfig, IEventBus, IHotKey, ISetters, makeObservable, obx } from '@felce/lowcode-editor-core';
import { Context, IViewContext } from './context/view-context';
import { IWorkspace } from './workspace';
import { IResource } from './resource';
import { IPublicApiPlugins, IPublicApiProject, IPublicModelWindow, IPublicTypeDisposable } from '@felce/lowcode-types';
import { IDesigner, ILowCodePluginManager, IProject } from '@felce/lowcode-designer';
import { ISkeleton } from '@felce/lowcode-editor-skeleton';

interface IWindowCOnfig {
  title: string | undefined;
  options?: Object;
  viewName?: string | undefined;
  sleep?: boolean;
}

export interface IEditorWindow
  extends Omit<
    IPublicModelWindow<IResource>,
    'changeViewType' | 'currentEditorView' | 'editorViews'
  > {
  readonly resource: IResource;

  title: string | undefined;

  url: string | undefined;

  editorViews: Map<string, IViewContext>;

  _editorView: IViewContext;

  editorView: Context;


  project: IPublicApiProject

  innerProject: IProject

  innerSkeleton: ISkeleton

  innerSetters: ISetters

  innerHotkey: IHotKey

  editor: Editor

  designer: IDesigner

  plugins: IPublicApiPlugins

  innerPlugins: ILowCodePluginManager

  workspace: IWorkspace

  resourceType: string

  initViewTypes: (name: string) => Promise<void>

  changeViewName: (name: string, ignoreEmit?: boolean) => void;

  execViewTypesInit: () => Promise<void>

  setDefaultViewName: () => void

  initViewType: (name: string) => Promise<void>

  initReady: boolean;

  sleep?: boolean;

  init(): void;

  updateState(state: WINDOW_STATE): void;
}

export enum WINDOW_STATE {
  // 睡眠
  sleep = 'sleep',

  // 激活
  active = 'active',

  // 未激活
  inactive = 'inactive',

  // 销毁
  destroyed = 'destroyed',
}

export class EditorWindow implements IEditorWindow {
  id: string = uniqueId('window');
  icon: React.ReactElement | React.FunctionComponent | React.ComponentClass | undefined;

  private emitter: IEventBus = createModuleEventBus('Project');

  title: string | undefined;

  url: string | undefined;

  @obx.ref _editorView: Context;

  @obx editorViews: Map<string, Context> = new Map<string, Context>();

  @obx initReady = false;

  sleep: boolean | undefined;

  get editorView() {
    if (!this._editorView) {
      return this.editorViews.values().next().value;
    }
    return this._editorView;
  }

  constructor(
    readonly resource: IResource,
    readonly workspace: IWorkspace,
    private config: IWindowCOnfig,
  ) {
    makeObservable(this);
    this.title = config.title;
    this.icon = resource.icon;
    this.sleep = config.sleep;
    if (config.sleep) {
      this.updateState(WINDOW_STATE.sleep);
    }
  }

  updateState(state: WINDOW_STATE): void {
    switch (state) {
      case WINDOW_STATE.active:
        this._editorView?.setActivate(true);
        break;
      case WINDOW_STATE.inactive:
        this._editorView?.setActivate(false);
        break;
      case WINDOW_STATE.destroyed:
        break;
    }
  }

  async importSchema(schema: any) {
    const newSchema = await this.resource.import(schema);

    if (!newSchema) {
      return;
    }

    Object.keys(newSchema).forEach((key) => {
      const view = this.editorViews.get(key);
      view?.project.importSchema(newSchema[key]);
    });
  }

  async save() {
    const value: any = {};
    const editorViews = this.resource.editorViews;
    if (!editorViews) {
      return;
    }
    for (let i = 0; i < editorViews.length; i++) {
      const name = editorViews[i].viewName;
      const saveResult = await this.editorViews.get(name)?.save();
      value[name] = saveResult;
    }
    const result = await this.resource.save(value);
    this.emitter.emit('handle.save');

    return result;
  }

  onSave(fn: () => void) {
    this.emitter.on('handle.save', fn);

    return () => {
      this.emitter.off('handle.save', fn);
    };
  }

  async init() {
    await this.initViewTypes();
    await this.execViewTypesInit();
    Promise.all(
      Array.from(this.editorViews.values()).map((d) => d.onSimulatorRendererReady()),
    ).then(() => {
      this.workspace.emitWindowRendererReady();
    });
    this.url = await this.resource.url();
    this.setDefaultViewName();
    this.initReady = true;
    this.workspace.checkWindowQueue();
    this.sleep = false;
    this.updateState(WINDOW_STATE.active);
  }

  initViewTypes = async () => {
    const editorViews = this.resource.editorViews;
    if (!editorViews) {
      return;
    }
    for (let i = 0; i < editorViews.length; i++) {
      const name = editorViews[i].viewName;
      await this.initViewType(name);
      if (!this._editorView) {
        this.changeViewName(name);
      }
    }
  };

  onChangeViewType(fn: (viewName: string) => void): IPublicTypeDisposable {
    this.emitter.on('window.change.view.type', fn);

    return () => {
      this.emitter.off('window.change.view.type', fn);
    };
  }

  execViewTypesInit = async () => {
    const editorViews = this.resource.editorViews;
    if (!editorViews) {
      return;
    }
    for (let i = 0; i < editorViews.length; i++) {
      const name = editorViews[i].viewName;
      this.changeViewName(name);
      await this.editorViews.get(name)?.init();
    }
  };

  setDefaultViewName = () => {
    this.changeViewName(this.config.viewName ?? this.resource.defaultViewName!);
  };

  get resourceType() {
    return this.resource.resourceType.type;
  }

  initViewType = async (name: string) => {
    const viewInfo = this.resource.getEditorView(name);
    if (this.editorViews.get(name)) {
      return;
    }
    const editorView = new Context(this.workspace, this, viewInfo as any, this.config.options);
    this.editorViews.set(name, editorView);
  };

  changeViewName = (name: string, ignoreEmit: boolean = true) => {
    this._editorView?.setActivate(false);
    this._editorView = this.editorViews.get(name)!;

    if (!this._editorView) {
      return;
    }

    this._editorView.setActivate(true);

    if (!ignoreEmit) {
      this.emitter.emit('window.change.view.type', name);

      if (this.id === this.workspace.window.id) {
        this.workspace.emitChangeActiveEditorView();
      }
    }
  };

  get project() {
    return this.editorView?.project;
  }

  get innerProject() {
    return this.editorView?.innerProject;
  }

  get innerSkeleton() {
    return this.editorView?.innerSkeleton;
  }

  get innerSetters() {
    return this.editorView?.innerSetters;
  }

  get innerHotkey() {
    return this.editorView?.innerHotkey;
  }

  get editor() {
    return this.editorView?.editor;
  }

  get designer() {
    return this.editorView?.designer;
  }

  get plugins() {
    return this.editorView?.plugins;
  }

  get innerPlugins() {
    return this.editorView?.innerPlugins;
  }
}
