<template>
    <div class="default-main dashboard-page" v-loading="loading">
        <div class="small-panel-box">
            <el-row :gutter="20">
                <el-col :sm="12" :lg="6">
                    <div class="small-panel user-reg suspension">
                        <div class="small-panel-title">{{ t('dashboard.stat_user_total') }}</div>
                        <div class="small-panel-content">
                            <div class="content-left">
                                <Icon color="#8595F4" size="20" name="fa fa-users" />
                                <el-statistic :value="stats.user_total" :value-style="statisticValueStyle" />
                            </div>
                        </div>
                    </div>
                </el-col>
                <el-col :sm="12" :lg="6">
                    <div class="small-panel file suspension">
                        <div class="small-panel-title">{{ t('dashboard.stat_new_today') }}</div>
                        <div class="small-panel-content">
                            <div class="content-left">
                                <Icon color="#AD85F4" size="20" name="fa fa-user-plus" />
                                <el-statistic :value="stats.user_new_today" :value-style="statisticValueStyle" />
                            </div>
                            <div class="content-right" :class="growthClass">{{ growthText }}</div>
                        </div>
                    </div>
                </el-col>
                <el-col :sm="12" :lg="6">
                    <div class="small-panel users suspension">
                        <div class="small-panel-title">{{ t('dashboard.stat_deposit_today') }}</div>
                        <div class="small-panel-content">
                            <div class="content-left">
                                <Icon color="#74A8B5" size="20" name="fa fa-credit-card" />
                                <el-statistic :value="depositTodayDisplay" :value-style="statisticValueStyle" />
                            </div>
                            <div class="content-right color-success">{{ depositCountText }}</div>
                        </div>
                    </div>
                </el-col>
                <el-col :sm="12" :lg="6">
                    <div class="small-panel addons suspension">
                        <div class="small-panel-title">{{ t('dashboard.stat_withdraw_pending') }}</div>
                        <div class="small-panel-content">
                            <div class="content-left">
                                <Icon color="#F48595" size="20" name="fa fa-clock-o" />
                                <el-statistic :value="stats.withdraw_pending" :value-style="statisticValueStyle" />
                            </div>
                            <div class="content-right color-warning">{{ t('dashboard.stat_hint_pending') }}</div>
                        </div>
                    </div>
                </el-col>
            </el-row>
        </div>

        <div class="growth-chart">
            <el-row :gutter="20">
                <el-col class="lg-mb-20" :xs="24" :sm="24" :md="12" :lg="9">
                    <el-card shadow="hover" :header="t('dashboard.chart_new_user_deposit')">
                        <div class="user-growth-chart" :ref="chartRefs.set"></div>
                    </el-card>
                </el-col>
                <el-col class="lg-mb-20" :xs="24" :sm="24" :md="12" :lg="9">
                    <el-card shadow="hover" :header="t('dashboard.chart_bet_7d')">
                        <div class="file-growth-chart" :ref="chartRefs.set"></div>
                    </el-card>
                </el-col>
                <el-col :xs="24" :sm="24" :md="24" :lg="6">
                    <el-card class="new-user-card" shadow="hover" :header="t('dashboard.recent_users')">
                        <div class="new-user-growth">
                            <el-empty v-if="recentUsers.length === 0" :description="t('dashboard.no_data')" />
                            <el-scrollbar v-else>
                                <div v-for="u in recentUsers" :key="u.id" class="new-user-item">
                                    <img
                                        class="new-user-avatar"
                                        :src="u.head_image ? fullUrl(u.head_image) : fullUrl('/static/images/avatar.png')"
                                        alt=""
                                    />
                                    <div class="new-user-base">
                                        <div class="new-user-name">{{ u.username || '-' }}</div>
                                        <div class="new-user-time">{{ formatUserTime(u.create_time) }}</div>
                                        <div v-if="u.channel_name" class="new-user-channel">{{ u.channel_name }}</div>
                                    </div>
                                    <Icon class="new-user-arrow" color="#8595F4" name="fa fa-angle-right" />
                                </div>
                            </el-scrollbar>
                        </div>
                    </el-card>
                </el-col>
            </el-row>
        </div>

        <div class="growth-chart">
            <el-row :gutter="20">
                <el-col class="lg-mb-20" :xs="24" :sm="24" :md="24" :lg="12">
                    <el-card shadow="hover" :header="t('dashboard.chart_channel_users')">
                        <div class="user-source-chart" :ref="chartRefs.set"></div>
                    </el-card>
                </el-col>
                <el-col class="lg-mb-20" :xs="24" :sm="24" :md="24" :lg="12">
                    <el-card shadow="hover" :header="t('dashboard.chart_deposit_amount_channel')">
                        <div class="user-surname-chart" :ref="chartRefs.set"></div>
                    </el-card>
                </el-col>
            </el-row>
        </div>
    </div>
</template>

<script setup lang="ts">
import { useEventListener, useTemplateRefsList } from '@vueuse/core'
import * as echarts from 'echarts'
import type { EChartsType } from 'echarts/core'
import { CSSProperties, computed, nextTick, onActivated, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { index } from '/@/api/backend/dashboard'
import type { DashboardDepositAmountChannelItem, DashboardPayload, DashboardRecentUser } from '/@/api/backend/dashboard'
import { useNavTabs } from '/@/stores/navTabs'
import { fullUrl } from '/@/utils/common'

defineOptions({
    name: 'dashboard',
})

const { t } = useI18n()
const navTabs = useNavTabs()
const chartRefs = useTemplateRefsList<HTMLDivElement>()

const loading = ref(true)

const state: {
    charts: EChartsType[]
} = reactive({
    charts: [],
})

const stats = reactive({
    user_total: 0,
    user_new_today: 0,
    user_new_yesterday: 0,
    user_new_growth_pct: null as number | null,
    deposit_today_amount: '0.00',
    deposit_today_count: 0,
    withdraw_pending: 0,
    bet_today_amount: '0.00',
    bet_today_count: 0,
})

const trend = reactive({
    days: [] as string[],
    new_users: [] as number[],
    deposit_amount: [] as string[],
    bet_amount: [] as string[],
})

const channelShare = ref<{ name: string; value: number }[]>([])
const depositAmountChannelShare = ref<DashboardDepositAmountChannelItem[]>([])
const recentUsers = ref<DashboardRecentUser[]>([])

const depositTodayDisplay = computed(() => {
    const n = parseFloat(stats.deposit_today_amount)
    if (!Number.isFinite(n)) {
        return stats.deposit_today_amount
    }
    return n.toFixed(2)
})

const growthText = computed(() => {
    const p = stats.user_new_growth_pct
    if (p === null || p === undefined) {
        return '—'
    }
    const sign = p > 0 ? '+' : ''
    return sign + p.toFixed(1) + '%'
})

const growthClass = computed(() => {
    const p = stats.user_new_growth_pct
    if (p === null || p === undefined) {
        return 'color-info'
    }
    if (p > 0) {
        return 'color-success'
    }
    if (p < 0) {
        return 'color-danger'
    }
    return 'color-info'
})

const depositCountText = computed(() => {
    return t('dashboard.deposit_orders_today', { n: stats.deposit_today_count })
})

const statisticValueStyle: CSSProperties = {
    fontSize: '28px',
}

const disposeCharts = () => {
    for (const c of state.charts) {
        c.dispose()
    }
    state.charts = []
}

const parseMoneySeries = (arr: string[]) => arr.map((s) => parseFloat(String(s)) || 0)

const initTrendDepositChart = () => {
    const el = chartRefs.value[0] as HTMLElement
    if (!el) {
        return
    }
    const chart = echarts.init(el)
    chart.setOption({
        color: ['#8595F4', '#67C23A'],
        grid: { top: 48, right: 56, bottom: 24, left: 48 },
        tooltip: { trigger: 'axis' },
        legend: {
            data: [t('dashboard.series_new_users'), t('dashboard.series_deposit_amount')],
            textStyle: { color: '#73767a' },
            top: 0,
        },
        xAxis: { type: 'category', data: trend.days, boundaryGap: false },
        yAxis: [
            { type: 'value', name: t('dashboard.series_new_users'), splitLine: { lineStyle: { type: 'dashed' } } },
            {
                type: 'value',
                name: t('dashboard.series_deposit_amount'),
                position: 'right',
                splitLine: { show: false },
            },
        ],
        series: [
            {
                name: t('dashboard.series_new_users'),
                type: 'line',
                smooth: true,
                data: trend.new_users,
                areaStyle: { color: 'rgba(133,149,244,0.15)' },
            },
            {
                name: t('dashboard.series_deposit_amount'),
                type: 'line',
                smooth: true,
                yAxisIndex: 1,
                data: parseMoneySeries(trend.deposit_amount),
                areaStyle: { color: 'rgba(103,194,58,0.12)' },
            },
        ],
    })
    state.charts.push(chart)
}

const initBetTrendChart = () => {
    const el = chartRefs.value[1] as HTMLElement
    if (!el) {
        return
    }
    const chart = echarts.init(el)
    chart.setOption({
        color: ['#E6A23C'],
        grid: { top: 36, right: 16, bottom: 24, left: 48 },
        tooltip: { trigger: 'axis' },
        xAxis: { type: 'category', data: trend.days },
        yAxis: { type: 'value', splitLine: { lineStyle: { type: 'dashed' } } },
        series: [
            {
                name: t('dashboard.series_bet_amount'),
                type: 'bar',
                data: parseMoneySeries(trend.bet_amount),
                itemStyle: { borderRadius: [4, 4, 0, 0] },
            },
        ],
    })
    state.charts.push(chart)
}

const initChannelPie = () => {
    const el = chartRefs.value[2] as HTMLElement
    if (!el) {
        return
    }
    const chart = echarts.init(el)
    const data = channelShare.value.map((x) => ({ name: x.name, value: x.value }))
    chart.setOption({
        tooltip: { trigger: 'item' },
        legend: { type: 'scroll', bottom: 0, textStyle: { color: '#73767a' } },
        series: [
            {
                type: 'pie',
                radius: ['36%', '62%'],
                center: ['50%', '46%'],
                data,
                emphasis: {
                    itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.15)' },
                },
            },
        ],
    })
    state.charts.push(chart)
}

const initDepositAmountChannelPie = () => {
    const el = chartRefs.value[3] as HTMLElement
    if (!el) {
        return
    }
    const chart = echarts.init(el)
    const data = depositAmountChannelShare.value.map((x) => {
        const v = parseFloat(String(x.value))
        return {
            name: x.name,
            value: Number.isFinite(v) ? v : 0,
        }
    })
    chart.setOption({
        tooltip: {
            trigger: 'item',
            formatter: (p: { name?: string; value?: number; percent?: number }) => {
                const name = p.name ?? ''
                const val = typeof p.value === 'number' && Number.isFinite(p.value) ? p.value : 0
                const pct = typeof p.percent === 'number' && Number.isFinite(p.percent) ? p.percent : 0
                return name + '<br/>' + val.toFixed(2) + ' (' + pct.toFixed(1) + '%)'
            },
        },
        legend: { type: 'scroll', bottom: 0, textStyle: { color: '#73767a' } },
        series: [
            {
                type: 'pie',
                radius: ['32%', '58%'],
                center: ['50%', '46%'],
                data,
                emphasis: {
                    itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.15)' },
                },
            },
        ],
    })
    state.charts.push(chart)
}

const echartsResize = () => {
    nextTick(() => {
        for (const key in state.charts) {
            state.charts[key].resize()
        }
    })
}

const applyPayload = (payload: DashboardPayload) => {
    const s = payload.stats
    stats.user_total = s.user_total
    stats.user_new_today = s.user_new_today
    stats.user_new_yesterday = s.user_new_yesterday
    stats.user_new_growth_pct = s.user_new_growth_pct
    stats.deposit_today_amount = s.deposit_today_amount
    stats.deposit_today_count = s.deposit_today_count
    stats.withdraw_pending = s.withdraw_pending
    stats.bet_today_amount = s.bet_today_amount
    stats.bet_today_count = s.bet_today_count

    trend.days = payload.trend.days
    trend.new_users = payload.trend.new_users
    trend.deposit_amount = payload.trend.deposit_amount
    trend.bet_amount = payload.trend.bet_amount

    channelShare.value = payload.channel_share
    depositAmountChannelShare.value = payload.deposit_amount_channel_share ?? []
    recentUsers.value = payload.recent_users
}

const loadDashboard = async () => {
    loading.value = true
    try {
        const res = await index()
        const payload = res.data as unknown as DashboardPayload
        applyPayload(payload)
        await nextTick()
        disposeCharts()
        initTrendDepositChart()
        initBetTrendChart()
        initChannelPie()
        initDepositAmountChannelPie()
        echartsResize()
    } catch (_e) {
        // 统计数据加载失败时由 axios 拦截器提示；此处不重复展示
    } finally {
        loading.value = false
    }
}

function formatUserTime(ts: number): string {
    if (!ts || ts <= 0) {
        return '—'
    }
    const now = Math.floor(Date.now() / 1000)
    const diff = now - ts
    if (diff < 60) {
        return diff + t('dashboard.seconds_ago')
    }
    if (diff < 3600) {
        return Math.floor(diff / 60) + t('dashboard.minutes_ago')
    }
    if (diff < 86400) {
        return Math.floor(diff / 3600) + t('dashboard.hours_ago')
    }
    const d = new Date(ts * 1000)
    const pad = (n: number) => (n < 10 ? '0' + n : String(n))
    return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes())
}

onActivated(() => {
    echartsResize()
})

onMounted(() => {
    loadDashboard()
    useEventListener(window, 'resize', echartsResize)
})

onUnmounted(() => {
    disposeCharts()
})

watch(
    () => navTabs.state.tabFullScreen,
    () => {
        echartsResize()
    }
)

</script>

<style scoped lang="scss">
.small-panel-box {
    margin-top: 0;
}
.small-panel {
    background-color: #e9edf2;
    border-radius: var(--el-border-radius-base);
    padding: 25px;
    margin-bottom: 20px;
    .small-panel-title {
        color: #92969a;
        font-size: 15px;
    }
    .small-panel-content {
        display: flex;
        align-items: flex-end;
        margin-top: 20px;
        color: #2c3f5d;
        .content-left {
            display: flex;
            align-items: center;
            font-size: 24px;
            .icon {
                margin-right: 10px;
            }
        }
        .content-right {
            font-size: 14px;
            margin-left: auto;
            text-align: right;
            max-width: 48%;
            line-height: 1.3;
        }
        .color-success {
            color: var(--el-color-success);
        }
        .color-warning {
            color: var(--el-color-warning);
        }
        .color-danger {
            color: var(--el-color-danger);
        }
        .color-info {
            color: var(--el-text-color-secondary);
        }
    }
}
.growth-chart {
    margin-bottom: 20px;
}
.user-growth-chart,
.file-growth-chart {
    height: 260px;
}

.user-source-chart,
.user-surname-chart {
    height: 360px;
}
.new-user-growth {
    height: 300px;
}

.new-user-item {
    display: flex;
    align-items: center;
    padding: 16px 20px;
    margin: 8px 12px;
    box-shadow: 0 0 30px 0 rgba(82, 63, 105, 0.05);
    background-color: var(--ba-bg-color-overlay);
    .new-user-avatar {
        height: 48px;
        width: 48px;
        border-radius: 50%;
        object-fit: cover;
    }
    .new-user-base {
        margin-left: 10px;
        flex: 1;
        min-width: 0;
        color: #2c3f5d;
        .new-user-name {
            font-size: 15px;
            font-weight: 500;
        }
        .new-user-time {
            font-size: 13px;
            color: var(--el-text-color-secondary);
        }
        .new-user-channel {
            font-size: 12px;
            color: var(--el-text-color-placeholder);
            margin-top: 2px;
        }
    }
    .new-user-arrow {
        margin-left: auto;
        flex-shrink: 0;
    }
}
.new-user-card :deep(.el-card__body) {
    padding: 0;
}

@media screen and (max-width: 1200px) {
    .lg-mb-20 {
        margin-bottom: 20px;
    }
}
html.dark {
    .small-panel {
        background-color: var(--ba-bg-color-overlay);
        .small-panel-content {
            color: var(--el-text-color-regular);
        }
    }
    .new-user-item {
        .new-user-base {
            color: var(--el-text-color-regular);
        }
    }
}
</style>
