<template>
    <view :class="`wd-col-picker ${cell.border.value ? 'is-border' : ''} ${customClass}`" :style="customStyle">
        <view class="wd-col-picker__field" @click="showPicker">
            <slot v-if="useDefaultSlot"></slot>
            <view
                v-else
                :class="`wd-col-picker__cell ${disabled && 'is-disabled'} ${readonly && 'is-readonly'} ${
                    alignRight && 'is-align-right'
                } ${error && 'is-error'}  ${size && 'is-' + size}`"
            >
                <view
                    v-if="label || useLabelSlot"
                    :class="`wd-col-picker__label ${isRequired && 'is-required'} ${customLabelClass}`"
                    :style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
                >
                    <block v-if="label">{{ label }}</block>
                    <slot v-else name="label"></slot>
                </view>
                <view class="wd-col-picker__body">
                    <view class="wd-col-picker__value-wraper">
                        <view
                            :class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${
                                showValue ? '' : 'wd-col-picker__value--placeholder'
                            }`"
                        >
                            {{ showValue || placeholder || translate("placeholder") }}
                        </view>
                        <wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" />
                    </view>
                    <view v-if="errorMessage" class="wd-col-picker__error-message">{{ errorMessage }}</view>
                </view>
            </view>
        </view>
        <wd-action-sheet
            v-model="pickerShow"
            :duration="250"
            :title="title || translate('title')"
            :close-on-click-modal="closeOnClickModal"
            :z-index="zIndex"
            :safe-area-inset-bottom="safeAreaInsetBottom"
            @open="handlePickerOpend"
            @close="handlePickerClose"
        >
            <view class="wd-col-picker__selected">
                <scroll-view :scroll-x="true" scroll-with-animation :scroll-left="scrollLeft">
                    <view class="wd-col-picker__selected-container">
                        <view
                            v-for="(select, colIndex) in selectList"
                            :key="colIndex"
                            :class="`wd-col-picker__selected-item  ${colIndex === currentCol && 'is-selected'}`"
                            @click="handleColClick(colIndex)"
                        >
                            {{ selectShowList[colIndex] || translate("select") }}
                        </view>
                        <view class="wd-col-picker__selected-line" :style="lineStyle"></view>
                    </view>
                </scroll-view>
            </view>
            <view class="wd-col-picker__list-container">
                <view
                    v-for="(col, colIndex) in selectList"
                    :key="colIndex"
                    class="wd-col-picker__list"
                    :style="colIndex === currentCol ? 'display: block;' : 'display: none;'"
                >
                    <view
                        v-for="(item, index) in col"
                        :key="index"
                        :class="`wd-col-picker__list-item ${
                            pickerColSelected[colIndex] && item[valueKey] === pickerColSelected[colIndex] && 'is-selected'
                        } ${item.disabled && 'is-disabled'}`"
                        @click="chooseItem(colIndex, index)"
                    >
                        <view>
                            <view class="wd-col-picker__list-item-label">{{ item[labelKey] }}</view>
                            <view v-if="item[tipKey]" class="wd-col-picker__list-item-tip">{{ item[tipKey] }}</view>
                        </view>
                        <wd-icon custom-class="wd-col-picker__checked" name="check"></wd-icon>
                    </view>
                    <view v-if="loading" class="wd-col-picker__loading">
                        <wd-loading :color="loadingColor" />
                    </view>
                </view>
            </view>
        </wd-action-sheet>
    </view>
</template>
<script lang="ts">
export default {
    name: "wd-col-picker",
    options: {
        addGlobalClass: true,
        virtualHost: true,
        styleIsolation: "shared"
    }
};
</script>

<script lang="ts" setup>
import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
import { debounce, getRect, isArray, isBoolean, isFunction } from "wot-design-uni/components/common/util";
import { useCell } from "wot-design-uni/components/composables/useCell";
import { FORM_KEY, type FormItemRule } from "wot-design-uni/components/wd-form/types";
import { useParent } from "wot-design-uni/components/composables/useParent";
import { useTranslate } from "wot-design-uni/components/composables/useTranslate";
import { colPickerProps, type ColPickerExpose } from "./types";

const { translate } = useTranslate("col-picker");

const $container = ".wd-col-picker__selected-container";
const $item = ".wd-col-picker__selected-item";

const props = defineProps(colPickerProps);
const emit = defineEmits(["close", "update:modelValue", "confirm"]);

const pickerShow = ref<boolean>(false);
const currentCol = ref<number>(0);
const selectList = ref<Record<string, any>[][]>([]);
const pickerColSelected = ref<(string | number)[]>([]);
const selectShowList = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const isChange = ref<boolean>(false);
const lastSelectList = ref<Record<string, any>[][]>([]);
const lastPickerColSelected = ref<(string | number)[]>([]);
const lineStyle = ref<string>("");
const scrollLeft = ref<number>(0);
const inited = ref<boolean>(false);
const isCompleting = ref<boolean>(false);
const { proxy } = getCurrentInstance() as any;

const cell = useCell();

const updateLineAndScroll = debounce(function (animation = true) {
    setLineStyle(animation);
    lineScrollIntoView();
}, 50);

const showValue = computed(() => {
    const selectedItems = (props.modelValue || []).map((item, colIndex) => {
        return getSelectedItem(item, colIndex, selectList.value);
    });

    if (props.displayFormat) {
        return props.displayFormat(selectedItems);
    } else {
        return selectedItems
            .map(item => {
                return item[props.labelKey];
            })
            .join("");
    }
});

watch(
    () => props.modelValue,
    newValue => {
        if (newValue === pickerColSelected.value) return;
        pickerColSelected.value = newValue;
        newValue.map((item, colIndex) => {
            return getSelectedItem(item, colIndex, selectList.value)[props.labelKey];
        });
        handleAutoComplete();
    },
    {
        deep: true,
        immediate: true
    }
);

watch(
    () => props.columns,
    (newValue, oldValue) => {
        if (newValue.length && !isArray(newValue[0])) {
            console.error(
                "[wot design] error(wd-col-picker): the columns props of wd-col-picker should be a two-dimensional array"
            );
            return;
        }
        if (newValue.length === 0 && !oldValue) return;

        const newSelectedList = newValue.slice(0);

        selectList.value = newSelectedList;

        selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
            return getSelectedItem(item, colIndex, newSelectedList)[props.labelKey];
        });
        lastSelectList.value = newSelectedList;

        if (newSelectedList.length > 0) {
            currentCol.value = newSelectedList.length - 1;
        }
    },
    {
        deep: true,
        immediate: true
    }
);

watch(
    () => props.columnChange,
    fn => {
        if (fn && !isFunction(fn)) {
            console.error("The type of columnChange must be Function");
        }
    },
    {
        deep: true,
        immediate: true
    }
);

watch(
    () => props.displayFormat,
    fn => {
        if (fn && !isFunction(fn)) {
            console.error("The type of displayFormat must be Function");
        }
    },
    {
        deep: true,
        immediate: true
    }
);

watch(
    () => props.beforeConfirm,
    fn => {
        if (fn && !isFunction(fn)) {
            console.error("The type of beforeConfirm must be Function");
        }
    },
    {
        deep: true,
        immediate: true
    }
);

const { parent: form } = useParent(FORM_KEY);

// 表单校验错误信息
const errorMessage = computed(() => {
    if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
        return form.errorMessages[props.prop];
    } else {
        return "";
    }
});

// 是否展示必填
const isRequired = computed(() => {
    let formRequired = false;
    if (form && form.props.rules) {
        const rules = form.props.rules;
        for (const key in rules) {
            if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
                formRequired = rules[key].some((rule: FormItemRule) => rule.required);
            }
        }
    }
    return props.required || props.rules.some(rule => rule.required) || formRequired;
});

onMounted(() => {
    inited.value = true;
});

// 打开弹框
function open() {
    showPicker();
}
// 关闭弹框
function close() {
    handlePickerClose();
}
function handlePickerOpend() {
    updateLineAndScroll(false);
}

function handlePickerClose() {
    pickerShow.value = false;
    // 如果目前用户正在选择，需要在popup关闭时将数据重置回上次数据，popup 关闭时间 250
    if (isChange.value) {
        setTimeout(() => {
            selectList.value = lastSelectList.value.slice(0);
            pickerColSelected.value = lastPickerColSelected.value.slice(0);
            selectShowList.value = lastPickerColSelected.value.map((item, colIndex) => {
                return getSelectedItem(item, colIndex, lastSelectList.value)[props.labelKey];
            });
            currentCol.value = lastSelectList.value.length - 1;
            isChange.value = false;
        }, 250);
    }
    emit("close");
}
function showPicker() {
    const { disabled, readonly } = props;

    if (disabled || readonly) return;
    pickerShow.value = true;
    lastPickerColSelected.value = pickerColSelected.value.slice(0);
    lastSelectList.value = selectList.value.slice(0);
}

function getSelectedItem(value: string | number, colIndex: number, selectList: Record<string, any>[][]) {
    const { valueKey, labelKey } = props;
    if (selectList[colIndex]) {
        const selecteds = selectList[colIndex].filter(item => {
            return item[valueKey] === value;
        });

        if (selecteds.length > 0) {
            return selecteds[0];
        }
    }

    return {
        [valueKey]: value,
        [labelKey]: ""
    };
}

function chooseItem(colIndex: number, index: number) {
    const item = selectList.value[colIndex][index];
    if (item.disabled) return;

    const newPickerColSelected = pickerColSelected.value.slice(0, colIndex);
    newPickerColSelected.push(item[props.valueKey]);
    isChange.value = true;
    pickerColSelected.value = newPickerColSelected;
    selectList.value = selectList.value.slice(0, colIndex + 1);
    selectShowList.value = newPickerColSelected.map((item, colIndex) => {
        return getSelectedItem(item, colIndex, selectList.value)[props.labelKey];
    });
    handleColChange(colIndex, item, index);
}
function handleColChange(colIndex: number, item: Record<string, any>, index: number, callback?: () => void) {
    loading.value = true;
    const { columnChange, beforeConfirm } = props;
    columnChange &&
        columnChange({
            selectedItem: item,
            index: colIndex,
            rowIndex: index,
            resolve: (nextColumn: Record<string, any>[]) => {
                if (!isArray(nextColumn)) {
                    console.error(
                        "[wot design] error(wd-col-picker): the data of each column of wd-col-picker should be an array"
                    );
                    return;
                }

                const newSelectList = selectList.value.slice(0);
                newSelectList[colIndex + 1] = nextColumn;

                selectList.value = newSelectList;
                loading.value = false;
                currentCol.value = colIndex + 1;

                updateLineAndScroll(true);
                if (typeof callback === "function") {
                    isCompleting.value = false;
                    selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
                        return getSelectedItem(item, colIndex, selectList.value)[props.labelKey];
                    });
                    callback();
                }
            },
            finish: (isOk?: boolean) => {
                // 每设置展示数据回显
                if (typeof callback === "function") {
                    loading.value = false;
                    isCompleting.value = false;
                    return;
                }
                if (isBoolean(isOk) && !isOk) {
                    loading.value = false;
                    return;
                }

                if (beforeConfirm) {
                    beforeConfirm(
                        pickerColSelected.value,
                        pickerColSelected.value.map((item, colIndex) => {
                            return getSelectedItem(item, colIndex, selectList.value);
                        }),
                        (isPass: boolean) => {
                            if (isPass) {
                                onConfirm();
                            } else {
                                loading.value = false;
                            }
                        }
                    );
                } else {
                    onConfirm();
                }
            }
        });
}
function onConfirm() {
    isChange.value = false;
    loading.value = false;
    pickerShow.value = false;

    emit("update:modelValue", pickerColSelected.value);
    emit("confirm", {
        value: pickerColSelected.value,
        selectedItems: pickerColSelected.value.map((item, colIndex) => {
            return getSelectedItem(item, colIndex, selectList.value);
        })
    });
}
function handleColClick(index: number) {
    isChange.value = true;
    currentCol.value = index;
    updateLineAndScroll(true);
}
/**
 * @description 更新navBar underline的偏移量
 * @param {Boolean} animation 是否伴随动画
 */
function setLineStyle(animation = true) {
    if (!inited.value) return;
    getRect($item, true, proxy).then(rects => {
        const rect = rects[currentCol.value];
        // const width = lineWidth || (slidableNum < items.length ? rect.width : (rect.width - 14))
        const width = 16;
        let left = rects.slice(0, currentCol.value).reduce((prev: any, curr: any) => prev + curr.width, 0);
        left += (Number(rect.width) - width) / 2;
        const transition = animation ? "transition: width 300ms ease, transform 300ms ease;" : "";

        const lineStyleTemp = `
          transform: translateX(${left}px);
          ${transition}
        `;
        // 防止重复绘制
        if (lineStyle.value !== lineStyleTemp) {
            lineStyle.value = lineStyleTemp;
        }
    });
}
/**
 * @description scroll-view滑动到active的tab_nav
 */
function lineScrollIntoView() {
    if (!inited.value) return;
    Promise.all([getRect($item, true, proxy), getRect($container, false, proxy)]).then(([navItemsRects, navRect]) => {
        if (!isArray(navItemsRects) || navItemsRects.length === 0) return;
        // 选中元素
        const selectItem = navItemsRects[currentCol.value];
        // 选中元素之前的节点的宽度总和
        const offsetLeft = navItemsRects.slice(0, currentCol.value).reduce((prev, curr) => prev + Number(curr.width), 0);
        // scroll-view滑动到selectItem的偏移量
        scrollLeft.value = offsetLeft - ((navRect as any).width - Number(selectItem.width)) / 2;
    });
}

// 递归列数据补齐
function diffColumns(colIndex: number) {
    // colIndex 为 -1 时，item 为空对象，>=0 时则具有 value 属性
    const item = colIndex === -1 ? {} : { [props.valueKey]: props.modelValue[colIndex] };
    handleColChange(colIndex, item, -1, () => {
        // 如果 columns 长度还小于 value 长度，colIndex + 1，继续递归补齐
        if (selectList.value.length < props.modelValue.length) {
            diffColumns(colIndex + 1);
        }
    });
}
function handleAutoComplete() {
    if (props.autoComplete) {
        // 如果 columns 数组长度为空，或者长度小于 value 的长度，自动触发 columnChange 来补齐数据
        if (selectList.value.length < props.modelValue.length || selectList.value.length === 0) {
            // isCompleting 是否在自动补全，锁操作
            if (!isCompleting.value) {
                // 如果 columns 长度为空，则传入的 colIndex 为 -1
                const colIndex = selectList.value.length === 0 ? -1 : selectList.value.length - 1;
                diffColumns(colIndex);
            }
            isCompleting.value = true;
        }
    }
}

defineExpose<ColPickerExpose>({
    close,
    open
});
</script>

<style lang="scss" scoped>
@import "./index.scss";
</style>
