<template>
    <div
        class="message-con d-flex align-items-center"
        :class="{
            'my-message flex-row-reverse': isMyMessage,
            'justify-content-center': isWithdrawMessage,
            'offset-bottom': matchKeywords,
        }"
    >
        <div class="msg-content" :class="{ 'algin-left': !isMyMessage }">
            <div
                class="msg-name no-selection"
                :class="{ 'algin-left': !isMyMessage }"
                v-if="!isWithdrawMessage"
            >
                {{ userName }}
            </div>
            <div
                class="content-avatar d-flex align-items-start"
                :class="{ 'justify-content-end': isMyMessage }"
            >
                <img
                    src="../imgs/default-host-avatar.svg"
                    style="width: 42px"
                    v-if="
                        !avatar &&
                        messageComponent &&
                        showHostAvatar &&
                        !isWithdrawMessage
                    "
                    class="host-avatar"
                />
                <component
                    :is="messageComponent"
                    :user-name="userName"
                    :class="{
                        'my-message': isMyMessage,
                    }"
                    v-if="messageComponent"
                    v-model="data"
                    @open="openFile"
                />
                <avatar v-if="avatar" :src="avatar" shape="circle" />
            </div>
        </div>

        <i
            class="el-icon-warning text-danger"
            v-if="failed"
            title="发送失败"
        ></i>
        <i class="el-icon-loading" v-else-if="isSendingMessage"></i>

        <template v-if="backend && showReadSummary && !isWithdrawMessage">
            <div v-if="isMyMessage" class="msg-read pos-rel">
                <span
                    @click="openReaderList"
                    class="pointer"
                    :class="{ all: isAllRead }"
                >
                    <template v-if="isAllRead">全部已读</template>
                    <template v-else-if="data.read_count > 0"
                        >{{ data.read_count }}人已读</template
                    >
                    <template v-else>未读</template>
                </span>
                <who-read-list
                    v-if="readListVisibility"
                    @blur="readListVisibility = false"
                    :msgId="data.id"
                    :class="{ offset: readerListOffset }"
                />
            </div>
        </template>

        <span
            class="withdraw"
            v-if="isMyMessage && canWithdraw && !isWithdrawMessage"
            @click="withdraw"
            >撤回此消息</span
        >

        <el-popover
            :visible-arrow="false"
            v-if="backend && matchKeywords"
            placement="right"
            popper-class="match-keyword-popover"
            trigger="click"
            :disabled="!!handled"
        >
            <ul class="keyword-action">
                <li @click.stop="executeHandled">设为已处理</li>
                <li @click.stop="ignoredKeyword">忽略</li>
                <!-- <li>创建工作流</li> -->
                <!-- <li>更多</li> -->
            </ul>
            <span
                slot="reference"
                class="match-keyword d-flex align-items-center text-nowrap"
                :class="{ handled: handled === 1, ignored: handled === 2 }"
            >
                <i :class="handled ? 'el-icon-success' : 'el-icon-info'"></i>
                <span>{{
                    handled
                        ? handled === 1
                            ? "已处理"
                            : "已忽略"
                        : "触发敏感词,请处理"
                }}</span>
            </span>
        </el-popover>
    </div>
</template>

<script lang="ts">
    import { Component, Inject, Prop, Vue } from "vue-property-decorator";
    import * as dto from "../model";
    import chat from "./../xim";
    import {
        isAudio,
        isImage,
        isVideo,
        MAX_FILE_SIZE,
        MAX_IMAGE_SIZE,
    } from "./message-item/file-controller";
    import WhoReadList from "./who-read-list.vue";
    import avatar from "@/customer-service/components/avatar.vue";
    import { chatStore, ChatStore } from "@/customer-service/store/model";
    import ximInstance from "../xim/xim";
    import { dbController } from "../database";
    import ImageMessage from "./message-item/image-message.vue";
    import FileMessage from "./message-item/file-message.vue";
    import AudioMessage from "./message-item/audio-message.vue";
    import VideoMessage from "./message-item/video-message.vue";
    import TextMessage from "./message-item/text-message.vue";
    import ActionMessage from "./message-item/action-message.vue";
    import WithdrawMessage from "./message-item/withdraw-message.vue";
    import xim from "./../xim";

    const twoMinutes = 2 * 60 * 1000;

    const messageMapping = new Map<dto.MessageType, string>([
        [dto.MessageType.Image, "image-message"],
        [dto.MessageType.File, "file-message"],
        [dto.MessageType.Video, "video-message"],
        [dto.MessageType.Voice, "audio-message"],
        [dto.MessageType.Text, "text-message"],
        [dto.MessageType.Withdraw, "withdraw-message"],
        [dto.MessageType.GeneralOrderMsg, "text-message"],
        [dto.MessageType.Action, "action-message"],
    ]);

    @Component({
        components: {
            WhoReadList,
            avatar,
            ImageMessage,
            FileMessage,
            AudioMessage,
            VideoMessage,
            TextMessage,
            WithdrawMessage,
            ActionMessage,
        },
    })
    export default class Message extends Vue {
        @chatStore.State(ChatStore.STATE_CHAT_CURRENT_USER_UID)
        private readonly chatMyId!: ChatStore.STATE_CHAT_CURRENT_USER_UID;

        @chatStore.Getter(ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS)
        private readonly chatMembers!: ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS;

        @chatStore.State(ChatStore.STATE_CHAT_SOURCE)
        private readonly chatSource!: ChatStore.STATE_CHAT_SOURCE;

        @chatStore.State(ChatStore.STATE_CHAT_CURRENT_CHAT_ID)
        private readonly chatId!: ChatStore.STATE_CHAT_CURRENT_CHAT_ID;

        @chatStore.Mutation(ChatStore.MUTATION_WITHDRAW)
        private readonly executeWithDraw!: ChatStore.MUTATION_WITHDRAW;

        @chatStore.Action(ChatStore.ACTION_SET_HANDLED)
        private readonly setHandled!: ChatStore.ACTION_SET_HANDLED;

        @Prop({ type: Object, default: () => Object.create(null) })
        private readonly data!: dto.Message;

        @Prop()
        private readonly isSendingMessage!: boolean;

        @Prop()
        private readonly failed!: boolean;

        @Prop({ default: "circle" })
        private readonly shape!: string;

        @Inject({ default: false }) readonly showReadSummary!: boolean;

        private readonly backend = chat.isBackend();

        private messageComponent = "";

        private readListVisibility = false;

        private org = "";

        private readerListOffset = false;
        private defaultMessageHandledStatus = dto.MessageHandled.Default;
        private showHostAvatar: boolean = false;

        private get canWithdraw() {
            if (this.backend && this.data) {
                return new Date().valueOf() - this.data.ts * 1000 < twoMinutes;
            }
            return false;
        }

        private get isWithdrawMessage() {
            return this.data.type === dto.MessageType.Withdraw;
        }

        private get isAllRead() {
            return this.data.read_count >= this.data.total_read_count;
        }

        private get messageBody(): { eid?: string; oid?: string; msg: any } {
            if (this.data) {
                const msg = this.data.msg;
                try {
                    if (msg.startsWith("{")) {
                        return { ...this.data, msg: JSON.parse(this.data.msg) };
                    }
                    return { ...this.data, msg: this.data.msg };
                } catch {
                    return {
                        ...this.data,
                        msg: JSON.parse(this.data.msg.replace(/\n/g, "\\n")),
                    };
                }
            }

            return { msg: { text: "" } };
        }

        private get handled() {
            if (this.data) {
                return this.defaultMessageHandledStatus || this.data.handled;
            }
            return dto.MessageHandled.Default;
        }

        private get isMyMessage() {
            if (this.isSendingMessage) {
                return true;
            }

            const senderEid = this.messageBody.eid;
            return senderEid!.toString() === this.chatMyId!.toString();
        }

        created() {
            this.messageComponent = messageMapping.get(this.messageType) as string;
        }

        private get userName() {
            if (this.chatMembers) {
                const t = this.chatMembers.find((i) => i.eid === this.data.eid);
                if (t) {
                    return t.name;
                }
            }
            return "";
        }

        private get avatar() {
            const avatar = chat.getUserMapping();

            if (this.isSendingMessage) {
                if (Object.getOwnPropertyNames(avatar).length > 0) {
                    this.showHostAvatar = true;
                } else {
                    this.showHostAvatar = false;
                }
                if (avatar && this.chatMyId) {
                    const user = avatar[this.chatMyId];
                    if (user && user.avatar) {
                        return user.avatar;
                    }
                }
            }

            if (avatar && this.data) {
                if (Object.getOwnPropertyNames(avatar).length > 0) {
                    this.showHostAvatar = true;
                } else {
                    this.showHostAvatar = false;
                }
                const value = avatar[this.data.eid];
                if (value && value.avatar) {
                    return value.avatar;
                }
            }

            return "";
        }

        private get messageType() {
            const type = this.data?.type;
            if (type === "file") {
                const name = this.messageBody?.msg.name;
                if (name) {
                    const size = this.messageBody?.msg.size;
                    if (size) {
                        const outImageSize = size > MAX_IMAGE_SIZE;
                        if (!outImageSize && isImage(name)) {
                            return dto.MessageType.Image;
                        }
                        const outSize = size > MAX_FILE_SIZE;
                        if (!outSize) {
                            if (isAudio(name)) {
                                return dto.MessageType.Voice;
                            }
                            if (isVideo(name)) {
                                return dto.MessageType.Video;
                            }
                        }
                    }
                }
            }
            return type;
        }

        private get isTextMessage() {
            return this.messageType === dto.MessageType.Text;
        }

        private get matchKeywords() {
            if (this.isTextMessage && !this.isMyMessage) {
                const m = this.messageBody.msg as { text: string };
                if (m && m.text) {
                    const keywords = xim.getMatchedTextKeywords();
                    return keywords.find((i) => m.text.includes(i));
                }
            }
            return false;
        }

        private isCustomer() {
            return !this.showReadSummary;
        }

        private openFile(url: string) {
            if (this.isSendingMessage) {
                return;
            }
            if (this.failed) {
                return;
            }
            const copy = { ...this.messageBody.msg };
            copy.url = url;
            this.$emit("open", { type: this.messageType, msg: copy });
        }

        private withdraw() {
            ximInstance.withdraw(this.chatId!, this.data.id).finally(() => {
                dbController
                    .removeMessage(this.chatId!, this.data.id)
                    .finally(() => {
                        this.executeWithDraw(this.data.id);
                        this.$emit("withdraw", this.data.id);
                    });
            });
        }

        private openReaderList(e: MouseEvent) {
            this.readerListOffset = e.x < 450;
            this.readListVisibility = true;
        }

        private executeHandled() {
            this.setHandled({
                id: this.data.id,
                value: (this.defaultMessageHandledStatus =
                    dto.MessageHandled.Handled),
            });
            this.closeKeywordPopover();
        }

        private ignoredKeyword() {
            this.setHandled({
                id: this.data.id,
                value: (this.defaultMessageHandledStatus =
                    dto.MessageHandled.Ignored),
            });
            this.closeKeywordPopover();
        }

        private closeKeywordPopover() {
            document.body.click();
        }
    }
</script>

<style lang="less" scoped>
    @import "./css/benefits-plan.less";
    .message-con {
        padding-bottom: 20px;
        margin-right: 15px;
        position: relative;

        &.my-message {
            .msg-detail {
                background-color: #dbf2ff;
                border-radius: 8px 0 8px 8px;
            }

            .msg-read {
                display: inline-block;
                color: #bfe1ff;
                margin-right: 15px;
                user-select: none;
                flex: none;
                margin-top: auto;
            }

            .download-icon {
                margin-right: 15px;
                margin-left: 0;
                margin-top: 0;
            }

            .withdraw {
                color: #999;
                position: absolute;
                bottom: 0;
                right: 0;
                font-size: 12px;
                display: none;
                cursor: pointer;
            }

            &:hover {
                .withdraw {
                    display: inline-block;
                }
            }

            /deep/ .file-message-info {
                text-align: initial;
                word-break: break-all;
            }
        }

        &.offset-bottom {
            margin-bottom: 30px;
        }

        > i {
            height: 16px;
            font-size: 16px;
            margin-right: 10px;
        }
    }

    .msg-name {
        font-size: 14px;
        color: #888;
        text-align: right;
        margin-bottom: 3px;
        min-height: 16px;
        &.algin-left {
            text-align: left;
        }
    }

    .msg-content {
        text-align: right;
        &.algin-left {
            text-align: left;
        }
    }

    .msg-detail {
        margin-top: 10px;
        font-size: 14px;
        line-height: 20px;
        background: #f5f6fa;
        border-radius: 0px 8px 8px;
        padding: 10px;
        word-break: break-word;
        /deep/ img {
            max-width: 300px;
        }
    }

    .download-icon {
        cursor: pointer;
        text-decoration: none;
        margin-left: 15px;
        margin-top: 42px;

        i {
            color: #fff;
            font-size: 14px;
        }
    }

    .no-selection {
        user-select: none;
    }

    .pointer {
        cursor: pointer;
    }

    .all {
        color: #4389f8;
    }

    .match-keyword {
        background-color: #fff3e0;
        border-radius: 13px;
        padding: 3px 8px;
        color: #e87005;
        position: absolute;
        bottom: -10px;
        left: 0;
        cursor: pointer;
        font-size: 12px;

        &.handled {
            color: #59ba7b;
            background-color: #f0f0f0;
            cursor: default;
        }

        &.ignored {
            color: #999;
            background-color: #f0f0f0;
            cursor: default;
        }

        i {
            margin-right: 5px;
        }
    }

    .keyword-action {
        list-style: none;

        li {
            list-style: none;
            padding: 8px 10px;
            color: #077aec;
            cursor: pointer;

            &:hover {
                background-color: #f5f6fa;
            }
        }
    }
</style>

<style lang="less">
    .match-keyword-popover {
        padding: 0;
    }
</style>