<template>
    <view class="assistant-page">
        <view class="assistant-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
            <view class="assistant-nav__btn" @tap="goBack">
                <u-icon name="arrow-left" size="34" color="#1f2937" />
            </view>
            <text class="assistant-nav__title">世博头条 AI 助手</text>
            <view class="assistant-nav__actions">
                <view class="assistant-nav__btn" @tap="toggleHistory">
                    <u-icon name="clock" size="32" color="#1f2937" />
                </view>
                <view class="assistant-nav__btn" @tap="newSession">
                    <u-icon name="plus" size="32" color="#1f2937" />
                </view>
            </view>
        </view>

        <view v-if="showHistory" class="history-panel">
            <view class="history-panel__header">
                <text>历史会话</text>
                <text class="history-panel__clear" @tap="clearAllSessions">清空</text>
            </view>
            <scroll-view scroll-y class="history-panel__list">
                <view v-if="!sessions.length" class="history-panel__empty">暂无历史会话</view>
                <view v-for="item in sessions" :key="item.id" class="history-item" @tap="openSession(item)">
                    <text class="history-item__title">{{ item.title || '新的对话' }}</text>
                    <text class="history-item__desc">{{ item.last_message || item.last_answer }}</text>
                </view>
            </scroll-view>
        </view>

        <scroll-view
            scroll-y
            class="message-list"
            :scroll-into-view="scrollIntoView"
            scroll-with-animation
        >
            <view class="welcome">
                <view class="welcome__badge">AI</view>
                <view class="welcome__body">
                    <text class="welcome__title">你好，我可以帮你查站内内容</text>
                    <text class="welcome__desc">可以问资讯、赛事、社区帖子、彩票分析和加密行情。</text>
                </view>
            </view>

            <view class="quick-list">
                <view v-for="item in quickQuestions" :key="item" class="quick-list__item" @tap="sendQuick(item)">
                    <text class="quick-list__text">{{ item }}</text>
                </view>
            </view>

            <view
                v-for="(item, index) in messages"
                :id="'msg-' + index"
                :key="item.id || index"
                class="message-row"
                :class="item.role === 'user' ? 'message-row--user' : 'message-row--assistant'"
            >
                <view v-if="item.role !== 'user'" class="message-row__avatar">AI</view>
                <view class="message-row__body">
                    <view class="message-bubble">
                        <view v-if="item.pending" class="message-bubble__pending">
                            <view class="message-bubble__pulse" />
                            <text class="message-bubble__loading">正在整理站内资料...</text>
                            <text class="message-bubble__timer">分析用时 {{ analysisDurationText }}</text>
                        </view>
                        <text v-else-if="item.role === 'user'" class="message-bubble__text">{{ item.content }}</text>
                        <view v-else class="message-bubble__markdown">
                            <u-parse :html="renderAssistantMarkdown(item.content)" />
                        </view>
                        <view v-if="item.sources && item.sources.length" class="source-list">
                            <view v-for="source in item.sources" :key="source.type + source.source_id + source.title"
                                class="source-item" @tap="openSource(source)">
                                <view class="source-item__meta">{{ sourceTypeName(source.type) }}</view>
                                <text class="source-item__title">{{ source.title }}</text>
                                <text class="source-item__summary">{{ source.summary }}</text>
                            </view>
                        </view>
                    </view>
                </view>
            </view>
        </scroll-view>

        <view class="input-panel">
            <view class="input-bar">
                <input
                    v-model="inputText"
                    class="input-bar__input"
                    type="text"
                    maxlength="500"
                    :cursor-spacing="18"
                    :disabled="sending"
                    placeholder="问问世博头条里的内容"
                    confirm-type="send"
                    @confirm="sendMessage"
                />
                <view class="input-bar__send" :class="{ 'input-bar__send--disabled': !canSend }" @tap="sendMessage">
                    <u-icon name="arrow-upward" size="32" color="#fff" />
                </view>
            </view>
        </view>
    </view>
</template>

<script setup lang="ts">
import { computed, nextTick, onUnmounted, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import {
    assistantChat,
    assistantClear,
    assistantMessages,
    assistantSessions,
    type AssistantMessage,
    type AssistantSession,
    type AssistantSource
} from '@/api/assistant'
import { renderMarkdownToHtml } from '@/utils/assistant-markdown'

const STORAGE_CLIENT_ID = 'sbnews_assistant_client_id'

const statusBarHeight = ref(0)
const clientId = ref('')
const sessionId = ref(0)
const inputText = ref('')
const sending = ref(false)
const showHistory = ref(false)
const scrollIntoView = ref('')
const messages = ref<AssistantMessage[]>([])
const sessions = ref<AssistantSession[]>([])
const analysisElapsedSeconds = ref(0)
let analysisTimer: ReturnType<typeof setInterval> | null = null

const quickQuestions = [
    '今天有什么值得关注的赛事？',
    '帮我总结最新世界杯资讯',
    '旧澳六合最近有什么分析线索？',
    'BTC 今天行情怎么样？'
]

const canSend = computed(() => inputText.value.trim().length > 0 && !sending.value)
const analysisDurationText = computed(() => {
    const total = Math.max(0, Number(analysisElapsedSeconds.value || 0))
    const minutes = Math.floor(total / 60)
    const seconds = total % 60
    return `${minutes}m${seconds}s`
})

onLoad(async () => {
    const sysInfo = uni.getSystemInfoSync()
    // #ifdef APP-PLUS || MP
    statusBarHeight.value = sysInfo.statusBarHeight || 44
    // #endif
    // #ifdef H5
    statusBarHeight.value = 0
    // #endif
    clientId.value = getClientId()
    await loadSessions()
})

const getClientId = () => {
    const cached = uni.getStorageSync(STORAGE_CLIENT_ID)
    if (cached) return String(cached)
    const id = `c_${Date.now()}_${Math.random().toString(16).slice(2)}`
    uni.setStorageSync(STORAGE_CLIENT_ID, id)
    return id
}

const loadSessions = async () => {
    try {
        sessions.value = await assistantSessions({ client_id: clientId.value })
    } catch (e) {
        sessions.value = []
    }
}

const openSession = async (session: AssistantSession) => {
    try {
        const res = await assistantMessages({ client_id: clientId.value, session_id: session.id })
        sessionId.value = res.session?.id || session.id
        messages.value = res.messages || []
        showHistory.value = false
        scrollToBottom()
    } catch (e) {
        uni.showToast({ title: '会话加载失败', icon: 'none' })
    }
}

const newSession = () => {
    sessionId.value = 0
    messages.value = []
    showHistory.value = false
    inputText.value = ''
}

const toggleHistory = async () => {
    showHistory.value = !showHistory.value
    if (showHistory.value) {
        await loadSessions()
    }
}

const sendQuick = (text: string) => {
    inputText.value = text
    sendMessage()
}

const renderAssistantMarkdown = (content: string) => renderMarkdownToHtml(content)

const sendMessage = async () => {
    const text = inputText.value.trim()
    if (!text || sending.value) return

    const userMessage: AssistantMessage = {
        id: Date.now(),
        session_id: sessionId.value,
        role: 'user',
        content: text
    }
    const pendingMessage: AssistantMessage = {
        id: Date.now() + 1,
        session_id: sessionId.value,
        role: 'assistant',
        content: '',
        pending: true
    }
    messages.value.push(userMessage, pendingMessage)
    inputText.value = ''
    sending.value = true
    startAnalysisTimer()
    scrollToBottom()

    try {
        const res = await assistantChat({
            message: text,
            client_id: clientId.value,
            session_id: sessionId.value || undefined
        })
        sessionId.value = res.session?.id || sessionId.value
        const answer = res.message || {
            role: 'assistant',
            content: '我暂时没有生成成功，请稍后再试。',
            sources: res.sources || []
        }
        messages.value.splice(messages.value.length - 1, 1, answer)
        await loadSessions()
    } catch (e: any) {
        console.error('[ai_assistant] chat failed:', e)
        const recovered = await recoverLatestAssistantReply(text)
        if (recovered) return
        messages.value.splice(messages.value.length - 1, 1, {
            id: Date.now() + 2,
            session_id: sessionId.value,
            role: 'assistant',
            content: getAssistantErrorMessage(e),
            sources: []
        })
    } finally {
        stopAnalysisTimer()
        sending.value = false
        scrollToBottom()
    }
}

const recoverLatestAssistantReply = async (question: string) => {
    try {
        await loadSessions()
        const matchedSession = sessions.value.find((item) => item.last_message === question)
        if (!matchedSession?.id) return false

        const res = await assistantMessages({ client_id: clientId.value, session_id: matchedSession.id })
        const serverMessages = res.messages || []
        const hasMatchedQuestion = serverMessages.some((item) => item.role === 'user' && item.content === question)
        const hasAssistantReply = serverMessages.some((item) => item.role === 'assistant' && !item.pending)
        if (!hasMatchedQuestion || !hasAssistantReply) return false

        sessionId.value = res.session?.id || matchedSession.id
        messages.value = serverMessages
        await loadSessions()
        scrollToBottom()
        return true
    } catch (e) {
        console.error('[ai_assistant] recover latest reply failed:', e)
        return false
    }
}

const getRawAssistantErrorMessage = (error: any) => {
    return typeof error === 'string'
        ? error
        : String(error?.msg || error?.errMsg || error?.message || error?.data?.msg || '')
}

const getAssistantErrorMessage = (error: any) => {
    const rawMessage = getRawAssistantErrorMessage(error)

    if (/timeout|超时/i.test(rawMessage)) {
        return 'AI助手响应时间较长，请稍后再试；当前请求已超时。'
    }
    if (/abort|cancel/i.test(rawMessage)) {
        return 'AI助手请求已取消，请重新发送问题。'
    }
    if (/request:fail|network|网络/i.test(rawMessage)) {
        return rawMessage
            ? `AI助手请求失败，请检查网络后重试。错误详情：${rawMessage}`
            : 'AI助手请求失败，请检查网络后重试。'
    }

    return rawMessage || 'AI助手请求失败，请稍后重试。'
}

const startAnalysisTimer = () => {
    stopAnalysisTimer()
    analysisElapsedSeconds.value = 0
    const startAt = Date.now()
    analysisTimer = setInterval(() => {
        analysisElapsedSeconds.value = Math.floor((Date.now() - startAt) / 1000)
    }, 1000)
}

const stopAnalysisTimer = () => {
    if (analysisTimer) {
        clearInterval(analysisTimer)
        analysisTimer = null
    }
}

const clearAllSessions = () => {
    uni.showModal({
        title: '清空历史',
        content: '确定清空所有助手会话？',
        success: async (res) => {
            if (!res.confirm) return
            try {
                await assistantClear({ client_id: clientId.value })
                sessions.value = []
                newSession()
            } catch (e) {
                uni.showToast({ title: '清空失败', icon: 'none' })
            }
        }
    })
}

const scrollToBottom = () => {
    nextTick(() => {
        scrollIntoView.value = 'msg-' + Math.max(messages.value.length - 1, 0)
    })
}

const openSource = (source: AssistantSource) => {
    if (!source.path) return
    if (source.path === '/pages/crypto/crypto' || source.path === '/pages/lottery_analysis/lottery_analysis') {
        uni.switchTab({ url: source.path })
        return
    }
    uni.navigateTo({ url: source.path })
}

const sourceTypeName = (type: string) => {
    const map: Record<string, string> = {
        article: '资讯',
        post: '社区',
        match: '赛事',
        lottery: '彩票',
        crypto: '行情',
        web_news: '外网资讯'
    }
    return map[type] || '来源'
}

const goBack = () => {
    const pages = getCurrentPages()
    if (pages.length > 1) {
        uni.navigateBack()
        return
    }
    uni.switchTab({ url: '/pages/index/index' })
}

onUnmounted(() => {
    stopAnalysisTimer()
})
</script>

<style lang="scss" scoped>
.assistant-page {
    height: 100vh;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: #f3f6fa;
    overflow: hidden;
}

.assistant-nav {
    flex-shrink: 0;
    min-height: 96rpx;
    padding-left: 20rpx;
    padding-right: 20rpx;
    background: rgba(255, 255, 255, 0.96);
    display: flex;
    align-items: center;
    border-bottom: 1rpx solid rgba(211, 220, 232, 0.8);
    box-shadow: 0 8rpx 28rpx rgba(31, 41, 55, 0.05);

    &__title {
        flex: 1;
        min-width: 0;
        text-align: center;
        font-size: 32rpx;
        font-weight: 700;
        color: #111827;
    }

    &__actions {
        display: flex;
        align-items: center;
        gap: 8rpx;
    }

    &__btn {
        width: 64rpx;
        height: 64rpx;
        border-radius: 8rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    &__btn:active {
        background: #eef3fb;
    }
}

.history-panel {
    flex-shrink: 0;
    max-height: 46vh;
    background: #fff;
    border-bottom: 1rpx solid #dbe6f5;
    box-shadow: 0 18rpx 36rpx rgba(38, 55, 77, 0.08);

    &__header {
        height: 80rpx;
        padding: 0 28rpx;
        display: flex;
        align-items: center;
        justify-content: space-between;
        font-size: 26rpx;
        color: #111827;
        font-weight: 600;
    }

    &__clear {
        color: #185dff;
        font-weight: 500;
    }

    &__list {
        max-height: calc(46vh - 80rpx);
    }

    &__empty {
        padding: 48rpx 0;
        text-align: center;
        font-size: 24rpx;
        color: #9ca3af;
    }
}

.history-item {
    padding: 22rpx 28rpx 24rpx;
    border-top: 1rpx solid #f1f5f9;
    background: #fff;

    &__title {
        display: block;
        font-size: 28rpx;
        color: #111827;
        font-weight: 600;
    }

    &__desc {
        display: block;
        margin-top: 6rpx;
        font-size: 24rpx;
        color: #6b7280;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.message-list {
    flex: 1;
    min-height: 0;
    padding: 24rpx 24rpx 36rpx;
    box-sizing: border-box;
}

.welcome {
    display: flex;
    gap: 18rpx;
    padding: 24rpx 24rpx 26rpx;
    background: #fff;
    border: 1rpx solid rgba(214, 224, 237, 0.92);
    border-radius: 16rpx;
    box-shadow: 0 12rpx 28rpx rgba(41, 60, 87, 0.06);

    &__badge {
        width: 60rpx;
        height: 60rpx;
        border-radius: 14rpx;
        background: #185dff;
        color: #fff;
        font-size: 24rpx;
        font-weight: 700;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    &__body {
        flex: 1;
        min-width: 0;
    }

    &__title {
        display: block;
        font-size: 31rpx;
        color: #111827;
        font-weight: 700;
        line-height: 1.35;
    }

    &__desc {
        display: block;
        margin-top: 10rpx;
        font-size: 24rpx;
        color: #6b7280;
        line-height: 1.5;
    }
}

.quick-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14rpx;
    margin: 22rpx 0 32rpx;

    &__item {
        min-height: 62rpx;
        padding: 12rpx 18rpx;
        border-radius: 14rpx;
        background: #fff;
        border: 1rpx solid rgba(207, 220, 238, 0.95);
        color: #185dff;
        font-size: 24rpx;
        line-height: 1.35;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        box-shadow: 0 8rpx 20rpx rgba(24, 93, 255, 0.04);
    }

    &__text {
        display: block;
        max-width: 100%;
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }

    &__item:active {
        background: #edf4ff;
        border-color: #b9cff7;
    }
}

.message-row {
    display: flex;
    align-items: flex-start;
    margin-bottom: 26rpx;

    &--user {
        justify-content: flex-end;

        .message-row__body {
            max-width: 82%;
            align-items: flex-end;
        }

        .message-bubble {
            background: #185dff;
            border-color: #185dff;
            color: #fff;
            box-shadow: 0 12rpx 28rpx rgba(24, 93, 255, 0.22);
        }
    }

    &--assistant {
        justify-content: flex-start;

        .message-row__body {
            max-width: calc(100% - 78rpx);
            align-items: flex-start;
        }

        .message-bubble {
            background: #fff;
            color: #111827;
            border: 1rpx solid rgba(214, 224, 237, 0.95);
            box-shadow: 0 12rpx 30rpx rgba(39, 55, 77, 0.07);
        }
    }

    &__avatar {
        width: 58rpx;
        height: 58rpx;
        margin-right: 16rpx;
        border-radius: 14rpx;
        background: #111827;
        color: #fff;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 22rpx;
        font-weight: 700;
        flex-shrink: 0;
        box-shadow: 0 10rpx 20rpx rgba(17, 24, 39, 0.14);
    }

    &__body {
        min-width: 0;
        display: flex;
        flex-direction: column;
    }
}

.message-bubble {
    max-width: 100%;
    padding: 20rpx 24rpx;
    border-radius: 16rpx;
    box-sizing: border-box;

    &__pending {
        display: flex;
        flex-direction: column;
        gap: 8rpx;
    }

    &__pulse {
        width: 36rpx;
        height: 8rpx;
        border-radius: 8rpx;
        background: #185dff;
        opacity: 0.78;
    }

    &__text,
    &__loading,
    &__markdown {
        font-size: 28rpx;
        line-height: 1.72;
        white-space: pre-wrap;
        word-break: break-word;
    }

    &__markdown {
        white-space: normal;
    }

    &__loading {
        color: #6b7280;
    }

    &__timer {
        font-size: 22rpx;
        line-height: 1.4;
        color: #0f7a5f;
        font-weight: 600;
    }
}

.message-bubble__markdown :deep(*) {
    box-sizing: border-box;
}

.message-bubble__markdown :deep(p),
.message-bubble__markdown :deep(ul),
.message-bubble__markdown :deep(ol),
.message-bubble__markdown :deep(blockquote),
.message-bubble__markdown :deep(pre),
.message-bubble__markdown :deep(hr) {
    margin: 0 0 18rpx;
}

.message-bubble__markdown :deep(h1),
.message-bubble__markdown :deep(h2),
.message-bubble__markdown :deep(h3),
.message-bubble__markdown :deep(h4),
.message-bubble__markdown :deep(h5),
.message-bubble__markdown :deep(h6) {
    margin: 0 0 16rpx;
    color: inherit;
    font-weight: 700;
    line-height: 1.45;
}

.message-bubble__markdown :deep(h1) {
    font-size: 34rpx;
}

.message-bubble__markdown :deep(h2) {
    font-size: 32rpx;
}

.message-bubble__markdown :deep(h3) {
    font-size: 30rpx;
}

.message-bubble__markdown :deep(ul),
.message-bubble__markdown :deep(ol) {
    padding-left: 34rpx;
}

.message-bubble__markdown :deep(li) {
    margin-bottom: 10rpx;
    line-height: 1.72;
}

.message-bubble__markdown :deep(blockquote) {
    padding: 14rpx 18rpx;
    border-left: 6rpx solid #bfd5ff;
    border-radius: 0 12rpx 12rpx 0;
    background: #f6f9ff;
    color: #4b5563;
}

.message-bubble__markdown :deep(code) {
    padding: 2rpx 10rpx;
    border-radius: 8rpx;
    background: #eff3f8;
    color: #0f172a;
    font-size: 24rpx;
}

.message-bubble__markdown :deep(pre) {
    overflow-x: auto;
    padding: 18rpx;
    border-radius: 14rpx;
    background: #0f172a;
}

.message-bubble__markdown :deep(pre code) {
    display: block;
    padding: 0;
    background: transparent;
    color: #f8fafc;
    white-space: pre-wrap;
    word-break: break-word;
}

.message-bubble__markdown :deep(a) {
    color: #185dff;
    text-decoration: underline;
    word-break: break-all;
}

.message-bubble__markdown :deep(strong) {
    font-weight: 700;
}

.message-bubble__markdown :deep(hr) {
    border: 0;
    border-top: 1rpx solid #dbe4f0;
}

.message-bubble__markdown :deep(img) {
    max-width: 100%;
    height: auto;
    border-radius: 12rpx;
}

.message-bubble__markdown :deep(table) {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 18rpx;
    overflow: hidden;
    border-radius: 12rpx;
    background: #fff;
    border: 1rpx solid #dbe4f0;
}

.message-bubble__markdown :deep(th),
.message-bubble__markdown :deep(td) {
    padding: 12rpx 14rpx;
    border: 1rpx solid #dbe4f0;
    text-align: left;
    vertical-align: top;
    line-height: 1.6;
    font-size: 24rpx;
}

.message-bubble__markdown :deep(th) {
    background: #f6f9ff;
    font-weight: 700;
}

.message-bubble__markdown :deep(p:last-child),
.message-bubble__markdown :deep(ul:last-child),
.message-bubble__markdown :deep(ol:last-child),
.message-bubble__markdown :deep(blockquote:last-child),
.message-bubble__markdown :deep(pre:last-child),
.message-bubble__markdown :deep(table:last-child),
.message-bubble__markdown :deep(hr:last-child),
.message-bubble__markdown :deep(h1:last-child),
.message-bubble__markdown :deep(h2:last-child),
.message-bubble__markdown :deep(h3:last-child),
.message-bubble__markdown :deep(h4:last-child),
.message-bubble__markdown :deep(h5:last-child),
.message-bubble__markdown :deep(h6:last-child) {
    margin-bottom: 0;
}

.source-list {
    margin-top: 18rpx;
    display: flex;
    flex-direction: column;
    gap: 12rpx;
}

.source-item {
    position: relative;
    padding: 18rpx 18rpx 18rpx 22rpx;
    border-radius: 14rpx;
    background: #f8fafc;
    border: 1rpx solid #e2e8f0;
    overflow: hidden;

    &::before {
        content: '';
        position: absolute;
        top: 18rpx;
        bottom: 18rpx;
        left: 0;
        width: 6rpx;
        border-radius: 0 6rpx 6rpx 0;
        background: #0f7a5f;
    }

    &__meta {
        display: inline-flex;
        align-items: center;
        height: 34rpx;
        padding: 0 12rpx;
        border-radius: 6rpx;
        background: #e8f6f1;
        color: #0f7a5f;
        font-size: 20rpx;
        font-weight: 600;
    }

    &__title {
        display: block;
        margin-top: 10rpx;
        font-size: 25rpx;
        color: #111827;
        font-weight: 600;
        line-height: 1.4;
    }

    &__summary {
        display: block;
        margin-top: 6rpx;
        font-size: 22rpx;
        color: #6b7280;
        line-height: 1.45;
    }
}

.input-panel {
    flex-shrink: 0;
    padding-top: 16rpx;
    padding-left: 22rpx;
    padding-right: 22rpx;
    padding-bottom: calc(18rpx + constant(safe-area-inset-bottom));
    padding-bottom: calc(18rpx + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, 0.98);
    border-top: 1rpx solid rgba(211, 220, 232, 0.86);
    box-shadow: 0 -10rpx 28rpx rgba(31, 41, 55, 0.05);
}

.input-bar {
    display: flex;
    align-items: center;
    gap: 16rpx;
    height: 64rpx;
    overflow: hidden;

    &__input {
        flex: 1;
        height: 64rpx;
        min-height: 64rpx;
        max-height: 64rpx;
        padding: 0 20rpx;
        box-sizing: border-box;
        border-radius: 14rpx;
        background: #eef3f8;
        font-size: 28rpx;
        line-height: 64rpx;
        color: #111827;
    }

    &__send {
        width: 64rpx;
        height: 64rpx;
        border-radius: 14rpx;
        background: #185dff;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        box-shadow: 0 10rpx 20rpx rgba(24, 93, 255, 0.22);

        &--disabled {
            background: #b8c5d8;
            box-shadow: none;
        }
    }
}
</style>
