﻿<template>
    <view
        class="verify-code-input"
        @tap="focusInput"
        :style="{
            '--vci-active-color': activeColor,
            '--vci-filled-color': filledColor,
            '--vci-border-color': borderColor
        }"
    >
        <view class="vci-title" v-if="title">
            <text class="vci-title__text">{{ title }}</text>
        </view>
        <view class="vci-desc" v-if="desc">
            <text class="vci-desc__text">{{ desc }}</text>
        </view>

        <view class="vci-code-box">
            <view
                v-for="(item, index) in codeLength"
                :key="index"
                class="vci-code-item"
                :class="{
                    'vci-code-item--active': isFocused && currentIndex === index,
                    'vci-code-item--filled': code.length > index,
                    'vci-code-item--underline': type === 'underline',
                    'vci-code-item--box': type === 'box'
                }"
            >
                <text v-if="code[index] && !mask" class="vci-code-text">{{ code[index] }}</text>
                <view v-else-if="code[index] && mask" class="vci-code-dot"></view>
                <view v-if="isFocused && currentIndex === index && code.length === index" class="vci-code-cursor"></view>
            </view>
        </view>

        <view class="vci-tips" v-if="errorMsg">
            <text class="vci-tips__text">{{ errorMsg }}</text>
        </view>

        <input
            ref="inputRef"
            class="vci-hidden-input"
            type="number"
            :maxlength="codeLength"
            :value="code"
            :focus="isFocused"
            :adjust-position="false"
            @input="onInput"
            @focus="onFocus"
            @blur="onBlur"
        />
    </view>
</template>

<script setup>
import { ref, computed, watch } from 'vue'

const props = defineProps({
    modelValue: {
        type: String,
        default: ''
    },
    title: {
        type: String,
        default: ''
    },
    desc: {
        type: String,
        default: ''
    },
    length: {
        type: Number,
        default: 6
    },
    mask: {
        type: Boolean,
        default: false
    },
    type: {
        type: String,
        default: 'box'
    },
    errorMsg: {
        type: String,
        default: ''
    },
    autoFocus: {
        type: Boolean,
        default: false
    },
    activeColor: {
        type: String,
        default: '#1677FF'
    },
    filledColor: {
        type: String,
        default: '#1A202C'
    },
    borderColor: {
        type: String,
        default: '#E2E8F0'
    }
})

const emit = defineEmits(['update:modelValue', 'complete', 'focus', 'blur'])

const code = ref(props.modelValue)
const isFocused = ref(false)
const currentIndex = ref(0)
const inputRef = ref(null)

const codeLength = computed(() => props.length)

watch(() => props.modelValue, (val) => {
    code.value = val
    currentIndex.value = Math.min(val.length, codeLength.value - 1)
})

function focusInput() {
    isFocused.value = true
}

function onInput(e) {
    let val = e.detail.value.replace(/\D/g, '').slice(0, codeLength.value)
    code.value = val
    currentIndex.value = Math.min(val.length, codeLength.value - 1)
    emit('update:modelValue', val)
    if (val.length === codeLength.value) {
        emit('complete', val)
    }
}

function onFocus() {
    isFocused.value = true
    currentIndex.value = code.value.length
    emit('focus')
}

function onBlur() {
    isFocused.value = false
    emit('blur')
}

function clear() {
    code.value = ''
    currentIndex.value = 0
    emit('update:modelValue', '')
}

function setFocus(val) {
    isFocused.value = val
}

defineExpose({
    clear,
    focusInput,
    setFocus
})
</script>

<style lang="scss" scoped>
.verify-code-input {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
}

.vci-title {
    margin-bottom: 8px;

    &__text {
        font-size: 18px;
        font-weight: 600;
        color: #1A202C;
    }
}

.vci-desc {
    margin-bottom: 32px;

    &__text {
        font-size: 13px;
        color: #718096;
    }
}

.vci-code-box {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-bottom: 20px;
}

.vci-code-item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;

    &--box {
        width: 44px;
        height: 44px;
        background: #FFFFFF;
        border: 1px solid var(--vci-border-color, #E2E8F0);
        border-radius: 8px;

        &.vci-code-item--active {
            border-color: var(--vci-active-color, #1677FF);
        }

        &.vci-code-item--filled {
            border-color: var(--vci-filled-color, #1A202C);
        }
    }

    &--underline {
        width: 40px;
        height: 50px;
        border-bottom: 2px solid var(--vci-border-color, #E2E8F0);

        &.vci-code-item--active {
            border-bottom-color: var(--vci-active-color, #FF8C42);
        }

        &.vci-code-item--filled {
            border-bottom-color: var(--vci-filled-color, #1A202C);
        }
    }
}

.vci-code-text {
    font-size: 24px;
    font-weight: 600;
    color: var(--vci-filled-color, #1A202C);
    line-height: 1;
}

.vci-code-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--vci-filled-color, #1A202C);
}

.vci-code-cursor {
    position: absolute;
    width: 2px;
    height: 24px;
    background: var(--vci-active-color, #1677FF);
    animation: vci-blink 1s infinite;
}

@keyframes vci-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0; }
}

.vci-tips {
    text-align: center;
    margin-bottom: 16px;

    &__text {
        font-size: 13px;
        color: #E53E3E;
    }
}

.vci-hidden-input {
    position: absolute;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    opacity: 0;
}
</style>
