import {
    OB11Group,
    OB11GroupMember,
    OB11GroupMemberRole,
    OB11Message,
    OB11MessageData,
    OB11MessageDataType,
    OB11User,
    OB11UserSex
} from "./types";
import {
    AtType,
    ChatType,
    GrayTipElementSubType,
    Group,
    GroupMember,
    IMAGE_HTTP_HOST,
    RawMessage,
    SelfInfo,
    Sex,
    TipGroupElementType,
    User, VideoElement
} from '../ntqqapi/types';
import {getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data';
import {EventType} from "./event/OB11BaseEvent";
import {encodeCQCode} from "./cqcode";
import {dbUtil} from "../common/db";
import {OB11GroupIncreaseEvent} from "./event/notice/OB11GroupIncreaseEvent";
import {OB11GroupBanEvent} from "./event/notice/OB11GroupBanEvent";
import {OB11GroupUploadNoticeEvent} from "./event/notice/OB11GroupUploadNoticeEvent";
import {OB11GroupNoticeEvent} from "./event/notice/OB11GroupNoticeEvent";
import {NTQQUserApi} from "../ntqqapi/api/user";
import {NTQQFileApi} from "../ntqqapi/api/file";
import {calcQQLevel} from "../common/utils/qqlevel";
import {log} from "../common/utils/log";
import {sleep} from "../common/utils/helper";
import {getConfigUtil} from "../common/config";
import {OB11GroupTitleEvent} from "./event/notice/OB11GroupTitleEvent";
import {OB11GroupCardEvent} from "./event/notice/OB11GroupCardEvent";


export class OB11Constructor {
    static async message(msg: RawMessage): Promise<OB11Message> {

        const {enableLocalFile2Url, ob11: {messagePostFormat}} = getConfigUtil().getConfig()
        const message_type = msg.chatType == ChatType.group ? "group" : "private";
        const resMsg: OB11Message = {
            self_id: parseInt(selfInfo.uin),
            user_id: parseInt(msg.senderUin),
            time: parseInt(msg.msgTime) || Date.now(),
            message_id: msg.msgShortId,
            real_id: msg.msgShortId,
            message_type: msg.chatType == ChatType.group ? "group" : "private",
            sender: {
                user_id: parseInt(msg.senderUin),
                nickname: msg.sendNickName,
                card: msg.sendMemberName || "",
            },
            raw_message: "",
            font: 14,
            sub_type: "friend",
            message: messagePostFormat === 'string' ? '' : [],
            message_format: messagePostFormat === 'string' ? 'string' : 'array',
            post_type: selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
        }
        if (msg.chatType == ChatType.group) {
            resMsg.sub_type = "normal" // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼
            resMsg.group_id = parseInt(msg.peerUin)
            const member = await getGroupMember(msg.peerUin, msg.senderUin);
            if (member) {
                resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
                resMsg.sender.nickname = member.nick
            }
        } else if (msg.chatType == ChatType.friend) {
            resMsg.sub_type = "friend"
            const friend = await getFriend(msg.senderUin);
            if (friend) {
                resMsg.sender.nickname = friend.nick;
            }
        } else if (msg.chatType == ChatType.temp) {
            resMsg.sub_type = "group"
            const tempGroupCode = tempGroupCodeMap[msg.peerUin]
            if (tempGroupCode) {
                resMsg.group_id = parseInt(tempGroupCode)
            }
        }

        for (let element of msg.elements) {
            let message_data: OB11MessageData | any = {
                data: {},
                type: "unknown"
            }
            if (element.textElement && element.textElement?.atType !== AtType.notAt) {
                message_data["type"] = OB11MessageDataType.at
                if (element.textElement.atType == AtType.atAll) {
                    // message_data["data"]["mention"] = "all"
                    message_data["data"]["qq"] = "all"
                } else {
                    let atUid = element.textElement.atNtUid
                    let atQQ = element.textElement.atUid
                    if (!atQQ || atQQ === "0") {
                        const atMember = await getGroupMember(msg.peerUin, atUid)
                        if (atMember) {
                            atQQ = atMember.uin
                        }
                    }
                    if (atQQ) {
                        // message_data["data"]["mention"] = atQQ
                        message_data["data"]["qq"] = atQQ
                    }
                }
            } else if (element.textElement) {
                message_data["type"] = "text"
                let text = element.textElement.content
                if (!text.trim()) {
                    continue;
                }
                message_data["data"]["text"] = text
            } else if (element.replyElement) {
                message_data["type"] = "reply"
                // log("收到回复消息", element.replyElement.replayMsgSeq)
                try {
                    const replyMsg = await dbUtil.getMsgBySeqId(element.replyElement.replayMsgSeq)
                    // log("找到回复消息", replyMsg.msgShortId, replyMsg.msgId)
                    if (replyMsg) {
                        message_data["data"]["id"] = replyMsg.msgShortId.toString()
                    } else {
                        continue
                    }
                } catch (e) {
                    log("获取不到引用的消息", e.stack, element.replyElement.replayMsgSeq)
                }

            } else if (element.picElement) {
                message_data["type"] = "image"
                // message_data["data"]["file"] = element.picElement.sourcePath
                message_data["data"]["file"] = element.picElement.fileName
                // message_data["data"]["path"] = element.picElement.sourcePath
                const url = element.picElement.originImageUrl
                const fileMd5 = element.picElement.md5HexStr
                if (url) {
                    message_data["data"]["url"] = IMAGE_HTTP_HOST + url
                } else if (fileMd5 && element.picElement.fileUuid.indexOf("_") === -1) { // fileuuid有下划线的是Linux发送的,这个url是另外的格式,目前尚未得知如何组装
                    message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${fileMd5.toUpperCase()}/0`
                }
                // message_data["data"]["file_id"] = element.picElement.fileUuid
                message_data["data"]["file_size"] = element.picElement.fileSize
                dbUtil.addFileCache(element.picElement.fileName, {
                    fileName: element.picElement.fileName,
                    filePath: element.picElement.sourcePath,
                    fileSize: element.picElement.fileSize.toString(),
                    url: message_data["data"]["url"],
                    downloadFunc: async () => {
                        await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
                            element.elementId, element.picElement.thumbPath?.get(0) || "", element.picElement.sourcePath)
                    }
                }).then()
                // 不在自动下载图片

            } else if (element.videoElement || element.fileElement) {
                const videoOrFileElement = element.videoElement || element.fileElement
                const ob11MessageDataType = element.videoElement ? OB11MessageDataType.video : OB11MessageDataType.file
                message_data["type"] = ob11MessageDataType;
                message_data["data"]["file"] = videoOrFileElement.fileName
                message_data["data"]["path"] = videoOrFileElement.filePath
                message_data["data"]["file_id"] = videoOrFileElement.fileUuid
                message_data["data"]["file_size"] = videoOrFileElement.fileSize
                dbUtil.addFileCache(videoOrFileElement.fileUuid, {
                    msgId: msg.msgId,
                    fileName: videoOrFileElement.fileName,
                    filePath: videoOrFileElement.filePath,
                    fileSize: videoOrFileElement.fileSize,
                    downloadFunc: async () => {
                        await NTQQFileApi.downloadMedia(
                            msg.msgId, msg.chatType, msg.peerUid,
                            element.elementId,
                            ob11MessageDataType == OB11MessageDataType.video ? (videoOrFileElement as VideoElement).thumbPath.get(0) : null,
                            videoOrFileElement.filePath)
                    }
                }).then()
                // 怎么拿到url呢
            } else if (element.pttElement) {
                message_data["type"] = OB11MessageDataType.voice;
                message_data["data"]["file"] = element.pttElement.fileName
                message_data["data"]["path"] = element.pttElement.filePath
                // message_data["data"]["file_id"] = element.pttElement.fileUuid
                message_data["data"]["file_size"] = element.pttElement.fileSize
                dbUtil.addFileCache(element.pttElement.fileName, {
                    fileName: element.pttElement.fileName,
                    filePath: element.pttElement.filePath,
                    fileSize: element.pttElement.fileSize,
                }).then()

                // log("收到语音消息", msg)
                // window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => {
                //     console.log("语音转文字结果", text);
                // }).catch(err => {
                //     console.log("语音转文字失败", err);
                // })
            } else if (element.arkElement) {
                message_data["type"] = OB11MessageDataType.json;
                message_data["data"]["data"] = element.arkElement.bytesData;
            } else if (element.faceElement) {
                message_data["type"] = OB11MessageDataType.face;
                message_data["data"]["id"] = element.faceElement.faceIndex.toString();
            } else if (element.marketFaceElement) {
                message_data["type"] = OB11MessageDataType.mface;
                message_data["data"]["text"] = element.marketFaceElement.faceName;
            }
            if (message_data.type !== "unknown" && message_data.data) {
                const cqCode = encodeCQCode(message_data);
                if (messagePostFormat === 'string') {
                    (resMsg.message as string) += cqCode;
                } else (resMsg.message as OB11MessageData[]).push(message_data);

                resMsg.raw_message += cqCode;
            }
        }
        resMsg.raw_message = resMsg.raw_message.trim();
        return resMsg;
    }

    static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent> {
        if (msg.chatType !== ChatType.group) {
            return;
        }
        if (msg.senderUin){
            let member = await getGroupMember(msg.peerUid, msg.senderUin);
            if (member && member.cardName !== msg.sendMemberName) {
                member.cardName = msg.sendMemberName;
                return new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), msg.sendMemberName, member.cardName)
            }
        }
        // log("group msg", msg);
        for (let element of msg.elements) {
            const grayTipElement = element.grayTipElement
            const groupElement = grayTipElement?.groupElement
            if (groupElement) {
                // log("收到群提示消息", groupElement)
                if (groupElement.type == TipGroupElementType.memberIncrease) {
                    log("收到群成员增加消息", groupElement)
                    await sleep(1000);
                    const member = await getGroupMember(msg.peerUid, groupElement.memberUid);
                    let memberUin = member?.uin;
                    if (!memberUin) {
                        memberUin = (await NTQQUserApi.getUserDetailInfo(groupElement.memberUid)).uin
                    }
                    // log("获取新群成员QQ", memberUin)
                    const adminMember = await getGroupMember(msg.peerUid, groupElement.adminUid);
                    // log("获取同意新成员入群的管理员", adminMember)
                    if (memberUin) {
                        const operatorUin = adminMember?.uin || memberUin
                        let event = new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(operatorUin));
                        // log("构造群增加事件", event)
                        return event;
                    }
                } else if (groupElement.type === TipGroupElementType.ban) {
                    log("收到群群员禁言提示", groupElement)
                    const memberUid = groupElement.shutUp.member.uid
                    const adminUid = groupElement.shutUp.admin.uid
                    let memberUin: string = ""
                    let duration = parseInt(groupElement.shutUp.duration)
                    let sub_type: "ban" | "lift_ban" = duration > 0 ? "ban" : "lift_ban"
                    if (memberUid) {
                        memberUin = (await getGroupMember(msg.peerUid, memberUid))?.uin || (await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
                    } else {
                        memberUin = "0";  // 0表示全员禁言
                        if (duration > 0) {
                            duration = -1
                        }
                    }
                    const adminUin = (await getGroupMember(msg.peerUid, adminUid))?.uin || (await NTQQUserApi.getUserDetailInfo(adminUid))?.uin
                    if (memberUin && adminUin) {
                        return new OB11GroupBanEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(adminUin), duration, sub_type);
                    }
                }
            } else if (element.fileElement) {
                return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {
                    id: element.fileElement.fileUuid,
                    name: element.fileElement.fileName,
                    size: parseInt(element.fileElement.fileSize),
                    busid: element.fileElement.fileBizId || 0
                })
            }

            if (grayTipElement) {
                if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) {
                    log("收到新人被邀请进群消息", grayTipElement)
                    const xmlElement = grayTipElement.xmlElement
                    if (xmlElement?.content) {
                        const regex = /jp="(\d+)"/g;

                        let matches = [];
                        let match = null

                        while ((match = regex.exec(xmlElement.content)) !== null) {
                            matches.push(match[1]);
                        }
                        if (matches.length === 2) {
                            const [inviter, invitee] = matches;
                            return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(invitee), parseInt(inviter), "invite");
                        }
                    }
                } else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
                    const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
                    /*
                    {
                      align: 'center',
                      items: [
                        { txt: '恭喜', type: 'nor' },
                        {
                          col: '3',
                          jp: '5',
                          param: ["QQ号"],
                          txt: '林雨辰',
                          type: 'url'
                        },
                        { txt: '获得群主授予的', type: 'nor' },
                        {
                          col: '3',
                          jp: '',
                          txt: '好好好',
                          type: 'url'
                        },
                        { txt: '头衔', type: 'nor' }
                      ]
                    }

                    * */
                    const memberUin = json.items[1].param[0]
                    const title = json.items[3].txt
                    log("收到群成员新头衔消息", json)
                    return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title)
                }
            }
        }
    }

    static friend(friend: User): OB11User {
        return {
            user_id: parseInt(friend.uin),
            nickname: friend.nick,
            remark: friend.remark,
            sex: OB11Constructor.sex(friend.sex),
            level: friend.qqLevel && calcQQLevel(friend.qqLevel) || 0
        }
    }

    static selfInfo(selfInfo: SelfInfo): OB11User {
        return {
            user_id: parseInt(selfInfo.uin),
            nickname: selfInfo.nick,
        }
    }

    static friends(friends: User[]): OB11User[] {
        return friends.map(OB11Constructor.friend)
    }

    static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
        return {
            4: OB11GroupMemberRole.owner,
            3: OB11GroupMemberRole.admin,
            2: OB11GroupMemberRole.member
        }[role]
    }

    static sex(sex: Sex): OB11UserSex {
        const sexMap = {
            [Sex.male]: OB11UserSex.male,
            [Sex.female]: OB11UserSex.female,
            [Sex.unknown]: OB11UserSex.unknown
        }
        return sexMap[sex] || OB11UserSex.unknown
    }

    static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
        return {
            group_id: parseInt(group_id),
            user_id: parseInt(member.uin),
            nickname: member.nick,
            card: member.cardName,
            sex: OB11Constructor.sex(member.sex),
            age: 0,
            area: "",
            level: 0,
            qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
            join_time: 0,  // 暂时没法获取
            last_sent_time: 0,  // 暂时没法获取
            title_expire_time: 0,
            unfriendly: false,
            card_changeable: true,
            is_robot: member.isRobot,
            shut_up_timestamp: member.shutUpTime,
            role: OB11Constructor.groupMemberRole(member.role),
        }
    }

    static stranger(user: User): OB11User {
        return {
            ...user,
            user_id: parseInt(user.uin),
            nickname: user.nick,
            sex: OB11Constructor.sex(user.sex),
            age: 0,
            qid: user.qid,
            login_days: 0,
            level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
        }
    }

    static groupMembers(group: Group): OB11GroupMember[] {
        log("construct ob11 group members", group)
        return group.members.map(m => OB11Constructor.groupMember(group.groupCode, m))
    }

    static group(group: Group): OB11Group {
        return {
            group_id: parseInt(group.groupCode),
            group_name: group.groupName,
            member_count: group.memberCount,
            max_member_count: group.maxMember
        }
    }

    static groups(groups: Group[]): OB11Group[] {
        return groups.map(OB11Constructor.group)
    }
}