import { createElement, isValidElement, ReactNode } from 'react';
import {
  obx,
  computed,
  makeObservable,
  IEventBus,
  createModuleEventBus,
} from '@felce/lowcode-editor-core';
import { uniqueId, createContent, isReactComponent } from '@felce/lowcode-utils';
import {
  IPublicTypeHelpTipConfig,
  IPublicTypePanelConfig,
  IPublicTypeTitleContent,
} from '@felce/lowcode-types';
import { WidgetContainer } from './widget-container';
import { getEvent } from '@felce/lowcode-shell';
import { TitledPanelView, TabsPanelView, PanelView } from '../components/widget-views';
import { ISkeleton } from '../skeleton';
import { composeTitle } from './utils';
import { IWidget } from './widget';
import { isPanelDock, PanelDock } from './panel-dock';

export class Panel implements IWidget {
  readonly isWidget = true;

  readonly name: string;

  readonly id: string;

  @obx.ref inited = false;

  @obx.ref private _actived = false;

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

  @computed get actived(): boolean {
    return this._actived;
  }

  @computed get visible(): boolean {
    if (!this.parent || this.parent.visible) {
      const { props } = this.config;
      if (props?.condition) {
        return props.condition(this);
      }
      return this._actived;
    }
    return false;
  }

  readonly isPanel = true;

  get body() {
    const { content, contentProps } = this.config;

    // content 配置了 数组 类型，应该走 tabs
    if (this.container) {
      return createElement(TabsPanelView, {
        container: this.container,
        shouldHideSingleTab: true,
      });
    }

    // 如果是数组，应该走上面，不会走这里，目的是为了类型断言
    if (Array.isArray(content)) {
      return null;
    }

    return createContent(content, {
      ...contentProps,
      editor: getEvent(this.skeleton.editor),
      config: this.config,
      panel: this,
      pane: this,
    });
  }

  get content(): ReactNode {
    const area = this.config?.area || this.parent?.name;
    if (this.plain) {
      return createElement(PanelView, {
        panel: this,
        key: this.id,
        area,
      });
    }
    return createElement(TitledPanelView, { panel: this, key: this.id, area });
  }

  readonly title: IPublicTypeTitleContent;

  readonly help?: IPublicTypeHelpTipConfig;

  private plain = false;

  private container?: WidgetContainer<Panel, IPublicTypePanelConfig>;

  @obx.ref public parent?: WidgetContainer;

  constructor(
    readonly skeleton: ISkeleton,
    readonly config: IPublicTypePanelConfig,
  ) {
    makeObservable(this);
    const { name, content, props = {} } = config;
    const { hideTitleBar, title, icon, description, help } = props;
    this.name = name;
    this.id = uniqueId(`pane:${name}$`);
    this.title = composeTitle(title || name, icon, description);
    this.plain = hideTitleBar || !title;
    this.help = help;

    if (Array.isArray(content)) {
      this.container = this.skeleton.createContainer(
        name,
        (item) => {
          if (isPanel(item)) {
            return item;
          }
          return this.skeleton.createPanel(item);
        },
        true,
        () => this.visible,
        true,
      );
      content.forEach((item) => this.add(item));
    }
    if (props.onInit) {
      props.onInit.call(this, this);
    }
    // todo: process shortcut
  }

  setParent(parent: WidgetContainer) {
    if (parent === this.parent) {
      return;
    }
    if (this.parent) {
      this.parent.remove(this);
    }
    this.parent = parent;
  }

  add(item: Panel | IPublicTypePanelConfig) {
    return this.container?.add(item);
  }

  getPane(name: string): Panel | null {
    return this.container?.get(name) || null;
  }

  remove(item: Panel | string) {
    return this.container?.remove(item);
  }

  active(item?: Panel | string | null) {
    if (item) {
      this.container?.active(item);
    } else {
      this.setActive(true);
    }
  }

  getName() {
    return this.name;
  }

  getContent() {
    return this.content;
  }

  /**
   * check is current panel is in float area or not
   *
   * @returns {boolean}
   * @memberof Panel
   */
  isChildOfFloatArea(): boolean {
    return this.parent?.name === 'leftFloatArea';
  }

  /**
   * check is current panel is in fixed area or not
   *
   * @returns {boolean}
   * @memberof Panel
   */
  isChildOfFixedArea(): boolean {
    return this.parent?.name === 'leftFixedArea';
  }

  setActive(flag: boolean) {
    if (flag === this._actived) {
      // TODO: 如果移动到另外一个 container，会有问题
      return;
    }
    if (flag) {
      // 对于 Area 的直接 Child，要专门处理 Float & Fixed 分组切换, 其他情况不需要
      if (this.isChildOfFloatArea()) {
        this.skeleton.leftFixedArea.container.unactiveAll();
      } else if (this.isChildOfFixedArea()) {
        this.skeleton.leftFloatArea.container.unactiveAll();
      }
      this._actived = true;
      this.parent?.active(this);
      if (!this.inited) {
        this.inited = true;
      }
      this.emitter.emit('activechange', true);
    } else if (this.inited) {
      if (this.parent?.name && this.name.startsWith(this.parent.name)) {
        this.inited = false;
      }
      this._actived = false;
      this.parent?.unactive(this);
      this.emitter.emit('activechange', false);
    }
  }

  toggle() {
    this.setActive(!this._actived);
  }

  hide() {
    this.setActive(false);
  }

  disable() {}

  enable(): void {}

  show() {
    this.setActive(true);
  }

  getAssocDocks(): PanelDock[] {
    return this.skeleton.widgets.filter((item) => {
      return isPanelDock(item) && item.panelName === this.name;
    }) as any;
  }

  /**
   * @deprecated
   */
  getSupportedPositions() {
    return ['default'];
  }

  /**
   * @deprecated
   */
  getCurrentPosition() {
    return 'default';
  }

  /**
   * @deprecated
   */
  setPosition(/* position: string */) {
    // noop
  }

  /**
   * @deprecated
   */
  onActiveChange(fn: (flag: boolean) => void): () => void {
    this.emitter.on('activechange', fn);
    return () => {
      this.emitter.removeListener('activechange', fn);
    };
  }
}

export function isPanel(obj: any): obj is Panel {
  return obj && obj.isPanel;
}
