<template>
  <div class="x-transfer-wrapper">
    <div class="transfer-header">
      <slot name="title">
        <span class="header-title">{{ title }}</span>
      </slot>
      <div class="header-radio" v-if="showRadio">
        <a-radio-group
          v-model="radioValue"
          :size="radioConfig.size || 'default'"
          @change="handleRadioChange"
        >
          <a-radio
            v-for="option in radioOptions"
            :key="option.value"
            :value="option.value"
          >
            {{ option.label }}
          </a-radio>
        </a-radio-group>
      </div>
    </div>
    <div class="transfer-content">
      <div class="x-transfer-component">
        <a-spin :spinning="loading">
          <div class="x-transfer-container">
            <!-- 左侧：可选项列表 -->
            <div class="transfer-panel left-panel">
              <div class="panel-header" v-if="showLeftHeader">
                <span class="panel-title">{{ computedLeftTitle }}</span>
              </div>
              <div class="panel-body">
                <!-- 分组列表 -->
                <template v-if="localGroupDataSource.length > 0">
                  <div
                    v-for="group in localGroupDataSource"
                    :key="group.key"
                    class="group-section"
                  >
                    <div class="group-label-row" @click="toggleGroup(group.key)">
                      <span class="group-label">{{ group.label }}</span>
                      <a-icon
                        type="right"
                        class="item-icon icon-right"
                        @click.stop="addGroupItems(group)"
                      />
                    </div>
                    <div class="group-items" v-show="isGroupExpanded(group.key)">
                      <div
                        v-for="item in group.items"
                        :key="item.key"
                        class="item-row item-row-left"
                        :class="{ selected: localValue.includes(item.key) }"
                        :title="item.title"
                        @dblclick="addItem(item.key)"
                      >
                        <span class="item-title">{{ item.title }}</span>
                        <a-icon type="right" class="item-icon icon-right" @click.stop="addItem(item.key)"/>
                      </div>
                    </div>
                  </div>
                </template>

                <!-- 简单列表 -->
                <template v-else>
                  <div
                    v-for="item in localDataSource"
                    :key="item.key"
                    class="item-row"
                    :class="{ selected: localValue.includes(item.key) }"
                    :title="item.title"
                    @dblclick="addItem(item.key)"
                  >
                    <span class="item-title">{{ item.title }}</span>
                    <a-icon type="right" class="item-icon icon-right" @click.stop="addItem(item.key)"/>
                  </div>
                </template>

                <a-empty v-if="localDataSource.length === 0 && !loading"/>
              </div>
            </div>

            <!-- 中间：操作按钮容器（上下结构） -->
            <div class="transfer-actions">
              <div class="action-box" @click="selectAll">
                <span class="action-text">{{ computedAllText }}</span>
                <a-icon class="action-icon action-icon-right" type="right"/>
              </div>
              <div class="action-box" @click="clearAll">
                <a-icon class="action-icon action-icon-left" type="left"/>
                <span class="action-text">{{ computedAllText }}</span>
              </div>
            </div>

            <!-- 右侧：已选列表 -->
            <div class="transfer-panel right-panel">
              <div class="panel-header" v-if="showRightHeader">
                <span v-if="computedRightTitle" class="panel-title">{{ computedRightTitle }}</span>
              </div>
              <div class="panel-body">
                <div
                  v-for="key in localValue"
                  :key="key"
                  class="item-row selected-item"
                  :title="getItemTitle(key)"
                  @dblclick="removeItem(key)"
                >
                  <a-icon type="left" class="item-icon icon-left" @click.stop="removeItem(key)"/>
                  <span class="item-title">{{ getItemTitle(key) }}</span>
                </div>
                <a-empty v-if="localValue.length === 0"/>
              </div>
            </div>
          </div>
        </a-spin>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { runLogic, getConfigByName } from '@vue2-client/services/api/common'

const props = defineProps({
  queryParamsName: { type: String, default: '' },
  configName: { type: String, default: '' },
  serverName: { type: String, default: 'af-his' },
  logicName: { type: String, default: '' },
  parameter: { type: Object, default: () => ({}) },
  rowKey: { type: String, default: 'key' },
  displayKey: { type: String, default: 'title' },
  groupKey: { type: String, default: 'group' },
  groupLabelKey: { type: String, default: 'label' },
  groupItemsKey: { type: String, default: 'items' },
  leftTitle: { type: String, default: '' },
  rightTitle: { type: String, default: '' },
  allText: { type: String, default: 'All' },
  addHint: { type: String, default: '双击添加' },
  removeHint: { type: String, default: '双击移除' },
  initialValue: { type: Array, default: () => [] },
  disabled: { type: Boolean, default: false },
  data: { type: Array, default: null },
  config: { type: Object, default: null },
  env: { type: String, default: 'prod' },
  radioConfig: { type: Object, default: null }
})

const emit = defineEmits(['update:value', 'change', 'loaded', 'radioChange'])

const loading = ref(false)
const localValue = ref([])
const localDataSource = ref([])
const localGroupDataSource = ref([])
const localConfig = ref(null)
const expandedGroups = ref([])
const radioValue = ref(null)

const isDev = computed(() => props.env !== 'prod')

const getConfig = (key, defaultValue) => {
  if (props.config && props.config[key] !== undefined) {
    return props.config[key]
  }
  if (localConfig.value && localConfig.value[key] !== undefined) {
    return localConfig.value[key]
  }
  if (props[key] !== undefined) {
    return props[key]
  }
  return defaultValue
}

const logicName = computed(() => getConfig('logicName', props.logicName || ''))
const queryParamsName = computed(() => getConfig('queryParamsName', props.queryParamsName || props.configName || ''))
const parameter = computed(() => getConfig('parameter', props.parameter || {}))

const computedAllText = computed(() => getConfig('allText', 'All'))
const computedLeftTitle = computed(() => getConfig('leftTitle', ''))
const computedRightTitle = computed(() => getConfig('rightTitle', ''))
const computedDisabled = computed(() => getConfig('disabled', false))
const title = computed(() => getConfig('title', ''))
const computedInitialValue = computed(() => getConfig('initialValue', []))

const showLeftHeader = computed(() => !!computedLeftTitle.value)
const showRightHeader = computed(() => !!computedRightTitle.value)

const showRadio = computed(() => {
  const config = radioConfig.value || localConfig.value?.radioConfig
  return config && config.options && Array.isArray(config.options) && config.options.length > 0
})

const radioOptions = computed(() => {
  const config = radioConfig.value || localConfig.value?.radioConfig
  if (!config || !config.options) return []
  return config.options
})

const radioConfig = computed(() => {
  if (props.radioConfig) return props.radioConfig
  if (localConfig.value?.radioConfig) return localConfig.value.radioConfig
  return null
})

const initRadioValue = () => {
  if (!showRadio.value) return
  const config = radioConfig.value
  if (config?.default !== undefined && config.default !== null) {
    radioValue.value = config.default
  } else if (radioOptions.value.length > 0) {
    radioValue.value = radioOptions.value[0].value
  }
}

const handleRadioChange = (e) => {
  const radioVal = e.target.value
  emit('radioChange', {
    value: radioVal,
    options: radioOptions.value
  })
  if (radioConfig.value?.onChange) {
    radioConfig.value.onChange(radioVal)
  }
  if (radioConfig.value?.reloadLogic !== false && logicName.value) {
    reloadWithRadioValue(radioVal)
  }
}

const reloadWithRadioValue = async (radioVal) => {
  loading.value = true
  try {
    const config = localConfig.value || {}
    const logic = config.logicName || logicName.value
    const baseParams = config.parameter || parameter.value
    const radioParamsKey = radioConfig.value?.paramKey || 'radioValue'
    const params = { ...baseParams, [radioParamsKey]: radioVal }

    console.log('[XTransfer] reloadByRadio:', logic, params, props.serverName)

    const result = await loadByLogicWithConfig({ ...config, parameter: params })
    const { data, initialValue: logicInitialValue } = result

    parseData(data)

    const initValue = logicInitialValue.length > 0 ? logicInitialValue : computedInitialValue.value
    localValue.value = [...initValue]

    emit('loaded', {
      dataSource: localDataSource.value,
      groupDataSource: localGroupDataSource.value,
      initialValue: initValue,
      radioValue: radioVal
    })
  } catch (e) {
    console.error('[XTransfer] reload error:', e)
  } finally {
    loading.value = false
  }
}

watch(() => getConfig('value', undefined), (newVal) => {
  if (newVal !== undefined && JSON.stringify(newVal) !== JSON.stringify(localValue.value)) {
    localValue.value = [...newVal]
  }
}, { immediate: true })

watch(localValue, (newVal) => {
  emit('update:value', newVal)
})

const loadData = async () => {
  loading.value = true
  try {
    await loadByConfig()
  } catch (e) {
    console.error('[XTransfer] Load error:', e)
  } finally {
    loading.value = false
  }
}

const loadByConfig = () => {
  return new Promise((resolve, reject) => {
    console.log('[XTransfer] getConfigByName:', queryParamsName.value, props.serverName)
    getConfigByName(queryParamsName.value, props.serverName, async (config) => {
      console.log('[XTransfer] config loaded:', config)
      if (!config) {
        console.error('[XTransfer] config is null')
        loading.value = false
        resolve()
        return
      }

      localConfig.value = config

      initRadioValue()

      if (config.logicName) {
        try {
          const result = await loadByLogicWithConfig(config)
          const { data, initialValue: logicInitialValue } = result

          parseData(data)

          const initValue = logicInitialValue.length > 0 ? logicInitialValue : computedInitialValue.value
          if (initValue.length > 0) {
            localValue.value = [...initValue]
          }

          emit('loaded', {
            dataSource: localDataSource.value,
            groupDataSource: localGroupDataSource.value,
            initialValue: initValue
          })
        } catch (e) {
          console.error('[XTransfer] Load error:', e)
        }
      } else if (config.data && Array.isArray(config.data)) {
        parseData(config.data)
        if (computedInitialValue.value.length > 0) {
          localValue.value = [...computedInitialValue.value]
        }
      }

      loading.value = false
      resolve()
    }, isDev.value)
  })
}

const parseData = (data) => {
  if (!data || !Array.isArray(data)) {
    localDataSource.value = []
    localGroupDataSource.value = []
    return
  }

  const rowKey = getConfig('rowKey', 'key')
  const groupKey = getConfig('groupKey', 'group')
  const groupLabelKey = getConfig('groupLabelKey', 'label')
  const groupItemsKey = getConfig('groupItemsKey', 'items')

  const firstItem = data[0]
  const hasGroup = firstItem && (firstItem[groupKey] || firstItem[groupLabelKey])

  console.log('[XTransfer] parseData hasGroup:', hasGroup)

  if (hasGroup) {
    localGroupDataSource.value = data.map((group, index) => ({
      key: group[rowKey] || group.key || `group-${index}`,
      label: group[groupLabelKey] || group.label || '未命名',
      items: transformItems(group[groupItemsKey] || group.items || []),
      _rawData: group
    }))

    localDataSource.value = []
    data.forEach(group => {
      const items = group[groupItemsKey] || group.items || []
      localDataSource.value.push(...transformItems(items))
    })
  } else {
    localDataSource.value = transformItems(data)
    localGroupDataSource.value = []
  }
}

const loadByLogicWithConfig = async (config) => {
  const logic = config.logicName || logicName.value
  const params = config.parameter || parameter.value
  const server = config.serverName || props.serverName

  console.log('[XTransfer] loadByLogic:', logic, params, server)

  try {
    const res = await runLogic(logic, params, server, isDev.value)
    console.log('[XTransfer] logic result:', res)

    if (res === null || res === undefined) {
      return {data: [], initialValue: []}
    }

    if (Array.isArray(res)) {
      return {data: res, initialValue: []}
    }

    if (typeof res === 'object') {
      return {
        data: res.data || res.result || [],
        initialValue: res.initialValue || res.selected || res.defaultValue || []
      }
    }

    return {data: [], initialValue: []}
  } catch (e) {
    console.error('[XTransfer] logic error:', e)
    return {data: [], initialValue: []}
  }
}

const transformItems = (items) => {
  if (!items || !Array.isArray(items)) return []

  const rowKey = getConfig('rowKey', 'key')
  const displayKey = getConfig('displayKey', 'title')

  return items.map((item, index) => ({
    key: String(getNestedValue(item, rowKey) || item.key || `item-${index}`),
    title: String(getNestedValue(item, displayKey) || item.title || `选项${index + 1}`),
    disabled: item.disabled === true,
    _rawData: item
  }))
}

const getNestedValue = (obj, path) => {
  if (!path || !obj) return null
  return path.split('.').reduce((acc, part) => {
    if (acc === null || acc === undefined) return null
    return acc[part]
  }, obj)
}

const addItem = (key) => {
  if (computedDisabled.value) return
  if (!localValue.value.includes(key)) {
    localValue.value = [...localValue.value, key]
    emitChange()
  }
}

const addGroupItems = (group) => {
  if (computedDisabled.value) return
  const keys = group.items.map(item => item.key)
  const newKeys = keys.filter(key => !localValue.value.includes(key))
  if (newKeys.length > 0) {
    localValue.value = [...localValue.value, ...newKeys]
    emitChange()
  }
}

const toggleGroup = (groupKey) => {
  const index = expandedGroups.value.indexOf(groupKey)
  if (index > -1) {
    expandedGroups.value.splice(index, 1)
  } else {
    expandedGroups.value.push(groupKey)
  }
}

const isGroupExpanded = (groupKey) => {
  return expandedGroups.value.includes(groupKey)
}

const removeItem = (key) => {
  if (computedDisabled.value) return
  localValue.value = localValue.value.filter(k => k !== key)
  emitChange()
}

const selectAll = () => {
  if (computedDisabled.value) return
  const allKeys = localDataSource.value.map(item => item.key)
  localValue.value = [...allKeys]
  emitChange()
}

const clearAll = () => {
  if (computedDisabled.value) return
  localValue.value = []
  emitChange()
}

const emitChange = () => {
  emit('change', {
    value: localValue.value,
    data: getSelectedData()
  })
}

const getSelectedData = () => {
  return localValue.value.map(key => {
    const item = localDataSource.value.find(d => d.key === key)
    return item?._rawData
  }).filter(Boolean)
}

const getItemTitle = (key) => {
  const item = localDataSource.value.find(d => d.key === key)
  return item?.title || key
}

const init = (config) => {
  console.log('[XTransfer] init with config:', config)
  if (config) {
    localConfig.value = config
    loadData()
  }
}

const refresh = () => {
  loadData()
}

defineExpose({
  loadData,
  init,
  refresh,
  getSelectedData,
  getSelectedKeys: () => [...localValue.value],
  clear: clearAll,
  setValue: (keys) => {
    localValue.value = keys
  }
})

onMounted(() => {
  console.log('[XTransfer] mounted, queryParamsName:', props.queryParamsName, 'logicName:', props.logicName)
  loadData()
})

watch(() => [props.queryParamsName, props.logicName, props.config, props.data], () => {
  loadData()
}, {deep: true})
</script>

<style scoped>
.x-transfer-wrapper {
  width: 100%;
  height: 189px;
  border: none;
  background: transparent;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
.header-title {
  opacity: 1;

  font-family: Source Han Sans;
  font-size: 16px;
  font-weight: normal;
  line-height: 39px;
  letter-spacing: 0em;

  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  color: #313131;
}

.transfer-header {
  height: 41px;
  display: flex;
  align-items: center;
  padding-left: 24px;
  flex-shrink: 0;
  box-sizing: border-box;
  gap: 16px;
}

.header-radio {
  display: flex;
  align-items: center;
}

.header-radio :deep(.ant-radio-group) {
  display: flex;
  align-items: center;
  gap: 4px;
}

.header-radio :deep(.ant-radio-wrapper) {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 37px;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  color: #313131;
}

.header-radio :deep(.ant-radio-inner) {
  width: 16px;
  height: 16px;
  background: #FFFFFF;
  border: 1px solid #CDCFD4;
  box-sizing: border-box;
}

.header-radio :deep(.ant-radio-inner::after) {
  background: #0057FE;
  width: 10px;
  height: 10px;
}

.header-radio :deep(.ant-radio-checked .ant-radio-inner) {
  border-color: #CDCFD4;
}

.header-radio :deep(.ant-radio-button-wrapper) {
  height: 28px;
  line-height: 26px;
  font-size: 14px;
  padding: 0 12px;
}

.transfer-content {
  flex: 1;
  padding-left: 99px;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
}

.x-transfer-component {
  width: 100%;
  font-family: 'Source Han Sans';
}

.x-transfer-container {
  display: flex !important;
  flex-direction: row !important;
  gap: 0px;
  align-items: flex-start;
  width: 100%;
  height: 100%;
}

.transfer-panel {
  width: 324px;
  height: 143px;
  border: 1px solid #E5E9F0;
  border-radius: 6px;
  background: #fff;
  flex: 0 0 auto;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: 4px 0px 4px 8px;
}

.panel-header {
  padding: 0 12px;
  background: #fafafa;
  border-bottom: 1px solid #E5E9F0;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 37px;
  box-sizing: border-box;
  flex-shrink: 0;
}

.panel-title {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 37px;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  color: #313131;
}

.panel-count {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 37px;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  color: #313131;
  background: #f0f0f0;
  padding: 0 8px;
  border-radius: 10px;
}

.panel-count.selected {
  background: #1890ff;
  color: #fff;
}

.hint {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 37px;
  letter-spacing: 0em;
  font-feature-settings: "kern" on;
  color: #94979E;
  width: 100%;
  text-align: center;
}

.transfer-actions {
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.action-box {
  width: 68px;
  height: 78px;
  border-radius: 6px;
  opacity: 1;
  background: #FFFFFF;
  box-sizing: border-box;
  border: 1px solid #E5E9F0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  margin: 3px 5px;
}

.action-box + .action-box {
  margin-top: 0;
}

.action-text {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 1;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  color: #313131;
}

.action-arrow {
  width: 9.51px;
  height: 12.26px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.action-arrow svg {
  width: 100%;
  height: 100%;
}

.action-arrow.left {
  transform: rotate(180deg);
}

.action-box:hover {
  opacity: 0.7;
}

.action-box.disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.panel-body {
  padding: 0;
  overflow-y: auto;
  flex: 1;
  min-height: 100%;
}

.group-section:last-child {
  margin-bottom: 0;
}

.group-label-row {
  display: flex;
  align-items: center;
  height: 37px;
  line-height: 37px;
  padding: 0; /* 添加左右内边距 */
  box-sizing: border-box;
}

.group-label {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  color: #313131;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-items {
  overflow: hidden;
}

.item-row {
  border-bottom: 1px solid #E5E9F0;
  cursor: pointer;
  display: flex;
  align-items: center;
  transition: background-color 0.15s;
  user-select: none;
  height: 23px;
  line-height: 23px;
  box-sizing: border-box;
}

.item-row-left {
  padding-left: 12px;
}

.item-row:last-child {
  border-bottom: none;
}

.item-row.selected {
  color: #0057FE;
}

.item-title {
  font-family: 'Source Han Sans';
  font-size: 16px;
  font-weight: normal;
  line-height: 37px;
  letter-spacing: 0em;
  font-variation-settings: "opsz" auto;
  font-feature-settings: "kern" on;
  opacity: 1;
  flex: 1;
  color: #313131;
  flex-shrink: 0;
}

.item-icon {
  width: 8px;
  height: 13px;
  color: #94979E;
  flex-shrink: 0;
  opacity: 1;
}

.action-icon {
  width: 9px;
  height: 13px;
  color: #94979E;
  flex-shrink: 0;
  opacity: 1;
}

.action-icon-right {
  padding-left: 4px;
}

.action-icon-left {
  padding-right: 16px;
}

.icon-left {
  margin-right: 17px;
}

.icon-right {
  margin-right: 9px;
}

.selected-item {
  background: #fff;
  border-color: #E5E9F0;
}

.selected-item:hover {
  background: #f5f5f5 !important;
}

*::-webkit-scrollbar {
  width: 5px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: #E5E5E5;
  border-radius: 25px;
}

*::-webkit-scrollbar-thumb:hover {
  background: #D0D0D0;
}

* {
  scrollbar-width: thin;
  scrollbar-color: #E5E5E5 transparent;
}
</style>
