<template>
  <div class="meta-slot-container">
    <div class="label">使用插槽</div>
    <tiny-form ref="slotRef" class="slot-form" :model="slotList" label-width="0" inline>
      <div v-for="(slot, index) in slotList" :key="slot.name" class="use-slot">
        <div class="use-slot-item-name">{{ slot.name }}</div>
        <div class="use-slot-item-content">
          <div class="use-slot-switch-wrap">
            <div :class="['e__switch', { 'e_is-checked': slot.bind }]">
              <span class="e__switch-core" @click="toggleSlot(index, slot)"></span>
            </div>
            <tiny-tooltip effect="dark" :content="state.currentComponent?.content" placement="top">
              <span class="item-icon">
                <component :is="state.currentComponent?.icon"></component>
              </span>
            </tiny-tooltip>
          </div>
          <tiny-form-item
            :prop="paramsPropPath(index)"
            :rules="[{ validator: parmasStringValidator, trigger: 'blur' }]"
            class="slot-name-form-item"
          >
            <tiny-input
              v-model="slot.params"
              class="use-slot-params"
              @change="validParmas(slot, paramsPropPath(index))"
            ></tiny-input>
          </tiny-form-item>
        </div>
      </div>
    </tiny-form>
  </div>
</template>

<script>
import { ref, inject, watchEffect, reactive } from 'vue'
import { Input, Tooltip, Form, FormItem } from '@opentiny/vue'
import { useProperties, useCanvas, useModal } from '@opentiny/tiny-engine-controller'
import SvgICons from '@opentiny/vue-icon'
import { verifyJsVarName } from '@opentiny/tiny-engine-controller/js/verification'

export default {
  components: {
    TinyInput: Input,
    TinyForm: Form,
    TinyFormItem: FormItem,
    TinyTooltip: Tooltip
  },
  props: {
    modelValue: {
      type: Object
    },
    slots: {
      type: Array,
      default: () => []
    }
  },
  setup(props, { emit }) {
    const path = inject('path', '')
    const slotList = ref(
      props.slots.map((name) => {
        const slotInfo = props.modelValue?.[name]

        return {
          bind: Boolean(slotInfo),
          name,
          params: slotInfo?.params?.join(',') || ''
        }
      })
    )
    const componentsMap = {
      TinyGrid: {
        content:
          '暴露给插槽使用的变量，为解构的参数，可以使用多个用逗号分隔，如：row(行数据)，column(列数据)，$table(内部表格实例)，seq(序号)，cell(单元格)，columnIndex(列索引),rowIndex(行索引)',
        icon: SvgICons['IconUnknow']()
      }
    }

    const state = reactive({
      currentComponent: {}
    })

    const slotRef = ref(null)

    const paramsPropPath = (index) => `${index}.params`

    const parmasStringValidator = (rule, value, callback) => {
      if (value && value.split(',').some((parma) => !verifyJsVarName(parma))) {
        callback(new Error('仅支持JavaScript中有效的变量名'))
      } else {
        callback()
      }
    }

    const toggleSlot = (idx, { bind, name, params = '' }) => {
      const slotInfo = {
        [name]: {
          type: 'JSSlot',
          value: [
            {
              componentName: 'div'
            }
          ]
        }
      }

      const slotData = { ...slotInfo, ...(props.modelValue || {}) }

      if (params.length) {
        slotData[name].params = params.split(',')
      } else {
        delete slotData[name].params
      }
      if (bind) {
        useModal().confirm({
          title: '提示',
          message: '关闭后插槽内的内容将被清空，是否继续？',
          status: 'info',
          exec: () => {
            slotList.value[idx].bind = false
            delete slotData[name]
            emit('update:modelValue', slotData)
            const [propsName] = path.split('.')
            const schema = useProperties().getSchema()
            schema.props[propsName] = JSON.parse(JSON.stringify(schema.props[propsName]))
          },
          cancel: () => {}
        })
      } else {
        slotList.value[idx].bind = true
      }
      emit('update:modelValue', slotData)

      // 更新当前选中组件的根属性，不根新在jsslot中的数据非响应式
      const [propsName] = path.split('.')
      const schema = useProperties().getSchema()
      schema.props[propsName] = JSON.parse(JSON.stringify(schema.props[propsName]))
    }

    const setParams = (slot) => {
      slot.bind && toggleSlot(true, slot)
    }

    const validParmas = (slot, parmasPath) => {
      slotRef.value.validateField([parmasPath], (tips) => {
        if (!tips) {
          setParams(slot)
        }
      })
    }

    watchEffect(() => {
      const componentName = useCanvas().getCurrentSchema()?.componentName
      state.currentComponent = componentsMap[componentName]
    })

    return {
      toggleSlot,
      slotList,
      paramsPropPath,
      slotRef,
      parmasStringValidator,
      validParmas,
      setParams,
      state,
      componentsMap
    }
  }
}
</script>

<style lang="less" scoped>
.meta-slot-container {
  text-align: left;
  color: var(--lowcode-meta-js-slot-color);
}

.slot-form {
  margin-top: 8px;
}

.use-slot {
  display: flex;
  justify-content: center;
  flex-direction: column;
  row-gap: 8px;

  .use-slot-item-content {
    display: flex;
  }

  .use-slot-switch-wrap {
    display: flex;
  }

  &-item-name {
    width: 100px;
  }

  &-switch {
    width: 60px;
  }

  &-params {
    margin-left: 5px;
  }

  & + .use-slot {
    margin-top: 16px;
  }

  .slot-name-form-item {
    margin-bottom: 0;
  }

  .item-icon {
    margin: 3px 3px 0 6px;
  }
}

.e__switch {
  display: inline-flex;
  align-items: center;
  position: relative;
  font-size: 14px;
  line-height: 20px;
  height: 20px;
  vertical-align: middle;
  cursor: pointer;
}

.e__switch-core {
  margin: 0;
  position: relative;
  width: 60px;
  height: 20px;
  border: 1px solid var(--ti-lowcode-base-bg);
  outline: 0;
  border-radius: 10px;
  box-sizing: border-box;
  background: var(--ti-lowcode-base-bg);
  transition: border-color 0.3s, background-color 0.3s;
  vertical-align: middle;
}

.e__switch-core::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 1px;
  border-radius: 100%;
  transition: all 0.3s;
  width: 16px;
  height: 16px;
  background-color: #ffffff;
}

.e__switch.e_is-checked .e__switch-core {
  border-color: var(--ti-lowcode-base-blue-6);
  background-color: var(--ti-lowcode-base-blue-6);
}

.e__switch.e_is-checked .e__switch-core::after {
  left: 100%;
  margin-left: -17px;
}
</style>
