<template>
    <div class="conversion-stats-page">
        <el-card class="!border-none" shadow="never">
            <el-form :inline="true" :model="queryParams" class="stats-filter-form">
                <el-form-item label="统计维度">
                    <el-segmented
                        v-model="queryParams.dimension"
                        :options="dimensionOptions"
                        @change="handleDimensionChange"
                    />
                </el-form-item>

                <el-form-item :label="entityLabel">
                    <el-tree-select
                        v-if="queryParams.dimension === 'dept'"
                        v-model="currentEntityId"
                        :data="deptTreeOptions"
                        :placeholder="`全部${entityLabel}`"
                        clearable
                        filterable
                        node-key="id"
                        check-strictly
                        :default-expand-all="true"
                        :props="deptTreeProps"
                        style="width: 220px"
                        @change="handleEntityChange"
                    />
                    <el-select
                        v-else
                        v-model="currentEntityId"
                        :placeholder="`全部${entityLabel}`"
                        clearable
                        filterable
                        style="width: 220px"
                        @change="handleEntityChange"
                    >
                        <el-option
                            v-for="item in currentEntityOptions"
                            :key="item.id"
                            :label="item.name"
                            :value="item.id"
                        />
                    </el-select>
                </el-form-item>

                <el-form-item label="自媒体渠道">
                    <el-select
                        v-model="queryParams.media_channel_code"
                        placeholder="全部渠道"
                        clearable
                        filterable
                        style="width: 220px"
                        @change="handleMediaChannelChange"
                    >
                        <el-option
                            v-for="item in mediaChannelOptions"
                            :key="item.code"
                            :label="item.name"
                            :value="item.code"
                        />
                    </el-select>
                </el-form-item>

                <el-form-item label="时间范围">
                    <el-radio-group v-model="queryParams.time_type" @change="handleTimeTypeChange">
                        <el-radio-button label="today">今天</el-radio-button>
                        <el-radio-button label="yesterday">昨天</el-radio-button>
                        <el-radio-button label="week">最近7天</el-radio-button>
                        <el-radio-button label="month">最近30天</el-radio-button>
                        <el-radio-button label="custom">自定义</el-radio-button>
                    </el-radio-group>
                </el-form-item>

                <el-form-item v-if="queryParams.time_type === 'custom'">
                    <el-date-picker
                        v-model="dateRange"
                        type="daterange"
                        value-format="YYYY-MM-DD"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        style="width: 260px"
                        @change="handleCustomDateChange"
                    />
                </el-form-item>

                <el-form-item>
                    <el-button type="primary" :loading="pager.loading" @click="handleQuery">查询</el-button>
                    <el-button @click="handleReset">重置</el-button>
                </el-form-item>
            </el-form>
        </el-card>

        <div class="stats-kpi-grid">
            <div
                v-for="card in summaryCards"
                :key="card.key"
                class="stats-kpi-card"
            >
                <div class="stats-kpi-label">{{ card.label }}</div>
                <div class="stats-kpi-value" :class="{ 'is-money': card.type === 'money' }">
                    {{ renderMetric(card.key, overview.summary, card.type, card.placeholder) }}
                </div>
            </div>
        </div>

        <el-row :gutter="20" class="mt-4">
            <el-col :xs="24" :lg="14">
                <el-card class="!border-none" shadow="never">
                    <template #header>
                        <div class="card-header">
                            <span>转化排行</span>
                            <span class="card-hint">{{ dateRangeText }}</span>
                        </div>
                    </template>
                    <v-charts
                        v-if="rankingChartHasData"
                        class="stats-chart"
                        :option="rankingChartOption"
                        autoresize
                    />
                    <el-empty v-else description="暂无图表数据" />
                </el-card>
            </el-col>
            <el-col :xs="24" :lg="10">
                <el-card class="!border-none" shadow="never">
                    <template #header>
                        <div class="card-header">
                            <span>诊单金额占比</span>
                            <span class="card-hint">TOP 10</span>
                        </div>
                    </template>
                    <v-charts
                        v-if="amountPieHasData"
                        class="stats-chart"
                        :option="amountPieOption"
                        autoresize
                    />
                    <el-empty v-else description="暂无金额数据" />
                </el-card>
            </el-col>
        </el-row>

        <el-row :gutter="20" class="mt-4">
            <el-col :xs="24" :lg="10">
                <el-card class="!border-none" shadow="never">
                    <template #header>
                        <div class="card-header">
                            <span>加粉占比</span>
                            <span class="card-hint">TOP 10</span>
                        </div>
                    </template>
                    <v-charts
                        v-if="fanPieHasData"
                        class="stats-chart"
                        :option="fanPieOption"
                        autoresize
                    />
                    <el-empty v-else description="暂无加粉数据" />
                </el-card>
            </el-col>
            <el-col :xs="24" :lg="14">
                <el-card class="!border-none" shadow="never">
                    <template #header>
                        <div class="card-header">
                            <span>明细列表</span>
                            <span class="card-hint">{{ entityLabel }}维度</span>
                        </div>
                    </template>
                    <el-table
                        :data="pager.lists"
                        :row-key="queryParams.dimension === 'dept' ? 'id' : undefined"
                        :tree-props="queryParams.dimension === 'dept' ? treeProps : undefined"
                        :default-expand-all="false"
                        size="large"
                        style="width: 100%"
                        max-height="560"
                    >
                        <el-table-column
                            :label="entityLabel"
                            prop="name"
                            fixed="left"
                            min-width="280"
                            class-name="dept-name-column"
                            show-overflow-tooltip
                        >
                            <template #default="{ row }">
                                <span class="dept-name-cell" :title="row.name">
                                    {{ row.name }}
                                </span>
                            </template>
                        </el-table-column>
                        <el-table-column
                            v-for="column in tableColumns"
                            :key="column.key"
                            :label="column.label"
                            :min-width="column.minWidth || 110"
                            :width="column.width"
                            :align="column.align || 'center'"
                        >
                            <template #default="{ row }">
                                {{ renderMetric(column.key, row, column.type, column.placeholder) }}
                            </template>
                        </el-table-column>
                    </el-table>
                    <div class="mt-4 flex justify-end">
                        <pagination v-model="pager" @change="getLists" />
                    </div>
                </el-card>
            </el-col>
        </el-row>
    </div>
</template>

<script setup lang="ts" name="conversionStatsPage">
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import vCharts from 'vue-echarts'

import { getConversionStatsOverview } from '@/api/stats'
import { usePaging } from '@/hooks/usePaging'

type StatsDimension = 'dept' | 'assistant' | 'doctor'

interface OptionItem {
    id: number
    name: string
}

interface MediaChannelOption {
    code: string
    name: string
}

interface MetricCard {
    key: string
    label: string
    type: 'count' | 'money' | 'percent' | 'ratio'
    placeholder?: boolean
}

interface MetricColumn extends MetricCard {
    minWidth?: number
    width?: number
    align?: 'left' | 'center' | 'right'
}

const dateRange = ref<string[]>([])

const deptTreeOptions = ref<any[]>([])
const deptOptions = ref<OptionItem[]>([])
const assistantOptions = ref<OptionItem[]>([])
const doctorOptions = ref<OptionItem[]>([])
const mediaChannelOptions = ref<MediaChannelOption[]>([])
const filtersLoaded = ref(false)
const overview = reactive<Record<string, any>>({
    dimension: 'dept',
    date_range: [],
    summary: {},
    charts: {
        ranking: {
            names: [],
            amounts: [],
            order_counts: [],
            fan_counts: [],
            rois: []
        },
        amount_share: [],
        fan_share: []
    }
})

const queryParams = reactive({
    dimension: 'dept' as StatsDimension,
    dept_id: undefined as number | undefined,
    assistant_id: undefined as number | undefined,
    doctor_id: undefined as number | undefined,
    media_channel_code: '',
    time_type: 'today',
    start_date: '',
    end_date: ''
})

const fetchOverviewList = async (params: Record<string, any>) => {
    params.include_filters = filtersLoaded.value ? 0 : 1
    if (queryParams.time_type === 'custom') {
        params.start_date = dateRange.value[0] || ''
        params.end_date = dateRange.value[1] || ''
    }
    const res = await getConversionStatsOverview(params)
    Object.assign(overview, res?.extend || {})
    applyFilterOptions(res?.extend?.filters)
    return res
}

const { pager, getLists } = usePaging({
    fetchFun: fetchOverviewList,
    params: queryParams,
    firstLoading: true
})

const dimensionOptions = [
    { label: '部门', value: 'dept' },
    { label: '医助', value: 'assistant' },
    { label: '医生', value: 'doctor' }
]
const deptTreeProps = {
    value: 'id',
    label: 'name',
    children: 'children'
}
const treeProps = {
    children: 'children'
}

const entityLabel = computed(() => {
    if (queryParams.dimension === 'assistant') return '医助'
    if (queryParams.dimension === 'doctor') return '医生'
    return '部门'
})

const currentEntityOptions = computed(() => {
    if (queryParams.dimension === 'assistant') return assistantOptions.value
    if (queryParams.dimension === 'doctor') return doctorOptions.value
    return deptOptions.value
})

const currentEntityId = computed({
    get: () => {
        if (queryParams.dimension === 'assistant') return queryParams.assistant_id
        if (queryParams.dimension === 'doctor') return queryParams.doctor_id
        return queryParams.dept_id
    },
    set: (value: number | undefined) => {
        if (queryParams.dimension === 'assistant') {
            queryParams.assistant_id = value
            return
        }
        if (queryParams.dimension === 'doctor') {
            queryParams.doctor_id = value
            return
        }
        queryParams.dept_id = value
    }
})

const summaryCards: MetricCard[] = [
    { key: 'add_fans_count', label: '加粉数', type: 'count' },
    { key: 'paid_appointment_count', label: '付费挂号', type: 'count' },
    { key: 'free_appointment_count', label: '免费挂号', type: 'count' },
    { key: 'interview_count', label: '面诊', type: 'count' },
    { key: 'completed_order_count', label: '接诊诊单', type: 'count' },
    { key: 'paid_appointment_rate', label: '付费挂号率', type: 'percent' },
    { key: 'interview_rate', label: '面诊率', type: 'percent' },
    { key: 'receive_rate', label: '接诊率', type: 'percent' },
    { key: 'interview_receive_rate', label: '面诊接诊率', type: 'percent' },
    { key: 'business_order_amount', label: '业务订单额', type: 'money' },
    { key: 'completed_order_amount', label: '诊单金额', type: 'money' },
    { key: 'avg_unit_price', label: '平均单价', type: 'money' },
    { key: 'account_cost', label: '账户消耗', type: 'money' },
    { key: 'cash_cost', label: '现金成本', type: 'money' },
    { key: 'roi', label: 'ROI', type: 'ratio' }
]

const tableColumns: MetricColumn[] = [
    { key: 'add_fans_count', label: '加粉数', type: 'count' },
    { key: 'total_open_count', label: '总开口', type: 'count', placeholder: true },
    { key: 'unreplied_count', label: '未回复', type: 'count', placeholder: true },
    { key: 'paid_appointment_count', label: '付费挂号', type: 'count' },
    { key: 'free_appointment_count', label: '免费挂号', type: 'count' },
    { key: 'appointment_total_count', label: '挂号总数', type: 'count' },
    { key: 'interview_count', label: '面诊', type: 'count' },
    { key: 'completed_order_count', label: '接诊诊单', type: 'count' },
    { key: 'completed_order_amount', label: '诊单金额', type: 'money', minWidth: 120 },
    { key: 'paid_appointment_rate', label: '付费挂号率', type: 'percent', minWidth: 120 },
    { key: 'open_appointment_rate', label: '开口挂号率', type: 'percent', placeholder: true, minWidth: 120 },
    { key: 'interview_rate', label: '面诊率', type: 'percent', minWidth: 100 },
    { key: 'receive_rate', label: '接诊率', type: 'percent', minWidth: 100 },
    { key: 'interview_receive_rate', label: '面诊接诊率', type: 'percent', minWidth: 130 },
    { key: 'open_receive_rate', label: '开口接诊率', type: 'percent', placeholder: true, minWidth: 120 },
    { key: 'avg_unit_price', label: '平均单价', type: 'money', minWidth: 110 },
    { key: 'cash_cost', label: '现金成本', type: 'money', minWidth: 110 },
    { key: 'roi', label: 'ROI', type: 'ratio', minWidth: 90 }
]

const dateRangeText = computed(() => {
    if (!overview.date_range?.length) return '未选择'
    return `${overview.date_range[0]} 至 ${overview.date_range[1]}`
})

const rankingChartHasData = computed(() => overview.charts.ranking.names.length > 0)
const amountPieHasData = computed(() => overview.charts.amount_share.length > 0)
const fanPieHasData = computed(() => overview.charts.fan_share.length > 0)

const rankingChartData = computed(() => {
    const ranking = overview.charts?.ranking || {}
    const names: unknown[] = Array.isArray(ranking.names) ? ranking.names : []
    const normalize = (values: unknown[]) => names.map((_: unknown, index: number) => Number(values?.[index] ?? 0))

    return {
        names,
        amounts: normalize(Array.isArray(ranking.amounts) ? ranking.amounts : []),
        orderCounts: normalize(Array.isArray(ranking.order_counts) ? ranking.order_counts : []),
        fanCounts: normalize(Array.isArray(ranking.fan_counts) ? ranking.fan_counts : []),
    }
})

const rankingChartOption = computed(() => ({
    tooltip: { trigger: 'axis' },
    legend: { data: ['诊单金额', '接诊诊单', '加粉数'] },
    grid: { left: 48, right: 24, top: 48, bottom: 48 },
    xAxis: {
        type: 'category',
        data: rankingChartData.value.names,
        axisLabel: {
            interval: 0,
            rotate: rankingChartData.value.names.length > 6 ? 25 : 0
        }
    },
    yAxis: [
        { type: 'value', name: '金额 / 数量' }
    ],
    series: [
        {
            name: '诊单金额',
            type: 'bar',
            barMaxWidth: 36,
            data: rankingChartData.value.amounts,
            itemStyle: { color: '#4a78ff' }
        },
        {
            name: '接诊诊单',
            type: 'line',
            smooth: true,
            showSymbol: true,
            symbolSize: 8,
            lineStyle: { width: 3 },
            data: rankingChartData.value.orderCounts,
            itemStyle: { color: '#15b8a6' }
        },
        {
            name: '加粉数',
            type: 'line',
            smooth: true,
            showSymbol: true,
            symbolSize: 8,
            lineStyle: { width: 3 },
            data: rankingChartData.value.fanCounts,
            itemStyle: { color: '#ff9f43' }
        }
    ]
}))

const amountPieOption = computed(() => ({
    tooltip: { trigger: 'item' },
    legend: {
        bottom: 0,
        type: 'scroll',
        pageIconColor: '#409eff',
        pageTextStyle: {
            color: '#909399',
        },
    },
    series: [
        {
            name: '诊单金额',
            type: 'pie',
            radius: ['42%', '70%'],
            center: ['50%', '42%'],
            avoidLabelOverlap: true,
            minAngle: 3,
            label: {
                show: true,
                formatter: '{b}',
                overflow: 'truncate',
                width: 90,
            },
            labelLayout: {
                hideOverlap: true,
            },
            data: overview.charts.amount_share
        }
    ]
}))

const fanPieOption = computed(() => ({
    tooltip: { trigger: 'item' },
    legend: {
        bottom: 0,
        type: 'scroll',
        pageIconColor: '#409eff',
        pageTextStyle: {
            color: '#909399',
        },
    },
    series: [
        {
            name: '加粉数',
            type: 'pie',
            radius: ['42%', '70%'],
            center: ['50%', '42%'],
            avoidLabelOverlap: true,
            minAngle: 3,
            label: {
                show: true,
                formatter: '{b}',
                overflow: 'truncate',
                width: 90,
            },
            labelLayout: {
                hideOverlap: true,
            },
            data: overview.charts.fan_share
        }
    ]
}))

const flattenDepts = (items: any[]): OptionItem[] => {
    let result: OptionItem[] = []
    items.forEach((item) => {
        result.push({ id: Number(item.id), name: item.name })
        if (Array.isArray(item.children) && item.children.length > 0) {
            result = result.concat(flattenDepts(item.children))
        }
    })
    return result
}

const applyFilterOptions = (filters?: Record<string, any>) => {
    if (!filters) return
    filtersLoaded.value = true
    const departments = Array.isArray(filters.departments) ? filters.departments : []
    const assistants = Array.isArray(filters.assistants) ? filters.assistants : []
    const doctors = Array.isArray(filters.doctors) ? filters.doctors : []
    const mediaChannels = Array.isArray(filters.media_channels) ? filters.media_channels : []

    deptTreeOptions.value = departments
    deptOptions.value = flattenDepts(departments)
    assistantOptions.value = assistants.map((item: any) => ({
        id: Number(item.id),
        name: item.name,
    }))
    doctorOptions.value = doctors.map((item: any) => ({
        id: Number(item.id),
        name: item.name,
    }))
    mediaChannelOptions.value = mediaChannels.map((item: any) => ({
        code: String(item.code || ''),
        name: String(item.name || ''),
    }))
}

const fetchOverview = async () => {
    try {
        await getLists()
    } catch (error: any) {
        console.error('获取统计概览失败:', error)
        ElMessage.error(error?.msg || '获取统计数据失败')
    }
}

const handleDimensionChange = () => {
    queryParams.dept_id = undefined
    queryParams.assistant_id = undefined
    queryParams.doctor_id = undefined
    pager.page = 1
    fetchOverview()
}

const handleEntityChange = () => {
    pager.page = 1
    fetchOverview()
}

const handleMediaChannelChange = () => {
    pager.page = 1
    fetchOverview()
}

const handleTimeTypeChange = () => {
    if (queryParams.time_type !== 'custom') {
        dateRange.value = []
        pager.page = 1
        fetchOverview()
    }
}

const handleCustomDateChange = () => {
    if (dateRange.value.length === 2) {
        pager.page = 1
        fetchOverview()
    }
}

const handleQuery = () => {
    pager.page = 1
    fetchOverview()
}

const handleReset = () => {
    queryParams.dimension = 'dept'
    queryParams.dept_id = undefined
    queryParams.assistant_id = undefined
    queryParams.doctor_id = undefined
    queryParams.media_channel_code = ''
    queryParams.time_type = 'today'
    dateRange.value = []
    pager.page = 1
    pager.size = 15
    fetchOverview()
}

const renderMetric = (key: string, source: Record<string, any>, type = 'count', placeholder = false) => {
    if (placeholder) return '—'
    const value = source?.[key] ?? 0
    if (type === 'money') return `¥${Number(value || 0).toFixed(2)}`
    if (type === 'percent') return `${Number(value || 0).toFixed(2)}%`
    if (type === 'ratio') return Number(value || 0).toFixed(2)
    return String(value ?? 0)
}

onMounted(async () => {
    await fetchOverview()
})
</script>

<style scoped lang="scss">
.conversion-stats-page {
    padding: 20px;
}

.stats-filter-form {
    :deep(.el-form-item) {
        margin-bottom: 0;
    }
}

.stats-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: 16px;
    margin-top: 16px;
}

.stats-kpi-card {
    background: linear-gradient(145deg, #ffffff, #f5f8ff);
    border: 1px solid #ebf1ff;
    border-radius: 14px;
    padding: 18px 16px;
    box-shadow: 0 10px 24px rgba(74, 120, 255, 0.08);
}

.stats-kpi-label {
    color: #6b7280;
    font-size: 13px;
}

.stats-kpi-value {
    margin-top: 12px;
    font-size: 28px;
    line-height: 1.1;
    font-weight: 700;
    color: #1f2937;
}

.stats-kpi-value.is-money {
    font-size: 24px;
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
}

.card-hint {
    color: #909399;
    font-size: 12px;
}

.dept-name-cell {
    display: inline-block;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}

:deep(.dept-name-column .cell) {
    white-space: nowrap;
}

.stats-chart {
    height: 360px;
}
</style>
