<template>
    <view class="chat-room-page">
        <view class="room-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
            <view class="room-nav__btn" @tap="goBack">
                <u-icon name="arrow-left" size="34" color="#111827" />
            </view>
            <view class="room-nav__profile">
                <image class="room-nav__avatar" :src="targetUser?.avatar || defaultAvatar" mode="aspectFill" />
                <view class="room-nav__text">
                    <text class="room-nav__name">{{ targetUser?.nickname || '私聊' }}</text>
                </view>
            </view>
            <view class="room-nav__btn" />
        </view>

        <scroll-view scroll-y class="message-list" :scroll-into-view="scrollIntoView" scroll-with-animation>
            <view v-if="loading && messages.length === 0" class="message-state">
                <u-loading mode="circle" />
            </view>

            <view v-if="relationshipNoticeText" class="relationship-notice">
                <view class="relationship-notice__inner">
                    <text class="relationship-notice__text">{{ relationshipNoticeText }}</text>
                    <text
                        v-if="showAddFriendAction"
                        class="relationship-notice__action"
                        @tap.stop="handleAddFriend"
                    >
                        {{ addingFriend ? '添加中' : addFriendActionText }}
                    </text>
                </view>
            </view>

            <view v-for="item in messages" :id="'msg-' + item.id" :key="item.id" class="message-row"
                :class="{ 'message-row--self': item.is_self }">
                <image
                    v-if="!item.is_self"
                    class="message-row__avatar"
                    :src="targetUser?.avatar || defaultAvatar"
                    mode="aspectFill"
                />
                <view class="message-row__body">
                    <text v-if="!item.is_self" class="message-row__name">{{ targetUser?.nickname || '对方' }}</text>
                    <view class="message-bubble" :class="{ 'message-bubble--image': item.message_type === 'image' }">
                        <text v-if="item.message_type === 'text'" class="message-bubble__text">{{ item.content }}</text>
                        <image v-else class="message-bubble__image" :src="item.content" mode="widthFix"
                            @tap="previewImage(item.content)" />
                    </view>
                </view>
                <image
                    v-if="item.is_self"
                    class="message-row__avatar message-row__avatar--self"
                    :src="myAvatar"
                    mode="aspectFill"
                />
            </view>

            <view v-if="!loading && messages.length === 0" class="message-empty">
                <text>开始私聊吧</text>
            </view>
        </scroll-view>

        <view class="input-panel">
            <view v-if="voicePanelVisible" class="voice-panel">
                <view class="voice-panel__status">
                    <view class="voice-panel__dot" :class="{ 'voice-panel__dot--active': recording }" />
                    <text>{{ voiceStatusText }}</text>
                </view>
                <view class="voice-panel__button"
                    :class="{ 'voice-panel__button--recording': recording, 'voice-panel__button--disabled': transcribing }"
                    @touchstart="startRecord" @touchend="stopRecord" @touchcancel="cancelRecord">
                    <u-icon name="mic" size="42" :color="recording ? '#fff' : '#185dff'" />
                </view>
                <view class="voice-panel__meta">
                    <text>{{ recordTimeText }}</text>
                </view>
            </view>

            <view class="input-bar">
                <view class="input-bar__tool"
                    :class="{ 'input-bar__tool--disabled': toolDisabled, 'input-bar__tool--active': voicePanelVisible }"
                    @tap="toggleVoicePanel">
                    <u-icon name="mic" size="34" color="#185dff" />
                </view>
                <view class="input-bar__tool" :class="{ 'input-bar__tool--disabled': toolDisabled }" @tap="chooseImage">
                    <u-icon name="photo" size="34" color="#185dff" />
                </view>
                <textarea v-model="inputText" class="input-bar__textarea" maxlength="1000"
                    :disabled="sending || recording || transcribing" :placeholder="inputPlaceholder"
                    disable-default-padding confirm-type="send" @confirm="sendText" />
                <view class="input-bar__send" :class="{ 'input-bar__send--disabled': !canSend }" @tap="sendText">
                    <u-icon name="arrow-upward" size="30" color="#fff" />
                </view>
            </view>
        </view>
    </view>
</template>

<script setup lang="ts">
import { computed, nextTick, ref } from 'vue'
import { onHide, onLoad, onShow, onUnload } from '@dcloudio/uni-app'
import {
    getChatMessages,
    openChat,
    sendChatMessage,
    voiceToText,
    type PrivateChatMessage,
    type PrivateChatSession
} from '@/api/chat'
import { uploadImage } from '@/api/app'
import { getToken } from '@/utils/auth'
import { useUserStore } from '@/stores/user'
import { toggleFollow } from '@/api/community'

const defaultAvatar = '/static/images/avatar.png'
const notFollowedText = '对方未添加好友'
const waitingMutualText = '对方未添加互关'
const followedMeText = '对方已关注'
const userStore = useUserStore()
const statusBarHeight = ref(0)
const sessionId = ref(0)
const targetUserId = ref(0)
const session = ref<PrivateChatSession | null>(null)
const messages = ref<PrivateChatMessage[]>([])
const inputText = ref('')
const loading = ref(false)
const sending = ref(false)
const polling = ref(false)
const voicePanelVisible = ref(false)
const voiceSupported = ref(false)
const recording = ref(false)
const transcribing = ref(false)
const recordSeconds = ref(0)
const addingFriend = ref(false)
const scrollIntoView = ref('')
let pollTimer: ReturnType<typeof setInterval> | null = null
let recordTimer: ReturnType<typeof setInterval> | null = null
let recorderManager: any = null
let recordCancelled = false
let recordStartAt = 0

const targetUser = computed(() => session.value?.target_user || null)
const isMutual = computed(() => {
    const relation = session.value?.relationship
    return !!(relation?.is_mutual || (relation?.is_followed_by_me && relation?.is_following_me))
})
const myAvatar = computed(() => userStore.userInfo?.avatar || defaultAvatar)
const relationshipNoticeText = computed(() => {
    const relation = session.value?.relationship
    if (!relation || relation.is_mutual) return ''
    if (relation.is_followed_by_me) return waitingMutualText
    if (relation.is_following_me) return followedMeText
    return notFollowedText
})
const showAddFriendAction = computed(() => {
    return !!session.value && !isMutual.value && !session.value.relationship?.is_followed_by_me
})
const addFriendActionText = computed(() => {
    return session.value?.relationship?.is_following_me ? '添加互关' : '添加好友'
})
const toolDisabled = computed(() => sending.value || recording.value || transcribing.value || !sessionId.value)
const canSend = computed(() => inputText.value.trim().length > 0 && !toolDisabled.value)
const inputPlaceholder = computed(() => {
    if (recording.value) return '正在录音'
    if (transcribing.value) return '语音识别中'
    return '输入消息'
})
const voiceStatusText = computed(() => {
    if (transcribing.value) return '正在识别语音'
    if (recording.value) return '松开发起识别'
    return '按住说话'
})
const recordTimeText = computed(() => {
    const seconds = Math.min(recordSeconds.value, 60)
    return `${seconds}s / 60s`
})

onLoad(async (options) => {
    const sysInfo = uni.getSystemInfoSync()
    // #ifdef APP-PLUS || MP
    statusBarHeight.value = sysInfo.statusBarHeight || 44
    // #endif
    // #ifdef H5
    statusBarHeight.value = 0
    // #endif
    // #ifdef APP-PLUS
    voiceSupported.value = true
    initRecorder()
    // #endif
    if (getToken() && !userStore.userInfo?.id) {
        userStore.getUser().catch(() => { })
    }

    sessionId.value = Number(options?.session_id || 0)
    targetUserId.value = Number(options?.target_user_id || 0)
    if (!sessionId.value && targetUserId.value) {
        await openTargetSession()
    }
    if (sessionId.value) {
        await loadMessages(0, true)
        startPolling()
    } else {
        uni.showToast({ title: '会话参数错误', icon: 'none' })
        setTimeout(() => goBack(), 1200)
    }
})

onShow(() => {
    if (sessionId.value) startPolling()
})

onHide(() => {
    stopPolling()
    cancelRecord()
})

onUnload(() => {
    stopPolling()
    cancelRecord()
})

const openTargetSession = async () => {
    try {
        const res = await openChat({ target_user_id: targetUserId.value })
        session.value = res
        sessionId.value = res?.id || 0
    } catch (e) {
        sessionId.value = 0
    }
}

const loadMessages = async (afterId = 0, showLoading = false) => {
    if (!sessionId.value) return
    if (afterId > 0 && polling.value) return
    if (showLoading) loading.value = true
    if (afterId > 0) polling.value = true
    try {
        const res = await getChatMessages({
            session_id: sessionId.value,
            after_id: afterId,
            page_no: 1,
            page_size: afterId > 0 ? 100 : 50
        })
        session.value = res?.session || session.value
        const list = res?.lists || []
        if (afterId > 0) {
            const exists = new Set(messages.value.map((item) => item.id))
            const nextList = list.filter((item) => !exists.has(item.id))
            if (nextList.length) {
                messages.value.push(...nextList)
                scrollToBottom()
            }
        } else {
            messages.value = list
            scrollToBottom()
        }
    } catch (e) {
        if (showLoading) {
            uni.showToast({ title: '消息加载失败', icon: 'none' })
        }
    } finally {
        if (showLoading) loading.value = false
        if (afterId > 0) polling.value = false
    }
}

const startPolling = () => {
    if (pollTimer || !sessionId.value) return
    pollTimer = setInterval(() => {
        const afterId = messages.value.length ? Number(messages.value[messages.value.length - 1].id || 0) : 0
        if (afterId > 0) {
            loadMessages(afterId)
        } else {
            loadMessages(0)
        }
    }, 3000)
}

const stopPolling = () => {
    if (pollTimer) {
        clearInterval(pollTimer)
        pollTimer = null
    }
}

const handleAddFriend = async () => {
    if (addingFriend.value || !showAddFriendAction.value) return
    const followUserId = Number(targetUser.value?.id || session.value?.target_user_id || targetUserId.value || 0)
    if (!followUserId) {
        uni.showToast({ title: '用户信息异常', icon: 'none' })
        return
    }

    addingFriend.value = true
    try {
        const res: any = await toggleFollow({ follow_user_id: followUserId })
        const followed = !!res?.is_followed
        if (!followed) {
            uni.showToast({ title: '添加失败，请重试', icon: 'none' })
            return
        }

        if (session.value) {
            const oldRelation = session.value.relationship || {}
            const isFollowingMe = !!oldRelation.is_following_me
            const relation = {
                ...oldRelation,
                is_followed_by_me: true,
                is_following_me: isFollowingMe,
                is_mutual: isFollowingMe,
                relationship_text: isFollowingMe ? '互相关注' : waitingMutualText
            }
            session.value = {
                ...session.value,
                relationship: relation
            }
        }
        uni.showToast({ title: '已添加好友', icon: 'success' })
    } catch (e) {
        uni.showToast({ title: '添加失败，请重试', icon: 'none' })
    } finally {
        addingFriend.value = false
    }
}

const sendText = async () => {
    const content = inputText.value.trim()
    if (!content || !canSend.value) return
    if (content.length > 1000) {
        uni.showToast({ title: '文字消息最多1000字', icon: 'none' })
        return
    }
    sending.value = true
    try {
        const message = await sendChatMessage({
            session_id: sessionId.value,
            message_type: 'text',
            content
        })
        inputText.value = ''
        appendMessage(message)
    } catch (e) {
        uni.showToast({ title: '发送失败', icon: 'none' })
    } finally {
        sending.value = false
    }
}

const chooseImage = () => {
    if (toolDisabled.value) return
    uni.chooseImage({
        count: 9,
        sizeType: ['compressed'],
        sourceType: ['album', 'camera'],
        success: async (res) => {
            const files = res.tempFilePaths || []
            if (!files.length) return
            sending.value = true
            uni.showLoading({ title: '发送中...' })
            try {
                for (const file of files) {
                    const uploadRes: any = await uploadImage(file, getToken() || '')
                    const url = uploadRes?.uri || uploadRes?.url || ''
                    if (!url) continue
                    const message = await sendChatMessage({
                        session_id: sessionId.value,
                        message_type: 'image',
                        content: url
                    })
                    appendMessage(message)
                }
            } catch (e) {
                uni.showToast({ title: '图片发送失败', icon: 'none' })
            } finally {
                uni.hideLoading()
                sending.value = false
            }
        }
    })
}

const toggleVoicePanel = () => {
    if (recording.value || transcribing.value || sending.value || !sessionId.value) return
    if (!voiceSupported.value) {
        uni.showToast({ title: '请在App中使用语音输入', icon: 'none' })
        return
    }
    voicePanelVisible.value = !voicePanelVisible.value
}

const initRecorder = () => {
    if (recorderManager) return recorderManager
    // #ifdef APP-PLUS
    recorderManager = uni.getRecorderManager()
    recorderManager.onStop(handleRecordStop)
    recorderManager.onError(() => {
        stopRecordTimer()
        recording.value = false
        transcribing.value = false
        uni.hideLoading()
        uni.showToast({ title: '录音失败，请检查麦克风权限', icon: 'none' })
    })
    // #endif
    return recorderManager
}

const startRecord = () => {
    if (!voiceSupported.value || recording.value || transcribing.value || sending.value) return
    const manager = initRecorder()
    if (!manager) {
        uni.showToast({ title: '请在App中使用语音输入', icon: 'none' })
        return
    }
    recordCancelled = false
    recording.value = true
    recordSeconds.value = 0
    recordStartAt = Date.now()
    startRecordTimer()
    manager.start({
        duration: 60000,
        sampleRate: 16000,
        numberOfChannels: 1,
        encodeBitRate: 48000,
        format: 'mp3'
    })
}

const stopRecord = () => {
    if (!recording.value) return
    const duration = Date.now() - recordStartAt
    if (duration < 800) {
        recordCancelled = true
        uni.showToast({ title: '说话时间太短', icon: 'none' })
    }
    recording.value = false
    stopRecordTimer()
    recorderManager?.stop()
}

const cancelRecord = () => {
    if (!recording.value) return
    recordCancelled = true
    recording.value = false
    stopRecordTimer()
    recorderManager?.stop()
}

const handleRecordStop = async (res: any) => {
    stopRecordTimer()
    recording.value = false
    if (recordCancelled) {
        recordCancelled = false
        return
    }
    const filePath = res?.tempFilePath || ''
    if (!filePath) {
        uni.showToast({ title: '录音文件生成失败', icon: 'none' })
        return
    }

    transcribing.value = true
    uni.showLoading({ title: '识别中...' })
    try {
        const result = await voiceToText(filePath, getToken() || '')
        const text = (result?.text || '').trim()
        if (!text) {
            uni.showToast({ title: '未识别到语音内容', icon: 'none' })
            return
        }
        inputText.value = inputText.value.trim() ? `${inputText.value.trim()}\n${text}` : text
        voicePanelVisible.value = false
    } catch (e: any) {
        const title = typeof e === 'string' && e ? e : '语音识别失败'
        uni.showToast({ title, icon: 'none' })
    } finally {
        uni.hideLoading()
        transcribing.value = false
    }
}

const startRecordTimer = () => {
    stopRecordTimer()
    recordTimer = setInterval(() => {
        recordSeconds.value = Math.min(60, Math.floor((Date.now() - recordStartAt) / 1000))
    }, 200)
}

const stopRecordTimer = () => {
    if (recordTimer) {
        clearInterval(recordTimer)
        recordTimer = null
    }
}

const appendMessage = (message: PrivateChatMessage) => {
    if (!message?.id) return
    if (messages.value.some((item) => item.id === message.id)) return
    messages.value.push(message)
    scrollToBottom()
}

const previewImage = (url: string) => {
    if (!url) return
    const urls = messages.value
        .filter((item) => item.message_type === 'image' && item.content)
        .map((item) => item.content)
    uni.previewImage({ current: url, urls: urls.length ? urls : [url] })
}

const scrollToBottom = () => {
    nextTick(() => {
        const last = messages.value[messages.value.length - 1]
        if (last?.id) {
            scrollIntoView.value = 'msg-' + last.id
        }
    })
}

const goBack = () => {
    const pages = getCurrentPages()
    if (pages.length > 1) {
        uni.navigateBack()
        return
    }
    uni.navigateTo({ url: '/pages/private_chat/list' })
}
</script>

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

.room-nav {
    flex-shrink: 0;
    min-height: 96rpx;
    padding-left: 20rpx;
    padding-right: 20rpx;
    background: #fff;
    display: flex;
    align-items: center;
    border-bottom: 1rpx solid #e8edf3;

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

    &__profile {
        flex: 1;
        min-width: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 14rpx;
    }

    &__avatar {
        width: 58rpx;
        height: 58rpx;
        border-radius: 8rpx;
        background: #e5e7eb;
        flex-shrink: 0;
    }

    &__text {
        min-width: 0;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
    }

    &__name {
        max-width: 360rpx;
        font-size: 30rpx;
        font-weight: 700;
        color: #111827;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

}

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

.relationship-notice {
    display: flex;
    justify-content: center;
    margin: 4rpx 0 28rpx;

    &__inner {
        min-height: 42rpx;
        padding: 0 18rpx;
        border-radius: 8rpx;
        background: rgba(17, 24, 39, 0.08);
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 12rpx;
    }

    &__text {
        color: #4b5563;
        font-size: 22rpx;
        line-height: 42rpx;
    }

    &__action {
        color: #185dff;
        font-size: 22rpx;
        line-height: 42rpx;
        text-decoration: underline;
    }
}

.message-row {
    display: flex;
    align-items: flex-start;
    justify-content: flex-start;
    margin-bottom: 22rpx;

    &__avatar {
        width: 64rpx;
        height: 64rpx;
        margin-right: 14rpx;
        border-radius: 8rpx;
        background: #dbe3ee;
        flex-shrink: 0;

        &--self {
            margin-right: 0;
            margin-left: 14rpx;
        }
    }

    &__body {
        max-width: calc(100% - 78rpx);
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        min-width: 0;
    }

    &__name {
        max-width: 360rpx;
        margin-bottom: 8rpx;
        color: #6b7280;
        font-size: 22rpx;
        line-height: 1.2;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

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

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

        .message-bubble {
            background: #185dff;
            border-color: #185dff;
            color: #fff;
        }

        .message-bubble--image {
            background: transparent;
            border-color: transparent;
        }
    }
}

.message-bubble {
    max-width: 100%;
    padding: 18rpx 22rpx;
    border-radius: 8rpx;
    background: #fff;
    border: 1rpx solid #dbe6f5;
    box-sizing: border-box;

    &--image {
        padding: 0;
        background: transparent;
        border-color: transparent;
        overflow: hidden;
    }

    &__text {
        font-size: 28rpx;
        line-height: 1.6;
        white-space: pre-wrap;
        word-break: break-word;
    }

    &__image {
        width: 300rpx;
        max-height: 520rpx;
        border-radius: 8rpx;
        display: block;
        background: #dbe3ee;
    }
}

.message-state,
.message-empty {
    display: flex;
    justify-content: center;
    padding: 120rpx 0;

    text {
        font-size: 24rpx;
        color: #9ca3af;
    }
}

.input-panel {
    flex-shrink: 0;
    background: #fff;
    border-top: 1rpx solid #e8edf3;
    padding: 16rpx 22rpx calc(18rpx + env(safe-area-inset-bottom));
}

.voice-panel {
    display: grid;
    grid-template-columns: 1fr 104rpx 1fr;
    align-items: center;
    min-height: 116rpx;
    padding-bottom: 16rpx;

    &__status,
    &__meta {
        display: flex;
        align-items: center;
        min-width: 0;
        font-size: 24rpx;
        color: #4b5563;
    }

    &__meta {
        justify-content: flex-end;
        color: #8a95a6;
    }

    &__dot {
        width: 12rpx;
        height: 12rpx;
        margin-right: 10rpx;
        border-radius: 50%;
        background: #b8c5d8;

        &--active {
            background: #ef4444;
        }
    }

    &__button {
        width: 104rpx;
        height: 104rpx;
        border-radius: 50%;
        background: #edf3ff;
        border: 1rpx solid #c9d8f4;
        display: flex;
        align-items: center;
        justify-content: center;

        &--recording {
            background: #185dff;
            border-color: #185dff;
        }

        &--disabled {
            opacity: 0.6;
        }
    }
}

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

    &__tool,
    &__send {
        width: 72rpx;
        height: 72rpx;
        border-radius: 8rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    &__tool {
        background: #edf3ff;
        border: 1rpx solid #c9d8f4;

        &--active {
            background: #dce9ff;
            border-color: #9fbaf3;
        }

        &--disabled {
            opacity: 0.5;
        }
    }

    &__textarea {
        flex: 1;
        height: 72rpx !important;
        min-height: 72rpx;
        max-height: 72rpx;
        padding: 16rpx 22rpx;
        box-sizing: border-box;
        border-radius: 8rpx;
        background: #f3f6fa;
        font-size: 28rpx;
        line-height: 40rpx;
        overflow-y: auto;
    }

    &__send {
        background: #185dff;

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