<template>
    <template v-if="contextMenu && !languagePopupVisible">
        <div class="message-context-menu" :class="{
            'message-context-menu--mobile': isMobilePlatform,
        }" :style="menuStyle" @click.stop>
            <button v-for="item in visibleActionItems" :key="item.key" class="message-context-menu-item" :class="{
                'message-context-menu-item--mobile': isMobilePlatform,
            }" type="button" :disabled="item.disabled" @click="handleActionClick(item)">
                <img class="message-context-menu-item__icon" :src="item.icon" :alt="item.label">
                <span class="message-context-menu-item__label">{{ item.label }}</span>
            </button>
        </div>
    </template>

    <t-popup v-model="languagePopupVisible" placement="bottom">
        <div class="translation-language-popup-panel">
            <div class="translation-language-popup-header">
                <div class="translation-language-popup-title">{{ t('切换语言') }}</div>
                <button class="translation-language-popup-close" type="button" :aria-label="t('关闭切换语言弹窗')"
                    @click="closeLanguagePopup">
                    <svg class="translation-language-popup-close-icon" viewBox="0 0 20 20" fill="none"
                        xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
                        <path d="M6 6L14 14M14 6L6 14" stroke="#A9ADB6" stroke-width="1.8" stroke-linecap="round"
                            stroke-linejoin="round" />
                    </svg>
                </button>
            </div>

            <div class="translation-language-search-box">
                <svg class="translation-language-search-icon" viewBox="0 0 20 20" fill="none"
                    xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
                    <path
                        d="M8.75 3.75a5 5 0 1 0 0 10 5 5 0 0 0 0-10Zm0-1.5a6.5 6.5 0 1 1 0 13 6.5 6.5 0 0 1 0-13Zm5.209 11.148 3.02 3.02a.75.75 0 1 1-1.06 1.061l-3.02-3.02a.75.75 0 0 1 1.06-1.06Z"
                        fill="#86909C" />
                </svg>
                <input v-model.trim="languageSearchKeyword" class="translation-language-search-input" type="text"
                    :placeholder="t('搜索语言名称或代码')" autocomplete="off">
            </div>

            <div class="translation-language-option-list">
                <button v-for="item in filteredTranslationLanguageOptions" :key="item.key"
                    class="translation-language-option" type="button"
                    @click="handleSelectTranslationLanguage(item.key)">
                    <span class="translation-language-option__key">{{ item.key }}</span>
                    <span class="translation-language-option__label">{{ item.label }}</span>
                </button>

                <div v-if="!filteredTranslationLanguageOptions.length" class="translation-language-empty">
                    {{ t('暂无匹配语言') }}
                </div>
            </div>
        </div>
    </t-popup>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch, type CSSProperties } from 'vue'
import { useI18n } from 'vue-i18n'

import AIDeskCoreInstance, {
    MESSAGE_TYPES,
    StoreName,
    type IMessageModel,
} from '@tencentcloud/ai-desk-core'

import changeLanguageIcon from '../../assets/changeLanguage.svg'
import copyIcon from '../../assets/copy.svg'
import revokeIcon from '../../assets/delete.svg'
import quoteIcon from '../../assets/quote.svg'
import translateIcon from '../../assets/translate.svg'
import { parseUnknownError } from '../../../../utils/parseUnknownError'
import toast from '../../../../utils/toast'
import { formateMessageAbstract, getReplyMessageOriginType } from '../../../../utils'

interface MessageToolsContextMenu {
    mouseX: number
    mouseY: number
}

interface TranslateTextInfoItem {
    translateResult: string
    translateStatus: 'loading' | 'success' | 'error'
}

interface TranslationLanguageOption {
    key: string
    label: string
}

interface MenuAction {
    key: string
    label: string
    icon: string
    visible: boolean
    disabled?: boolean
    click: () => Promise<void> | void
}

interface Props {
    contextMenu: MessageToolsContextMenu
    message: IMessageModel
    isGroupSession: boolean
    translateInfo?: TranslateTextInfoItem
    currentUserId?: string
}

const props = defineProps<Props>()

const emit = defineEmits<{
    (e: 'close'): void
    (e: 'translate', message: IMessageModel): void
}>()

const { t } = useI18n()

const MENU_VIEWPORT_GAP = 12
const DESKTOP_MENU_ITEM_WIDTH = 68
const MOBILE_MENU_ITEM_WIDTH = 60
const DESKTOP_MENU_GAP = 8
const MOBILE_MENU_GAP = 6
const DESKTOP_MENU_HORIZONTAL_PADDING = 16
const MOBILE_MENU_HORIZONTAL_PADDING = 12
const translationLanguageOptions: TranslationLanguageOption[] = [
    { key: 'zh', label: t('中文') },
    { key: 'zh-TW', label: t('繁体中文') },
    { key: 'en', label: t('英文') },
    { key: 'ja', label: t('日语') },
    { key: 'ko', label: t('韩语') },
    { key: 'fr', label: t('法语') },
    { key: 'es', label: t('西班牙语') },
    { key: 'it', label: t('意大利语') },
    { key: 'de', label: t('德语') },
    { key: 'tr', label: t('土耳其语') },
    { key: 'ru', label: t('俄语') },
    { key: 'pt', label: t('葡萄牙语') },
    { key: 'vi', label: t('越南语') },
    { key: 'id', label: t('印尼语') },
    { key: 'th', label: t('泰语') },
    { key: 'ms', label: t('马来语') },
    { key: 'ar', label: t('阿拉伯语') },
]

const isMobilePlatform = true
const languagePopupVisible = ref(false)
const languageSearchKeyword = ref('')
const shouldCloseMenuAfterLanguagePopup = ref(false)
const messageListTranslateTargetLanguage = ref('zh')
const userMsgTranslationEnable = ref(0)
const seatMsgTranslationEnable = ref(0)

onMounted(() => {
    AIDeskCoreInstance.coreStore.watch(StoreName.USERINFO, {
        messsageListTranslateTargetLanguage: onMessageListTranslateTargetLanguageUpdated,
    });
    AIDeskCoreInstance.coreStore.watch(StoreName.APP_SETTING, {
        userMsgTranslationEnable: onUserMsgTranslationEnableUpdated,
        seatMsgTranslationEnable: onSeatMsgTranslationEnableUpdated,
    });
});

onUnmounted(() => {
    AIDeskCoreInstance.coreStore.unwatch(StoreName.USERINFO, {
        messsageListTranslateTargetLanguage: onMessageListTranslateTargetLanguageUpdated,
    });
    AIDeskCoreInstance.coreStore.unwatch(StoreName.APP_SETTING, {
        userMsgTranslationEnable: onUserMsgTranslationEnableUpdated,
        seatMsgTranslationEnable: onSeatMsgTranslationEnableUpdated,
    });
});

const onMessageListTranslateTargetLanguageUpdated = (newLanguage: string) => {
    messageListTranslateTargetLanguage.value = newLanguage || 'zh'
}

const onUserMsgTranslationEnableUpdated = (value: number) => {
    userMsgTranslationEnable.value = value ?? 0
}

const onSeatMsgTranslationEnableUpdated = (value: number) => {
    seatMsgTranslationEnable.value = value ?? 0
}

const getEstimatedMenuWidth = () => {
    const itemCount = Math.max(visibleActionItems.value.length, 1)
    const itemWidth = isMobilePlatform ? MOBILE_MENU_ITEM_WIDTH : DESKTOP_MENU_ITEM_WIDTH
    const itemGap = isMobilePlatform ? MOBILE_MENU_GAP : DESKTOP_MENU_GAP
    const horizontalPadding = isMobilePlatform
        ? MOBILE_MENU_HORIZONTAL_PADDING
        : DESKTOP_MENU_HORIZONTAL_PADDING

    return horizontalPadding + itemCount * itemWidth + Math.max(0, itemCount - 1) * itemGap
}

const menuStyle = computed<CSSProperties>(() => {
    const viewportWidth = window.innerWidth
    const viewportHeight = window.innerHeight
    const estimatedMenuWidth = Math.min(
        getEstimatedMenuWidth(),
        viewportWidth - MENU_VIEWPORT_GAP * 2,
    )
    const { mouseX, mouseY } = props.contextMenu

    return {
        ...(mouseX + estimatedMenuWidth > viewportWidth - MENU_VIEWPORT_GAP
            ? { right: `${Math.max(MENU_VIEWPORT_GAP, viewportWidth - mouseX)}px` }
            : { left: `${Math.max(MENU_VIEWPORT_GAP, mouseX)}px` }),
        ...(mouseY > viewportHeight / 2
            ? { bottom: `${Math.max(MENU_VIEWPORT_GAP, viewportHeight - mouseY)}px` }
            : { top: `${Math.max(MENU_VIEWPORT_GAP, mouseY)}px` }),
    }
})

const filteredTranslationLanguageOptions = computed(() => {
    const keyword = languageSearchKeyword.value.trim().toLowerCase()

    if (!keyword) {
        return translationLanguageOptions
    }

    return translationLanguageOptions.filter((item) => {
        return item.key.toLowerCase().includes(keyword) || item.label.toLowerCase().includes(keyword)
    })
})

watch(languagePopupVisible, (visible, previousVisible) => {
    if (visible) {
        return
    }

    languageSearchKeyword.value = ''

    if (previousVisible && shouldCloseMenuAfterLanguagePopup.value) {
        shouldCloseMenuAfterLanguagePopup.value = false
        closeMenu()
    }
})

const canCopyMessage = computed(() => {
    const message = props.message
    if (message.type === MESSAGE_TYPES.MSG_TEXT) {
        return true
    }
    return false
})

const canRevokeMessage = computed(() => {
    const message = props.message
    if (props.isGroupSession) {
        return false
    }

    return message.flow === 'out' && message.status === 'success'
})

const canQuoteMessage = computed(() => {
    return !props.isGroupSession
})

const canTranslateMessage = computed(() => {
    return props.message.type === MESSAGE_TYPES.MSG_TEXT
})

const canChangeLanguageMessage = computed(() => {
    return props.message.type === MESSAGE_TYPES.MSG_TEXT
        && props.translateInfo?.translateStatus === 'success'
        && Boolean(props.translateInfo.translateResult)
})

function closeMenu() {
    emit('close')
}

const closeLanguagePopup = () => {
    languagePopupVisible.value = false
}

const openLanguagePopup = () => {
    shouldCloseMenuAfterLanguagePopup.value = true
    languageSearchKeyword.value = ''
    languagePopupVisible.value = true
}

const fallbackCopy = (text: string) => {
    const textarea = document.createElement('textarea')
    textarea.value = text
    textarea.style.position = 'fixed'
    textarea.style.opacity = '0'
    document.body.appendChild(textarea)
    textarea.select()
    document.execCommand('copy')
    document.body.removeChild(textarea)
}

const copyText = async (text: string) => {
    try {
        if (navigator.clipboard?.writeText) {
            await navigator.clipboard.writeText(text)
        } else {
            fallbackCopy(text)
        }

        toast.success(t('已复制消息文本'))
        closeMenu()
    } catch (error) {
        toast.error(t('复制失败：{reason}', { reason: parseUnknownError(error) }))
    }
}

const copyMessageContent = async () => {
    const message = props.message
    await copyText(message.payload?.text ?? '')
}

const revokeMessage = async () => {
    const message = props.message

    try {
        await AIDeskCoreInstance.chatService.revokeMessage(message)
        closeMenu()
    } catch (error) {
        toast.error(t('撤回失败：{reason}', { reason: parseUnknownError(error) }))
    }
}

const quoteMessage = () => {
    const message = props.message
    AIDeskCoreInstance.chatService.quoteMessage({
        messageID: message?.ID,
        messageAbstract: formateMessageAbstract(message, props.currentUserId),
        messageSender: message?.from,
        messageSenderNick: message?.nick,
        messageType: getReplyMessageOriginType(message),
        messageTime: message?.time,
        messageSequence: message?.sequence,
        version: 1,
    })
    closeMenu()
}

const translateMessage = async () => {
    closeMenu()
    await AIDeskCoreInstance.chatService.translateTextMessage({ message: props.message, targetLanguage: messageListTranslateTargetLanguage.value || 'zh' })
}

const handleSelectTranslationLanguage = async (targetLanguage: string) => {
    closeLanguagePopup()

    try {
        AIDeskCoreInstance.userInfoService.updateMesssageListTranslateTargetLanguage(targetLanguage)
        await AIDeskCoreInstance.chatService.translateTextMessage({
            message: props.message,
            targetLanguage,
        })
    } catch (error) {
        toast.error(t('切换语言失败：{reason}', { reason: parseUnknownError(error) }))
    }
}

const changeLanguage = () => {
    openLanguagePopup()
}

const actionItems = computed<MenuAction[]>(() => {
    const message = props.message
    const isUserMsg = message?.flow === 'in' && message?.from !== 'administrator'

    return [
        {
            key: 'copy',
            label: t('复制'),
            icon: copyIcon,
            visible: canCopyMessage.value,
            click: copyMessageContent,
        },
        {
            key: 'translate',
            label: t('翻译'),
            icon: translateIcon,
            visible: canTranslateMessage.value,
            disabled: isUserMsg
                ? userMsgTranslationEnable.value === 0
                : seatMsgTranslationEnable.value === 0,
            click: translateMessage,
        },
        {
            key: 'change-language',
            label: t('更换语言'),
            icon: changeLanguageIcon,
            visible: canChangeLanguageMessage.value,
            click: changeLanguage,
        },
        {
            key: 'revoke',
            label: t('撤回'),
            icon: revokeIcon,
            visible: canRevokeMessage.value,
            click: revokeMessage,
        },
        {
            key: 'quote',
            label: t('引用'),
            icon: quoteIcon,
            visible: canQuoteMessage.value,
            click: quoteMessage,
        },
    ]
})

const visibleActionItems = computed(() => {
    return actionItems.value.filter((item) => item.visible)
})

const handleActionClick = async (item: MenuAction) => {
    if (item.disabled) {
        return
    }
    await item.click()
}
</script>

<style scoped>
.message-context-menu {
    position: fixed;
    z-index: 1200;
    width: max-content;
    min-width: 0;
    max-width: calc(100vw - 24px);
    max-height: calc(100vh - 24px);
    box-sizing: border-box;
    padding: 8px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    gap: 8px;
    background: rgba(17, 17, 17, 0.96);
    border: none;
    border-radius: 18px;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.28);
    overflow-x: auto;
    overflow-y: hidden;
}

.message-context-menu--mobile {
    padding: 6px;
    gap: 6px;
}

.message-context-menu-item {
    flex: 0 0 68px;
    min-width: 68px;
    border: none;
    border-radius: 14px;
    background: transparent;
    color: #fff;
    text-align: center;
    padding: 10px 8px 8px;
    cursor: pointer;
    font-size: 12px;
    line-height: 18px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    transition: background-color 0.2s ease, opacity 0.2s ease;
}

.message-context-menu-item+.message-context-menu-item {
    border-top: none;
}

.message-context-menu-item:hover {
    background: rgba(255, 255, 255, 0.12);
}

.message-context-menu-item:disabled {
    cursor: not-allowed;
    opacity: 0.4;
}

.message-context-menu-item:disabled:hover {
    background: transparent;
}

.message-context-menu-item__icon {
    width: 24px;
    height: 24px;
    display: block;
    flex-shrink: 0;
}

.message-context-menu-item__label {
    color: inherit;
    white-space: nowrap;
}

.message-context-menu-item--mobile {
    flex-basis: 60px;
    min-width: 60px;
    padding: 10px 6px 8px;
    font-size: 12px;
    line-height: 18px;
    gap: 5px;
}

.translation-language-popup-panel {
    background: #ffffff;
    border-radius: 20px 20px 0 0;
    padding: 20px 16px calc(24px + env(safe-area-inset-bottom));
    max-height: min(78vh, 640px);
    display: flex;
    flex-direction: column;
}

.translation-language-popup-header {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 28px;
    flex-shrink: 0;
}

.translation-language-popup-title {
    font-size: 17px;
    line-height: 24px;
    font-weight: 600;
    color: #1d2129;
}

.translation-language-popup-close {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    width: 28px;
    height: 28px;
    border: none;
    background: transparent;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.translation-language-popup-close-icon {
    width: 18px;
    height: 18px;
    display: block;
}

.translation-language-search-box {
    margin-top: 20px;
    width: 100%;
    min-height: 40px;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 14px;
    border-radius: 20px;
    background: #f2f3f5;
    flex-shrink: 0;
}

.translation-language-search-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.translation-language-search-input {
    flex: 1;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font-size: 14px;
    line-height: 22px;
    color: #1d2129;
}

.translation-language-search-input::placeholder {
    color: #86909c;
}

.translation-language-option-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    margin-top: 20px;
    border-top: 1px solid #F0F2F7;
    border-bottom: 1px solid #F0F2F7;
}

.translation-language-option {
    width: 100%;
    min-height: 56px;
    padding: 14px 0;
    border: none;
    border-bottom: 1px solid #F0F2F7;
    background: #ffffff;
    display: flex;
    align-items: center;
    gap: 12px;
    text-align: left;
}

.translation-language-option:last-child {
    border-bottom: none;
}

.translation-language-option:disabled {
    opacity: 0.6;
}

.translation-language-option__key {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 48px;
    height: 28px;
    padding: 0 10px;
    border-radius: 999px;
    background: #E6E9F0;
    color: #676A70;
    font-size: 13px;
    line-height: 18px;
    white-space: nowrap;
    flex-shrink: 0;
}

.translation-language-option__label {
    flex: 1;
    min-width: 0;
    color: #000000;
    font-size: 15px;
    line-height: 22px;
    word-break: break-word;
}

.translation-language-empty {
    padding: 24px 0;
    text-align: center;
    color: #86909C;
    font-size: 14px;
    line-height: 22px;
}

.message-translate-preview {
    padding: 10px 12px;
    margin-bottom: 0;
    border-radius: 0;
    border-bottom: 1px solid #eef1f5;
    font-size: 13px;
    line-height: 18px;
    white-space: pre-wrap;
    word-break: break-word;
    background: #f8fafc;
    color: #1f2329;
}

.message-translate-preview.is-loading,
.message-translate-preview.is-error {
    color: #8b95a7;
}
</style>
