<template>
  <Teleport to="body" v-if="visible">
    <div ref="target" class="m-editor-float-box" :style="style">
      <div ref="dragTarget" class="m-editor-float-box-title">
        <slot name="title">
          <span>{{ title }}</span>
        </slot>
        <div>
          <TMagicButton link size="small" :icon="Close" @click="closeHandler"></TMagicButton>
        </div>
      </div>
      <div class="m-editor-float-box-body">
        <slot name="body"></slot>
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import { Close } from '@element-plus/icons-vue';
import VanillaMoveable from 'moveable';

import { TMagicButton } from '@tmagic/design';

interface Position {
  left: number;
  top: number;
}

interface Rect {
  width: number | string;
  height: number | string;
}

const props = withDefaults(defineProps<{ visible: boolean; position?: Position; rect?: Rect; title?: string }>(), {
  visible: false,
  title: '',
  position: () => ({ left: 0, top: 0 }),
  rect: () => ({ width: 'auto', height: 'auto' }),
});

const emit = defineEmits<{
  'update:visible': [boolean];
}>();

const target = ref<HTMLDivElement>();
const dragTarget = ref<HTMLDivElement>();

const style = computed(() => ({
  left: `${props.position.left}px`,
  top: `${props.position.top}px`,
  width: typeof props.rect.width === 'string' ? props.rect.width : `${props.rect.width}px`,
  height: typeof props.rect.height === 'string' ? props.rect.height : `${props.rect.height}px`,
}));

let moveable: VanillaMoveable | null = null;

const initMoveable = () => {
  moveable = new VanillaMoveable(globalThis.document.body, {
    className: 'm-editor-floating-box-moveable',
    target: target.value,
    draggable: true,
    resizable: true,
    edge: true,
    keepRatio: false,
    origin: false,
    snappable: true,
    dragTarget: dragTarget.value,
    dragTargetSelf: false,
    linePadding: 10,
    controlPadding: 10,
  });

  moveable.on('drag', (e) => {
    e.target.style.transform = e.transform;
  });

  moveable.on('resize', (e) => {
    e.target.style.width = `${e.width}px`;
    e.target.style.height = `${e.height}px`;
    e.target.style.transform = e.drag.transform;
  });
};

const destroyMoveable = () => {
  moveable?.destroy();
  moveable = null;
};

watch(
  () => props.visible,
  async (visible) => {
    if (visible) {
      await nextTick();
      initMoveable();
    } else {
      destroyMoveable();
    }
  },
  {
    immediate: true,
  },
);

onBeforeUnmount(() => {
  destroyMoveable();
});

const closeHandler = () => {
  emit('update:visible', false);
};

defineExpose({
  target,
});
</script>
