import { Buffer } from "node:buffer"
import Chat from "../data/Chat.ts"
import FileManager from "../data/FileManager.ts"
import MessagesManager from "../data/MessagesManager.ts"
import User from "../data/User.ts"
import UserChatLinker from "../data/UserChatLinker.ts"
import ApiManager from "./ApiManager.ts"
import BaseApi from "./BaseApi.ts"
import TokenManager from "./TokenManager.ts"
import ChatPrivate from "../data/ChatPrivate.ts"
import ChatGroup from "../data/ChatGroup.ts"

export default class ChatApi extends BaseApi {
    override getName(): string {
        return "Chat"
    }
    override onInit(): void {
        /**
         * 獲取對話訊息
         * @param token 令牌
         * @param target 目標對話
         */
        this.registerEvent("Chat.getInfo", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }

            const chat = Chat.findById(args.target as string)
            if (chat == null) return {
                code: 404,
                msg: "对话不存在",
            }
            if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
                code: 400,
                msg: "用户无权访问此对话",
            }

            // 私聊
            if (chat!.bean.type == 'private') {
                const mine = User.findById(token.author) as User

                return {
                    code: 200,
                    msg: "成功",
                    data: {
                        id: args.target as string,
                        type: chat.bean.type,
                        title: chat.getTitle(mine),
                        avatar: chat.getAvatarFileHash(mine) ? "uploaded_files/" + chat.getAvatarFileHash(mine) : undefined
                    }
                }
            }
            if (chat!.bean.type == 'group') {
                return {
                    code: 200,
                    msg: "成功",
                    data: {
                        id: args.target as string,
                        type: chat.bean.type,
                        title: chat.getTitle(),
                        avatar: chat.getAvatarFileHash() ? "uploaded_files/" + chat.getAvatarFileHash() : undefined
                    }
                }
            }

            return {
                code: 501,
                msg: "not implmented",
            }
        })
        /**
         * 發送訊息
         * @param token 令牌
         * @param target 目標對話
         * @param text 消息内容
         */
        this.registerEvent("Chat.sendMessage", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target', 'text'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }

            const chat = Chat.findById(args.target as string)
            if (chat == null) return {
                code: 404,
                msg: "对话不存在",
            }
            if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
                code: 400,
                msg: "用户无权访问此对话",
            }

            const msg = {
                text: args.text as string,
                time: Date.now(),
                user_id: token.author,
            }
            const id = MessagesManager.getInstanceForChat(chat).addMessage(msg)

            const users: string[] = UserChatLinker.getChatMembers(chat.bean.id)
            for (const user of users) {
                if (ApiManager.checkUserIsOnline(user)) {
                    const userInst = User.findById(user)
                    userInst?.updateRecentChat(chat.bean.id, args.text as string)
                    const sockets = ApiManager.getUserClientSockets(user)
                    for (const socket of Object.keys(sockets))
                        this.emitToClient(sockets[socket], 'Client.onMessage', {
                            chat: chat.bean.id,
                            msg: {
                                ...msg,
                                id
                            }
                        })
                } else {
                    // TODO: EventStore
                }
            }

            return {
                code: 200,
                msg: "成功",
            }
        })
        /**
         * 拉取歷史訊息
         * @param token 令牌
         * @param target 目標對話
         * @param page 頁面
         */
        this.registerEvent("Chat.getMessageHistory", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target', 'page'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }

            const chat = Chat.findById(args.target as string)
            if (chat == null) return {
                code: 404,
                msg: "对话不存在",
            }
            if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
                code: 400,
                msg: "用户无权访问此对话",
            }

            return {
                code: 200,
                msg: "成功",
                data: {
                    messages: MessagesManager.getInstanceForChat(chat).getMessagesWithPage(15, args.page as number),
                },
            }
        })
        /**
         * 上傳文件
         * @param token 令牌
         * @param target 目標對話
         * @param file_name 文件名稱
         * @param data 文件二進制數據
         */
        this.registerEvent("Chat.uploadFile", async (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target', 'data', 'file_name'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }

            const chat = Chat.findById(args.target as string)
            if (chat == null) return {
                code: 404,
                msg: "对话不存在",
            }
            if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
                code: 400,
                msg: "用户无权访问此对话",
            }

            const file = await FileManager.uploadFile(args.file_name as string, args.data as Buffer<ArrayBufferLike>, args.target as string)

            return {
                code: 200,
                msg: "成功",
                data: {
                    file_path: 'uploaded_files/' + file.getHash()
                },
            }
        })
        /**
         * 获取私聊的 ChatId
         * @param token 令牌
         * @param target 目標用户
         */
        this.registerEvent("Chat.getIdForPrivate", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }
            const user = User.findById(token.author) as User
            const targetUser = User.findById(args.target as string) as User
            if (targetUser == null) {
                return {
                    msg: "找不到用户",
                    code: 404,
                }
            }
            const chat = ChatPrivate.findOrCreateForPrivate(user, targetUser)

            return {
                code: 200,
                msg: '成功',
                data: {
                    chat_id: chat.bean.id,
                }
            }
        })
        /**
         * 创建群组
         * @param token 令牌
         * @param title 名称
         * @param [id] 群组 ID
         */
        this.registerEvent("Chat.createGroup", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'title'])) return {
                msg: "参数缺失",
                code: 400,
            }
            if (this.checkArgsEmpty(args, ['title'])) return {
                msg: "参数不得为空",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }
            const user = User.findById(token.author) as User
            
            const haveId = args.id && (args.id as string) != ''
            if (haveId && Chat.findById(args.id as string) != null) return {
                msg: "对话 ID 已被占用",
                code: 403,
            }
            
            const chat = ChatGroup.createGroup(haveId ? undefined : args.id as string)
            chat.setTitle(args.title as string)
            chat.addMembers([
                user.bean.id,
            ])
            user.addContact(chat.bean.id)

            return {
                code: 200,
                msg: '成功',
                data: {
                    chat_id: chat.bean.id,
                }
            }
        })
        /**
         * 从私聊获取对方的 UserId
         * @param token 令牌
         * @param target 目標对话
         */
        this.registerEvent("Chat.getAnotherUserIdFromPrivate", (args, { deviceId }) => {
            if (this.checkArgsMissing(args, ['token', 'target'])) return {
                msg: "参数缺失",
                code: 400,
            }

            const token = TokenManager.decode(args.token as string)
            if (!this.checkToken(token, deviceId)) return {
                code: 401,
                msg: "令牌无效",
            }

            const user = User.findById(token.author) as User

            const chat = Chat.findById(args.target as string)
            if (chat == null) return {
                code: 404,
                msg: "对话不存在",
            }
            if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
                code: 400,
                msg: "用户无权访问此对话",
            }

            if (chat.bean.type == 'private')
                return {
                    code: 200,
                    msg: '成功',
                    data: {
                        user_id: chat.getAnotherUserForPrivate(user)?.bean.id
                    }
                }
            
            return {
                code: 403,
                msg: "非私聊对话",
            }
        })
    }
}