<template>
    <div class="default-main">
        <el-alert type="info" :title="tip" show-icon class="mb-12" />
        <el-alert :type="connected ? 'success' : 'warning'" :title="connected ? t('test.ws.ws_connected') : t('test.ws.ws_disconnected')" show-icon class="mb-12" />

        <el-card shadow="never" class="mb-12">
            <el-form @submit.prevent>
                <el-form-item :label="t('test.ws.config_api')">
                    <el-input :model-value="configApi" readonly />
                </el-form-item>
                <el-form-item :label="t('test.ws.ws_url')">
                    <el-input v-model="wsUrl" :placeholder="t('test.ws.ws_url_placeholder')" />
                </el-form-item>
                <el-form-item :label="t('test.ws.send_message')">
                    <el-input v-model="sendMessage" type="textarea" :rows="4" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" :loading="loading" @click="loadConfig">{{ t('test.ws.btn_load_config') }}</el-button>
                    <el-button type="success" :disabled="!ready || connected" @click="connectWs">{{ t('test.ws.btn_connect') }}</el-button>
                    <el-button :disabled="!connected" @click="disconnectWs">{{ t('test.ws.btn_disconnect') }}</el-button>
                    <el-button :loading="sending" :disabled="!connected" @click="sendWs">{{ t('test.ws.btn_send') }}</el-button>
                    <el-button @click="clearLogs">{{ t('test.ws.btn_clear') }}</el-button>
                </el-form-item>
            </el-form>
            <div class="text-muted mb-8">{{ connectTip || '-' }}</div>
            <template v-if="ready && subscribeTopics.length">
                <div class="mb-4">
                    <span class="config-label">{{ t('test.ws.subscribe_topics') }}：</span>
                    <el-tag v-for="topic in subscribeTopics" :key="topic" size="small" class="mr-4 mb-4">{{ topic }}</el-tag>
                </div>
                <div v-if="oddsPushTopics.length" class="mb-4">
                    <span class="config-label">{{ t('test.ws.odds_push_topics') }}：</span>
                    <el-tag v-for="topic in oddsPushTopics" :key="'odds-' + topic" type="warning" size="small" class="mr-4 mb-4">{{ topic }}</el-tag>
                </div>
                <div v-if="playerOddsFields.length" class="mb-4">
                    <span class="config-label">{{ t('test.ws.player_odds_fields') }}：</span>
                    <el-tag v-for="field in playerOddsFields" :key="field" type="success" size="small" class="mr-4 mb-4">{{ field }}</el-tag>
                </div>
            </template>
            <el-card v-if="testPlayerOdds" shadow="never" class="test-odds-card">
                <template #header>
                    <span>{{ t('test.ws.test_player_odds') }}</span>
                    <el-tag type="info" size="small" class="ml-8">{{ testPlayerOddsSourceLabel }}</el-tag>
                </template>
                <p class="text-muted mb-8">{{ t('test.ws.test_player_odds_hint') }}</p>
                <el-descriptions :column="2" border size="small">
                    <el-descriptions-item label="user_id">{{ testPlayerOdds.user_id }}</el-descriptions-item>
                    <el-descriptions-item label="username">{{ testPlayerOdds.username || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="uuid">{{ testPlayerOdds.uuid || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="phone">{{ testPlayerOdds.phone || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="coin">{{ testPlayerOdds.coin }}</el-descriptions-item>
                    <el-descriptions-item label="current_streak">{{ testPlayerOdds.current_streak }}</el-descriptions-item>
                    <el-descriptions-item label="streak_level">{{ testPlayerOdds.streak_level }}</el-descriptions-item>
                    <el-descriptions-item label="odds_factor">{{ testPlayerOdds.odds_factor }}</el-descriptions-item>
                    <el-descriptions-item label="is_jackpot">{{ testPlayerOdds.is_jackpot ? 'true' : 'false' }}</el-descriptions-item>
                </el-descriptions>
            </el-card>
        </el-card>

        <el-card shadow="never">
            <template #header>{{ t('test.ws.log_title') }}</template>
            <el-scrollbar max-height="480">
                <pre class="ws-log">{{ logText }}</pre>
            </el-scrollbar>
        </el-card>
    </div>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import createAxios from '/@/utils/axios'

const props = withDefaults(
    defineProps<{
        /** 如 /admin/test.GameCurrentStatus/wsConfig */
        configApi: string
        tip: string
    }>(),
    {}
)

const { t } = useI18n()
const loading = ref(false)
const sending = ref(false)
const ready = ref(false)
const connected = ref(false)
const wsUrl = ref('')
const connectTip = ref('')
const sendMessage = ref('')
/** 连接成功后自动订阅（由 wsConfig.subscribe_topics 下发） */
const subscribeTopics = ref<string[]>([])
const oddsPushTopics = ref<string[]>([])
const playerOddsFields = ref<string[]>([])
const testPlayerOdds = ref<Record<string, unknown> | null>(null)
const ws = ref<WebSocket | null>(null)
const logs = ref<Array<{ t: number; event: string; payload: string }>>([])

const defaultSubscribeTopics = [
    'period.tick',
    'period.locked',
    'period.opened',
    'period.payout',
    'period.payout.tick',
    'bet.win',
    'user.streak',
    'wallet.changed',
    'bet.accepted',
    'jackpot.hit',
    'auto.spin.progress',
    'admin.live.snapshot',
    'admin.live.opened',
] as const

const testPlayerOddsSourceLabel = computed(() => {
    const source = testPlayerOdds.value?.source
    if (source === 'db_user') {
        return t('test.ws.test_source_db')
    }
    if (source === 'synthetic') {
        return t('test.ws.test_source_synthetic')
    }
    return ''
})

const logText = computed(() => {
    if (!logs.value.length) return t('test.ws.log_empty')
    return logs.value
        .map((row) => {
            const time = new Date(row.t).toLocaleString()
            return `[${time}] ${row.event}\n${row.payload}`
        })
        .join('\n\n')
})

function appendLog(line: { t: number; event: string; payload: string }) {
    logs.value = [line, ...logs.value].slice(0, 200)
}

function clearLogs() {
    logs.value = []
}

async function loadConfig() {
    loading.value = true
    try {
        const res = await createAxios({
            url: props.configApi,
            method: 'get',
            showCodeMessage: true,
        })
        if (res.code !== 1 || !res.data) {
            ready.value = false
            return
        }
        wsUrl.value = String(res.data.ws_url || '')
        connectTip.value = String(res.data.connect_tip || '')
        const rawTopics = res.data.subscribe_topics
        if (Array.isArray(rawTopics)) {
            subscribeTopics.value = rawTopics.filter((x: unknown): x is string => typeof x === 'string' && x.trim() !== '')
        } else {
            subscribeTopics.value = [...defaultSubscribeTopics]
        }
        const rawOddsTopics = res.data.odds_push_topics
        if (Array.isArray(rawOddsTopics)) {
            oddsPushTopics.value = rawOddsTopics.filter((x: unknown): x is string => typeof x === 'string' && x.trim() !== '')
        } else {
            oddsPushTopics.value = ['user.streak', 'wallet.changed', 'bet.accepted']
        }
        const rawOddsFields = res.data.player_odds_fields
        if (Array.isArray(rawOddsFields)) {
            playerOddsFields.value = rawOddsFields.filter((x: unknown): x is string => typeof x === 'string' && x.trim() !== '')
        } else {
            playerOddsFields.value = ['current_streak', 'streak_level', 'odds_factor', 'is_jackpot']
        }
        const rawTestOdds = res.data.test_player_odds
        testPlayerOdds.value = rawTestOdds && typeof rawTestOdds === 'object' ? (rawTestOdds as Record<string, unknown>) : null
        const firstSample = Array.isArray(res.data.sample_messages) && res.data.sample_messages.length ? String(res.data.sample_messages[0]) : ''
        sendMessage.value = firstSample
        ready.value = wsUrl.value !== ''
        appendLog({
            t: Date.now(),
            event: 'config.loaded',
            payload: JSON.stringify(res.data, null, 2),
        })
    } finally {
        loading.value = false
    }
}

function connectWs() {
    if (!ready.value || !wsUrl.value) {
        return
    }
    disconnectWs()
    const socket = new WebSocket(wsUrl.value)
    ws.value = socket
    socket.onopen = () => {
        connected.value = true
        appendLog({
            t: Date.now(),
            event: 'ws.open',
            payload: wsUrl.value,
        })
        const topics = subscribeTopics.value.length > 0 ? subscribeTopics.value : [...defaultSubscribeTopics]
        const subscribePayload = JSON.stringify({ action: 'subscribe', topics })
        socket.send(subscribePayload)
        appendLog({
            t: Date.now(),
            event: 'ws.auto_subscribe',
            payload: subscribePayload,
        })
    }
    socket.onmessage = (event) => {
        const raw = typeof event.data === 'string' ? event.data : JSON.stringify(event.data)
        let eventName = 'ws.message'
        try {
            const parsed = JSON.parse(raw) as { event?: string; topic?: string }
            if (typeof parsed.event === 'string' && parsed.event !== '') {
                eventName = parsed.event
            } else if (typeof parsed.topic === 'string' && parsed.topic !== '') {
                eventName = parsed.topic
            }
        } catch {
            // keep default event name
        }
        appendLog({
            t: Date.now(),
            event: eventName,
            payload: raw,
        })
    }
    socket.onerror = () => {
        appendLog({
            t: Date.now(),
            event: 'ws.error',
            payload: 'WebSocket error',
        })
    }
    socket.onclose = () => {
        connected.value = false
        ws.value = null
        appendLog({
            t: Date.now(),
            event: 'ws.close',
            payload: wsUrl.value,
        })
    }
}

function disconnectWs() {
    if (ws.value) {
        ws.value.close()
        ws.value = null
    }
    connected.value = false
}

function sendWs() {
    if (!ws.value || !connected.value) {
        return
    }
    const payload = sendMessage.value.trim()
    if (!payload) {
        return
    }
    sending.value = true
    try {
        ws.value.send(payload)
        appendLog({
            t: Date.now(),
            event: 'ws.send',
            payload,
        })
    } finally {
        sending.value = false
    }
}

onMounted(() => {
    void loadConfig()
})

onUnmounted(() => {
    disconnectWs()
    clearLogs()
})
</script>

<style scoped>
.ws-log {
    margin: 0;
    font-size: 12px;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-all;
}
.text-muted {
    color: var(--el-text-color-secondary);
    font-size: 13px;
}
.config-label {
    color: var(--el-text-color-regular);
    font-size: 13px;
    margin-right: 8px;
}
.mr-4 {
    margin-right: 4px;
}
.mb-4 {
    margin-bottom: 4px;
}
.test-odds-card {
    margin-top: 12px;
}
.ml-8 {
    margin-left: 8px;
}
</style>
