<script setup lang="ts">
import { h } from 'vue';

import { NumberOutlined } from '@ant-design/icons-vue';
import { Button, Flex } from 'ant-design-vue';
import { Sender } from 'ant-design-x-vue';

import { useMessagesContext } from '@/modules/chat/composables';
import { useGuideStore } from '@/stores';
import type { GuideUser } from '@/stores/guide.store.ts';
import type { MessageRendererEmits, MessageRendererProps } from '@/modules/chat/renderer/index.ts';
import type { BasicInfoPickerKey, BasicInfoPickerModel } from '@/modules/chat/types';
import type { BasicInfoPickerValue } from '@/modules/chat/config';
import { basicInfoPickerGroup } from '@/modules/chat/config';

const STRING_SEPARATOR = ', ';

defineOptions({ inheritAttrs: false });

interface Props extends MessageRendererProps {
  group?: Array<BasicInfoPickerKey | BasicInfoPickerValue>;
}

type Emits = MessageRendererEmits;

const { group = [], complete } = defineProps<Props>();
const emits = defineEmits<Emits>();

const open = defineModel('open', { default: false });
const senderInstance = useTemplateRef<AntXSenderInstance>('sender-ref');

const pickers = shallowRef<BasicInfoPickerValue[]>([]);
const pickerIndex = ref(0);
const picker = computed(() => pickers.value.at(pickerIndex.value));
watchEffect(() => {
  for (let item of group) {
    if (typeof item === 'string') item = Object.assign({ key: item }, basicInfoPickerGroup[item]);
    if (item) pickers.value.push(item);
  }
  triggerRef(pickers);
  pickerIndex.value = 0;
});

const pending = ref(false);
watchEffect(() => {
  pending.value = !complete;
  if (complete) {
    onTrigger(true);
    nextTick(() => senderInstance.value?.focus({ cursor: 'end' }));
  }
});

const guide = useGuideStore();
const { append } = useMessagesContext();

const { loading, model } = toRefs(reactive({ loading: false, model: {} as BasicInfoPickerModel }));

function onTrigger(key?: BasicInfoPickerKey | boolean) {
  if (key == null) open.value = !open.value;
  else if (typeof key === 'boolean') open.value = key;
  else {
    open.value = true;
    pickerIndex.value = pickers.value.findIndex((picker) => picker.key === key);
  }
}

const getDisplayValue = (values: string[] | string | undefined, columns: VantPickerColumn): string => {
  if (!Array.isArray(values)) values = values ? values.split(STRING_SEPARATOR) : [];
  return values
    .map((value, index) => {
      const column = Array.isArray(columns[index]) ? columns[index] : columns;
      return column.find((col) => col.value === value)?.text;
    })
    .join(STRING_SEPARATOR);
};

const displayValue = ref();

const pickerInstance = useTemplateRef<VantPickerInstance>('picker-ref');
const pickerProps = computed(() => {
  const { key, title, confirmButtonText = '确定', columns = [], defaultValue, clickOnConfirm = false, onConfirm } = picker.value ?? {};
  const value = model.value[key!] ?? defaultValue;

  displayValue.value = getDisplayValue(value, columns);

  return {
    title: typeof title === 'function' ? title(model.value) : title,
    columns,
    confirmButtonText,
    showToolbar: false,
    loading: pending.value,
    modelValue: value ? value.split(STRING_SEPARATOR) : [],
    'onUpdate:modelValue'(value) {
      displayValue.value = getDisplayValue(value, columns);
    },
    onClickOption(event) {
      if (clickOnConfirm) setTimeout(() => unref(pickerProps).onConfirm(event), 0);
    },
    async onConfirm(event) {
      model.value[key!] = event.selectedValues.join(STRING_SEPARATOR);
      if (await onConfirm?.(model)) {
        open.value = false;
        senderProps.value.onSubmit();
      } else {
        const index = pickerIndex.value + 1;
        if (index === pickers.value.length) {
          open.value = false;
          senderProps.value.onSubmit();
        } else pickerIndex.value = index;
      }
    },
  } satisfies VantPickProps;
});

const senderProps = computed(() => {
  return {
    readOnly: true,
    value: open.value ? displayValue.value : ' ',
    loading: loading.value,
    disabled: pending.value,
    onSubmit() {
      if (open.value) return pickerInstance.value?.confirm();
      const key = pickers.value.find((picker) => model.value[picker.key] == null)?.key;
      if (key) onTrigger(key);
      else {
        pickerIndex.value = pickers.value.length - 1;
        open.value = false;
        onSubmit(pickers.value.map((picker) => getDisplayValue(model.value[picker.key], picker.columns!)).join(STRING_SEPARATOR));
      }
    },
  } satisfies AntXSenderProps;
});

const senderHeaderProps = computed(() => {
  const { title, confirmButtonText } = pickerProps.value;
  return {
    open: open.value,
    title: title
      ? h(Flex, { justify: 'space-between' }, () => [
          h('span', null, title),
          !!confirmButtonText &&
            h(
              Button,
              {
                type: 'text',
                size: 'small',
                style: { color: '#1677ff' },
                onClick() {
                  pickerInstance.value?.confirm();
                },
              },
              () => confirmButtonText,
            ),
        ])
      : void 0,
  };
});

const prefixProps = computed(() => {
  return {
    type: 'text',
    icon: h(NumberOutlined),
    disabled: pending.value,
  } satisfies AntButtonProps;
});

async function onSubmit(content: string | VNode) {
  guide.updateUser(<GuideUser>model.value);
  append({ role: 'user', content });
  emits('next');
}
</script>

<template>
  <Sender ref="sender-ref" v-bind="senderProps">
    <template #header>
      <Sender.Header v-bind="senderHeaderProps">
        <van-picker ref="picker-ref" v-bind="pickerProps" :key="picker?.key" />
      </Sender.Header>
    </template>
    <template #prefix>
      <a-flex>
        <template v-for="(picker, i) in pickers" :key="picker.key">
          <a-button v-if="i <= pickerIndex" :class="{ active: open && pickerIndex === i }" v-bind="prefixProps" @click="onTrigger(picker.key)">
            {{ open && i === pickerIndex ? '' : getDisplayValue(model[picker.key], picker.columns!) }}
          </a-button>
        </template>
      </a-flex>
    </template>
  </Sender>
</template>

<style scoped lang="scss">
:deep(.ant-sender-prefix) {
  .ant-btn-text {
    &.active {
      color: #1677ff;
    }
  }
}

:deep(.ant-sender-header-content) {
  --van-picker-background: transparent;
  --van-picker-mask-color: transparent;
  --van-picker-group-background: transparent;
  --van-picker-loading-mask-color: transparent;

  .van-picker-column__item {
    &--selected {
      color: #1677ff;
    }
  }
}
</style>
