<template>
    <view class="timeline-container">
        <!-- 加载状态 -->
        <view v-if="loading && items.length === 0" class="loading-mask">
            <view class="loading-content">
                <uni-icons type="spinner-cycle" size="30" color="#007aff" class="spin"></uni-icons>
                <text class="loading-text">{{ t('common.loading') }}</text>
            </view>
        </view>

        <!-- 时间线列表 -->
        <scroll-view v-else class="timeline-scroll" scroll-y :scroll-top="scrollTop" @scrolltolower="handleLoadMore"
            :lower-threshold="lowerThreshold" :refresher-enabled="enableRefresh" :refresher-triggered="refreshing"
            @refresherrefresh="handleRefresh">
            <view class="card">
                <view class="timeline" :style="{ '--size': size }">
                    <view v-for="(item, index) in items" :key="item.id || index" class="timeline-item"
                        @click="handleItemClick(item)">
                        <!-- 时间点图标 -->
                        <view class="timeline-item-point" :class="[
                            `text-${getItemColor(item)}`,
                            { 'bg-white': !getItemSolid(item), 'solid': getItemSolid(item) }
                        ]">
                            <view class="icon-wrapper"
                                :style="{ backgroundColor: getItemSolid(item) ? getColorValue(getItemColor(item)) : 'transparent' }">
                                <uni-icons :type="getItemIcon(item)" :size="14"
                                    :color="getItemSolid(item) ? '#fff' : getColorValue(getItemColor(item))"></uni-icons>
                            </view>
                        </view>

                        <!-- 内容区域 -->
                        <view class="timeline-item-content">
                            <view class="timeline-item-header">
                                <text class="title">{{ getItemTitle(item) }}</text>
                                <text class="time">{{ formatTime(getItemTime(item)) }}</text>
                            </view>

                            <view class="content-description">{{ getItemDescription(item) }}</view>

                            <!-- 标签区域 -->
                            <view v-if="getItemTags(item) && getItemTags(item).length" class="tags-container"
                                @click.stop>
                                <view class="tags-label">{{ t('common.category') }}:</view>
                                <view v-for="(tag, tagIndex) in getItemTags(item)" :key="tagIndex" class="tag"
                                    @click="handleTagClick(tag, item)">
                                    #{{ tag }}
                                </view>
                            </view>

                            <!-- 额外信息插槽 -->
                            <slot name="extra" :item="item" :index="index"></slot>
                        </view>
                    </view>
                </view>

                <!-- 加载更多状态 -->
                <view v-if="loadingMore" class="loading-more">
                    <uni-icons type="spinner-cycle" size="20" color="#999" class="spin"></uni-icons>
                    <text>{{ t('common.loadingMore') }}</text>
                </view>

                <!-- 没有更多数据 -->
                <view v-if="!hasMore && items.length > 0" class="no-more">
                    <text>{{ t('common.noMore') }}</text>
                </view>

                <!-- 空数据 -->
                <view v-if="items.length === 0 && !loading" class="empty-data">
                    <uni-icons type="info" size="40" color="#999"></uni-icons>
                    <text>{{ t('common.noData') }}</text>
                </view>
            </view>
        </scroll-view>

        <!-- 回到顶部按钮 -->
        <view v-if="showBackToTop && scrollTop > 300" class="back-to-top" @click="scrollToTop">
            <uni-icons type="arrow-up" size="20" color="#fff"></uni-icons>
        </view>
    </view>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

const props = defineProps({
    // API 请求函数
    api: {
        type: Function,
        required: true
    },
    // 查询参数
    queryParams: {
        type: Object,
        default: () => ({})
    },
    // 每页条数
    pageSize: {
        type: Number,
        default: 10
    },
    // 初始页码
    initialPage: {
        type: Number,
        default: 1
    },
    // 是否立即加载
    immediate: {
        type: Boolean,
        default: true
    },
    // 时间点大小
    size: {
        type: String,
        default: '1.5rem'
    },
    // 滚动阈值
    lowerThreshold: {
        type: Number,
        default: 100
    },
    // 是否启用下拉刷新
    enableRefresh: {
        type: Boolean,
        default: true
    },
    // 标题
    title: {
        type: String,
        default: ''
    },
    // 是否显示标题栏
    showHeader: {
        type: Boolean,
        default: false
    },
    // 是否显示更多链接
    showMoreLink: {
        type: Boolean,
        default: false
    },
    // 更多链接跳转地址
    moreUrl: {
        type: String,
        default: ''
    },
    // 是否显示回到顶部按钮
    showBackToTop: {
        type: Boolean,
        default: true
    },
    // 数据映射函数（用于自定义数据格式）
    dataMapper: {
        type: Function,
        default: null
    },
    // 图标映射
    iconMap: {
        type: Object,
        default: () => ({
            'user-edit': 'person',
            'image': 'image',
            'leaf': 'leaf',
            'project-diagram': 'grid',
            'history': 'clock',
            'default': 'circle'
        })
    },
    // 颜色映射
    colorMap: {
        type: Object,
        default: () => ({
            'primary': '#007aff',
            'secondary': '#6c757d',
            'success': '#4cd964',
            'warning': '#ffcc00',
            'error': '#ff3b30',
            'info': '#5ac8fa'
        })
    }
})

const emit = defineEmits([
    'tag-click',
    'item-click',
    'more-click',
    'load-success',
    'load-error',
    'page-change'
])

// ==================== 数据状态 ====================
const items = ref<any[]>([])
const currentPage = ref(props.initialPage)
const hasMore = ref(true)
const loading = ref(false)
const loadingMore = ref(false)
const refreshing = ref(false)
const scrollTop = ref(0)
const total = ref(0)

// ==================== 工具函数 ====================
// 获取项目图标
const getItemIcon = (item: any): string => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return props.iconMap[mapped.icon] || props.iconMap.default
    }
    return props.iconMap[item.icon] || props.iconMap.default
}

// 获取项目颜色
const getItemColor = (item: any): string => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.color || 'secondary'
    }
    return item.color || 'secondary'
}

// 获取项目是否实心
const getItemSolid = (item: any): boolean => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.solid || false
    }
    return item.solid || false
}

// 获取项目标题
const getItemTitle = (item: any): string => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.title || ''
    }
    return item.title || ''
}

// 获取项目描述
const getItemDescription = (item: any): string => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.description || ''
    }
    return item.description || ''
}

// 获取项目时间
const getItemTime = (item: any): string | Date | number => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.time || new Date()
    }
    return item.time || new Date()
}

// 获取项目标签
const getItemTags = (item: any): string[] => {
    if (props.dataMapper) {
        const mapped = props.dataMapper(item)
        return mapped.tags || []
    }
    return item.tags || []
}

// 获取颜色值
const getColorValue = (color: string): string => {
    return props.colorMap[color] || props.colorMap.secondary
}

// 格式化时间
const formatTime = (time: string | Date | number): string => {
    if (!time) return ''

    const date = new Date(time)
    const now = new Date()
    const diff = now.getTime() - date.getTime()

    // 1分钟内
    if (diff < 60 * 1000) {
        return t('time.justNow')
    }
    // 1小时内
    if (diff < 60 * 60 * 1000) {
        const minutes = Math.floor(diff / (60 * 1000))
        return t('time.minutesAgo', { minutes })
    }
    // 24小时内
    if (diff < 24 * 60 * 60 * 1000) {
        const hours = Math.floor(diff / (60 * 60 * 1000))
        return t('time.hoursAgo', { hours })
    }
    // 7天内
    if (diff < 7 * 24 * 60 * 60 * 1000) {
        const days = Math.floor(diff / (24 * 60 * 60 * 1000))
        return t('time.daysAgo', { days })
    }

    // 超过7天显示具体日期
    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    const hour = String(date.getHours()).padStart(2, '0')
    const minute = String(date.getMinutes()).padStart(2, '0')
    return `${year}-${month}-${day} ${hour}:${minute}`
}
// API 请求函数
const fetchTimelineData = async (params: any) => {
    console.log('请求参数:', params)

    return new Promise((resolve) => {
        setTimeout(() => {
            // 模拟数据
            const mockData = [
                {
                    id: 1,
                    title: 'Dollar Index (ICE)',
                    description: 'Dollar Index (ICE) Intraday: caution.',
                    time: new Date(Date.now() - 12 * 60 * 1000),
                    icon: 'user-edit',
                    color: 'secondary',
                    url: '/pages/detail/1',  // 跳转链接
                    tags: ['美元', '指数']
                },
                {
                    id: 2,
                    title: 'USD/CHF',
                    description: 'USD/CHF Intraday: under pressure.',
                    time: new Date(Date.now() - 60 * 60 * 1000),
                    icon: 'image',
                    color: 'primary',
                    solid: true,
                    page: '/pages/detail/2',  // 内部页面
                    tags: ['外汇', '瑞士法郎']
                },
                {
                    id: 3,
                    title: 'USD/CAD',
                    description: 'USD/CAD Intraday: intraday support around 1.3545.',
                    time: new Date(Date.now() - 3 * 60 * 60 * 1000),
                    icon: 'leaf',
                    color: 'success'
                },
                {
                    id: 4,
                    title: 'AUD/USD',
                    description: 'AUD/USD Intraday: the downside prevails.',
                    time: new Date(Date.now() - 24 * 60 * 60 * 1000),
                    icon: 'project-diagram',
                    color: 'warning'
                },
                {
                    id: 5,
                    title: 'Weekly Report',
                    description: 'The weekly report was uploaded',
                    time: new Date(Date.now() - 24 * 60 * 60 * 1000),
                    icon: 'history',
                    color: 'error'
                }
            ]

            // 分页处理
            const { page, pageSize } = params
            const start = (page - 1) * pageSize
            const end = start + pageSize
            const pageData = mockData.slice(start, end)

            resolve({
                code: 200,
                data: {
                    list: pageData,
                    total: mockData.length
                }
            })
        }, 800)
    })
}
// ==================== 数据加载 ====================
const loadData = async (isRefresh: boolean = false) => {
    if (loading.value || loadingMore.value) return

    if (isRefresh) {
        refreshing.value = true
        currentPage.value = 1
    } else {
        loadingMore.value = true
    }

    loading.value = true

    try {
        const params = {
            page: currentPage.value,
            pageSize: props.pageSize,
            ...props.queryParams
        }

        const res = await fetchTimelineData(params)

        // 处理不同格式的返回数据
        let newItems: any[] = []
        let totalCount = 0

        if (res.code === 200 || res.code === 0) {
            const data = res.data || res

            if (Array.isArray(data)) {
                newItems = data
                totalCount = data.length
            } else if (data.list || data.records) {
                newItems = data.list || data.records
                totalCount = data.total || newItems.length
            } else {
                newItems = []
            }

            // 更新数据
            if (isRefresh) {
                items.value = newItems
            } else {
                items.value = [...items.value, ...newItems]
            }

            total.value = totalCount

            // 判断是否还有更多
            hasMore.value = newItems.length === props.pageSize

            emit('load-success', { data: res, isRefresh, page: currentPage.value })
        } else {
            throw new Error(res.message || '加载失败')
        }
    } catch (error: any) {
        console.error('加载失败:', error)
        uni.showToast({
            title: error.message || '加载失败',
            icon: 'none'
        })
        emit('load-error', { error, page: currentPage.value })
    } finally {
        loading.value = false
        loadingMore.value = false
        refreshing.value = false
    }
}

// 加载更多
const handleLoadMore = () => {
    if (!hasMore.value || loadingMore.value || loading.value) return

    currentPage.value++
    emit('page-change', currentPage.value)
    loadData(false)
}

// 下拉刷新
const handleRefresh = () => {
    loadData(true)
}

// ==================== 事件处理 ====================
// 项目点击
const handleItemClick = (item: any) => {
    emit('item-click', item)

    // 如果项目有跳转链接，自动跳转
    if (item.url) {
        // #ifdef H5
        window.open(item.url, item.target || '_self')
        // #endif
        // #ifndef H5
        if (item.url.startsWith('http')) {
            plus.runtime.openURL(item.url)
        } else {
            uni.navigateTo({ url: item.url })
        }
        // #endif
    } else if (item.page) {
        // 内部页面跳转
        uni.navigateTo({
            url: item.page,
            success: () => {
                console.log('跳转成功')
            },
            fail: (err) => {
                console.error('跳转失败:', err)
                uni.showToast({
                    title: '页面不存在',
                    icon: 'none'
                })
            }
        })
    }
}

// 标签点击
const handleTagClick = (tag: string, item: any) => {
    emit('tag-click', { tag, item })

    // 可以在这里添加标签搜索等功能
    uni.showToast({
        title: `标签: ${tag}`,
        icon: 'none'
    })
}

// 更多点击
const handleMoreClick = () => {
    emit('more-click')

    if (props.moreUrl) {
        // #ifdef H5
        window.open(props.moreUrl, '_self')
        // #endif
        // #ifndef H5
        if (props.moreUrl.startsWith('http')) {
            plus.runtime.openURL(props.moreUrl)
        } else {
            uni.navigateTo({ url: props.moreUrl })
        }
        // #endif
    }
}

// ==================== 滚动控制 ====================
// 监听滚动
const onScroll = (e: any) => {
    scrollTop.value = e.detail.scrollTop
}

// 回到顶部
const scrollToTop = () => {
    scrollTop.value = 0
    uni.pageScrollTo({
        scrollTop: 0,
        duration: 300
    })
}

// ==================== 外部方法 ====================
// 刷新数据
const refresh = () => {
    loadData(true)
}

// 重置并刷新
const reset = () => {
    items.value = []
    currentPage.value = 1
    hasMore.value = true
    loadData(true)
}

// 添加项目
const addItem = (item: any) => {
    items.value.unshift(item)
}

// 删除项目
const removeItem = (id: string | number) => {
    const index = items.value.findIndex(item => item.id === id)
    if (index !== -1) {
        items.value.splice(index, 1)
    }
}

// 更新项目
const updateItem = (id: string | number, newData: any) => {
    const index = items.value.findIndex(item => item.id === id)
    if (index !== -1) {
        items.value[index] = { ...items.value[index], ...newData }
    }
}

// ==================== 生命周期 ====================
// 监听查询参数变化
watch(() => props.queryParams, () => {
    refresh()
}, { deep: true })

// 初始化加载
onMounted(() => {
    if (props.immediate) {
        loadData(true)
    }
})

// 暴露方法给父组件
defineExpose({
    refresh,
    reset,
    addItem,
    removeItem,
    updateItem,
    items,
    loading,
    hasMore,
    total,
    currentPage
})
</script>


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

.timeline-container {
    max-width: px2rpx(576);
    min-width: px2rpx(20);
    padding: px2rpx(12);
    box-sizing: border-box;
    margin: 0 auto;
    background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;

    .timeline-scroll {
        height: 100%;
    }

    .timeline-item {
        position: relative;
        display: flex;
        padding-bottom: px2rpx(32);

        &::before {
            content: '';
            position: absolute;
            left: px2rpx(12);
            top: 0;
            bottom: 0;
            width: px2rpx(2);
            background-color: var(--color-slate-300);
            transform: translateX(-50%);
        }

        &:last-child {
            padding-bottom: 0;
        }


        .timeline-item-point {
            position: relative;
            width: px2rpx(24);
            height: px2rpx(24);
            display: flex;
            align-items: center;
            justify-content: center;
            background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
            border: 1px solid currentColor;
            flex-shrink: 0;
            z-index: 1;

            &.solid {
                border: none;

                .icon-wrapper {
                    width: 100%;
                    height: 100%;
                    border-radius: 50%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
            }

            // 颜色变体
            &.text-primary {
                color: #cf1322;
                border-color: #cf1322;
            }

            &.text-secondary {
                color: #6c757d;
                border-color: #6c757d;
            }

            &.text-success {
                color: #4cd964;
                border-color: #4cd964;
            }

            &.text-warning {
                color: #ffcc00;
                border-color: #ffcc00;
            }

            &.text-error {
                color: #ff3b30;
                border-color: #ff3b30;
            }

            &.text-info {
                color: #5ac8fa;
                border-color: #5ac8fa;
            }
        }

        .timeline-item-content {
            margin-left: px2rpx(32);
            flex: 1;

            .timeline-item-header {
                display: flex;
                justify-content: space-between;
                margin-bottom: px2rpx(8);

                .title {
                    color: var(--color-slate-600);
                    font-weight: 500;
                    font-size: px2rpx(14);
                }

                .time {
                    font-size: px2rpx(12);
                    color: var(--color-slate-600);
                }
            }

            .content-description {
                color: var(--color-slate-900);
                font-size: px2rpx(14);
                padding: px2rpx(4) 0;
            }

            .tags-container {
                display: inline-flex;
                margin-top: px2rpx(16);
                flex-wrap: wrap;
                gap: px2rpx(8);
                font-size: px2rpx(14);

                .tag {
                    color: var(--color-primary);
                    cursor: pointer;

                    &:hover {
                        text-decoration: underline;
                    }
                }
            }

            @media (min-width: 640px) {
                margin-left: px2rpx(40);
            }
        }

    }
}

// 加载状态
.loading-mask {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(255, 255, 255, 0.9);
    z-index: 100;
}

.loading-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: px2rpx(30);
    background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
    border-radius: px2rpx(16);
    box-shadow: 0 px2rpx(4) px2rpx(20) rgba(0, 0, 0, 0.1);
}

.loading-text {
    margin-top: px2rpx(20);
    font-size: px2rpx(28);
    color: #666;
}

.loading-more,
.no-more,
.empty-data {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: px2rpx(30);
    font-size: px2rpx(28);
    color: #999;
}

.empty-data {
    flex-direction: column;
    gap: px2rpx(20);
}

.spin {
    animation: spin 1s linear infinite;
    margin-right: px2rpx(10);
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}
</style>