<template>
  <Framework
    :disabled-page-fragment="disabledPageFragment"
    :page-bar-sort-options="pageBarSortOptions"
    :page-filter-function="pageFilterFunction"
    :hide-sidebar="hideSidebar"
  >
    <template #header>
      <slot name="header"></slot>
    </template>

    <template #nav>
      <slot name="nav" :editorService="editorService"><TMagicNavMenu :data="menu"></TMagicNavMenu></slot>
    </template>

    <template #content-before>
      <slot name="content-before"></slot>
    </template>

    <template #src-code><slot name="src-code" :editorService="editorService"></slot></template>

    <template #sidebar>
      <slot name="sidebar" :editorService="editorService">
        <Sidebar
          :data="sidebar"
          :layer-content-menu="layerContentMenu"
          :custom-content-menu="customContentMenu"
          :indent="treeIndent"
          :next-level-indent-increment="treeNextLevelIndentIncrement"
          :layer-node-is-expandable="layerNodeIsExpandable"
          :can-drop-in="canDropIn"
          :before-layer-node-dblclick="beforeLayerNodeDblclick"
          @layer-node-dblclick="layerNodeDblclickHandler"
        >
          <template #layer-panel-header>
            <slot name="layer-panel-header"></slot>
          </template>

          <template #layer-node-content="{ data }">
            <slot name="layer-node-content" :data="data"></slot>
          </template>

          <template #layer-node-label="{ data }">
            <slot name="layer-node-label" :data="data"></slot>
          </template>

          <template #layer-node-tool="{ data }">
            <slot name="layer-node-tool" :data="data"></slot>
          </template>

          <template #component-list="{ componentGroupList }">
            <slot name="component-list" :component-group-list="componentGroupList"></slot>
          </template>

          <template #component-list-panel-header>
            <slot name="component-list-panel-header"></slot>
          </template>

          <template #component-list-item="{ component }">
            <slot name="component-list-item" :component="component"></slot>
          </template>

          <template #code-block-panel-header>
            <slot name="code-block-panel-header"></slot>
          </template>

          <template #code-block-panel-tool="{ id, data }">
            <slot name="code-block-panel-tool" :id="id" :data="data"></slot>
          </template>

          <template #code-block-panel-search>
            <slot name="code-block-panel-search"></slot>
          </template>

          <template #data-source-panel-tool="{ data }">
            <slot name="data-source-panel-tool" :data="data"></slot>
          </template>

          <template #data-source-panel-search>
            <slot name="data-source-panel-search"></slot>
          </template>
        </Sidebar>
      </slot>
    </template>

    <template #workspace>
      <slot name="workspace" :editorService="editorService">
        <Workspace
          :disabled-stage-overlay="disabledStageOverlay"
          :stage-content-menu="stageContentMenu"
          :custom-content-menu="customContentMenu"
        >
          <template #stage-top><slot name="stage-top"></slot></template>
          <template #stage><slot name="stage"></slot></template>
          <template #workspace-content><slot name="workspace-content" :editorService="editorService"></slot></template>
        </Workspace>
      </slot>
    </template>

    <template #props-panel>
      <slot name="props-panel">
        <PropsPanel
          :extend-state="extendFormState"
          :disabled-show-src="disabledShowSrc"
          @mounted="propsPanelMountedHandler"
          @unmounted="propsPanelUnmountedHandler"
          @form-error="propsPanelFormErrorHandler"
          @submit-error="propsPanelSubmitErrorHandler"
        >
          <template #props-panel-header>
            <slot name="props-panel-header"></slot>
          </template>
        </PropsPanel>
      </slot>
    </template>

    <template #empty><slot name="empty" :editorService="editorService"></slot></template>

    <template #content-after>
      <slot name="content-after"></slot>
    </template>

    <template #footer>
      <slot name="footer"></slot>
    </template>

    <template #page-bar><slot name="page-bar"></slot></template>
    <template #page-bar-add-button><slot name="page-bar-add-button"></slot></template>
    <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
    <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
    <template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
  </Framework>
</template>

<script lang="ts" setup>
import { EventEmitter } from 'events';

import { provide, ref } from 'vue';

import type { MApp } from '@tmagic/core';

import Framework from './layouts/Framework.vue';
import TMagicNavMenu from './layouts/NavMenu.vue';
import FormPanel from './layouts/props-panel/FormPanel.vue';
import PropsPanel from './layouts/props-panel/PropsPanel.vue';
import Sidebar from './layouts/sidebar/Sidebar.vue';
import Workspace from './layouts/workspace/Workspace.vue';
import codeBlockService from './services/codeBlock';
import componentListService from './services/componentList';
import dataSourceService from './services/dataSource';
import depService from './services/dep';
import editorService from './services/editor';
import eventsService from './services/events';
import historyService from './services/history';
import keybindingService from './services/keybinding';
import propsService from './services/props';
import stageOverlayService from './services/stageOverlay';
import storageService from './services/storage';
import uiService from './services/ui';
import keybindingConfig from './utils/keybinding-config';
import { defaultEditorProps, EditorProps } from './editorProps';
import { initServiceEvents, initServiceState } from './initService';
import type { TreeNodeData } from './type';
import type { EditorSlots, EventBus, Services, StageOptions } from './type';

defineSlots<EditorSlots>();

defineOptions({
  name: 'MEditor',
});

const emit = defineEmits<{
  'props-panel-mounted': [instance: InstanceType<typeof FormPanel>];
  'props-panel-unmounted': [];
  'update:modelValue': [value: MApp | null];
  'props-form-error': [e: any];
  'props-submit-error': [e: any];
  'layer-node-dblclick': [event: MouseEvent, data: TreeNodeData];
}>();

const props = withDefaults(defineProps<EditorProps>(), defaultEditorProps);

const services: Services = {
  componentListService,
  eventsService,
  historyService,
  propsService,
  editorService,
  uiService,
  storageService,
  codeBlockService,
  depService,
  dataSourceService,
  keybindingService,
  stageOverlayService,
};

initServiceEvents(props, emit, services);
initServiceState(props, services);
keybindingService.register(keybindingConfig);
keybindingService.registerEl('global');

const stageOptions: StageOptions = {
  runtimeUrl: props.runtimeUrl,
  autoScrollIntoView: props.autoScrollIntoView,
  render: props.render,
  moveableOptions: props.moveableOptions,
  canSelect: props.canSelect,
  updateDragEl: props.updateDragEl,
  isContainer: props.isContainer,
  // sourceIds 为空表示从组件列表新增（尚无 id），否则是画布上拖动已有组件
  canDropIn: props.canDropIn
    ? (sourceIds, targetId) =>
        props.canDropIn!(sourceIds, targetId, sourceIds.length === 0 ? 'stage-add' : 'stage-drag')
    : undefined,
  containerHighlightClassName: props.containerHighlightClassName,
  containerHighlightDuration: props.containerHighlightDuration,
  containerHighlightType: props.containerHighlightType,
  disabledDragStart: props.disabledDragStart,
  renderType: props.renderType,
  guidesOptions: props.guidesOptions,
  disabledMultiSelect: props.disabledMultiSelect,
  alwaysMultiSelect: props.alwaysMultiSelect,
  disabledFlashTip: props.disabledFlashTip,
  beforeDblclick: props.beforeDblclick,
};

stageOverlayService.set('stageOptions', stageOptions);

const propsPanelRef = ref<InstanceType<typeof FormPanel> | null>(null);

provide('services', services);

provide('codeOptions', props.codeOptions);
provide('stageOptions', stageOptions);
/**
 * 把顶层 `extendFormState` 提供给非 PropsPanel 链路上的组件使用（例如历史差异对话框 HistoryDiffDialog
 * 内部的 CompareForm）。这样所有依赖业务上下文的表单 filterFunction 都能拿到一致的扩展状态，
 * 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
 */
provide('extendFormState', props.extendFormState);
/**
 * 提供 PropsPanel 主属性表单的 formState getter，供历史差异弹窗复用，
 * 让 CompareForm 与 PropsPanel 的 filterFunction 上下文保持一致。
 */
provide('getPropsPanelFormState', () => propsPanelRef.value?.configForm?.formState);

/**
 * 把历史记录面板的自定义扩展 tab 提供给深层的 HistoryListPanel（它挂在 NavMenu 中，
 * 以 markRaw component 形式渲染，无法直接通过 props 透传）。业务方可借此在历史记录
 * 面板内追加自定义模块的历史 tab。
 */
provide('historyListExtraTabs', props.historyListExtraTabs);

provide<EventBus>('eventBus', new EventEmitter());

const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
  propsPanelRef.value = e;
  emit('props-panel-mounted', e);
};
const propsPanelUnmountedHandler = () => {
  propsPanelRef.value = null;
  emit('props-panel-unmounted');
};

const propsPanelSubmitErrorHandler = (e: any) => {
  emit('props-submit-error', e);
};

const propsPanelFormErrorHandler = (e: any) => {
  emit('props-form-error', e);
};

const layerNodeDblclickHandler = (event: MouseEvent, data: TreeNodeData) => {
  emit('layer-node-dblclick', event, data);
};

defineExpose(services);
</script>
