import { encodeWithPassword } from "@/app/workers/crypto/crypto";
import { MessageReply } from "../DialogProvider/useReplyMessages";
import { Attachment, AttachmentType } from "../ProtocolProvider/protocol/packets/packet.message";
import { base64ImageToBlurhash } from "@/app/workers/image/image";
import { MESSAGE_MAX_TIME_TO_DELEVERED_S } from "@/app/constants";
import { useContext, useRef } from "react";
import { useTransport } from "../TransportProvider/useTransport";
import { useDialogsList } from "../DialogListProvider/useDialogsList";
import { useDatabase } from "../DatabaseProvider/useDatabase";
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
import { useDialogsCache } from "../DialogProvider/useDialogsCache";
import { AttachmentMeta, DialogContext } from "../DialogProvider/DialogProvider";
import { useTransportServer } from "../TransportProvider/useTransportServer";

export function usePrepareAttachment() {
    const intervalsRef = useRef<NodeJS.Timeout>(null);
    const {uploadFile} = useTransport();
    const {updateDialog} = useDialogsList();
    const {runQuery, getQuery} = useDatabase();
    const {info} = useConsoleLogger('usePrepareAttachment');
    const {getDialogCache} = useDialogsCache();
    const context = useContext(DialogContext);
    const transportServer = useTransportServer();

    const updateTimestampInDialogCache = (dialog : string, message_id: string) => {
        const dialogCache = getDialogCache(dialog);
        if(dialogCache == null){
            return;
        }
        for(let i = 0; i < dialogCache.length; i++){
            if(dialogCache[i].message_id == message_id){
                dialogCache[i].timestamp = Date.now();
                break;
            }
        }
    }

    /**
     * Обновляет транспортный сервер в кэше, чтобы поддерживать его в актуальном состоянии после загрузки
     */
    const updateAttachmentTransportInCache = (dialog: string, message_id : string, attachment: Attachment) => {
        const dialogCache = getDialogCache(dialog);
        if(dialogCache == null){
            return;
        }
        for(let i = 0; i < dialogCache.length; i++){
            if(dialogCache[i].message_id == message_id){
                for(let j = 0; j < dialogCache[i].attachments.length; j++){
                    if(dialogCache[i].attachments[j].id == attachment.id){
                        dialogCache[i].attachments[j].transport = attachment.transport;
                    }
                }
            }
        }
    }

    /**
     * Обновляет транспорт в базе после загрузки вложения (нам нужно сохранить транспорт)
     */
    const updateAttachmentTransportInDatabase = async (message_id : string, attachment: Attachment) => {
        let message = await getQuery(`SELECT attachments FROM messages WHERE message_id = ?`, [message_id]);
        console.info(message)
        if(!message){
            return;
        }
        if(message.attachments == '[]'){
            return;
        }
        let meta : AttachmentMeta[] = JSON.parse(message.attachments);
        for(let i = 0; i < meta.length; i++){
            if(meta[i].id == attachment.id){
                meta[i].transport = attachment.transport;
            }
        }
        await runQuery(`UPDATE messages SET attachments = ? WHERE message_id = ?`, [JSON.stringify(meta), message_id]);
    }

    /**
     * Обновляет вложение в стейте сообщений
     */
    const updateAttachmentTransportInContext = (message_id: string, attachment : Attachment) => {
        if(context == null || !context){
            /**
             * Если этот диалог сейчас не открыт
             */
            return;
        }
        context.setMessages((prev) => {
            return prev.map((value) => {
                if(value.message_id != message_id){
                    return value;
                }
                for(let i = 0; i < value.attachments.length; i++){
                    if(value.attachments[i].id != attachment.id){
                        return value;
                    }
                    value.attachments[i].transport = attachment.transport;
                    return value;
                }
                return value;
            })
        });
    }

    const updateTransportAfterUploading = async (dialog: string, message_id : string, attachment: Attachment) => {
        updateAttachmentTransportInCache(dialog, message_id, attachment);
        updateAttachmentTransportInDatabase(message_id, attachment);
        updateAttachmentTransportInContext(message_id, attachment);
    }

    /**
     * Обновляет временную метку в сообщении, пока вложения отправляются,
     * потому что если этого не делать, то сообщение может быть помечено как
     * не доставленное из-за таймаута доставки
     * @param attachments Вложения
     */
    const doTimestampUpdateImMessageWhileAttachmentsSend = (message_id: string, dialog: string) => {
        if(intervalsRef.current){
            clearInterval(intervalsRef.current);
        }
        intervalsRef.current = setInterval(async () => {
            /**
             * Обновляем время в левом меню
             */
            await runQuery("UPDATE messages SET timestamp = ? WHERE message_id = ?", [Date.now(), message_id]);
            updateDialog(dialog);
            /**
             * Обновляем состояние в кэше диалогов
             */
            updateTimestampInDialogCache(dialog, message_id);

            if(context == null || !context){
                /**
                 * Если этот диалог сейчас не открыт
                 */
                return;
            }
            context.setMessages((prev) => {
                return prev.map((value) => {
                    if(value.message_id != message_id){
                        return value;
                    }
                    return {
                        ...value,
                        timestamp: Date.now()
                    };
                })
            });
        }, (MESSAGE_MAX_TIME_TO_DELEVERED_S / 2) * 1000);
    }

    /**
     * Подготавливает вложения для отправки. Подготовка
     * состоит в загрузке файлов на транспортный сервер, мы не делаем
     * это через WebSocket из-за ограничений по размеру сообщений,
     * а так же из-за надежности доставки файлов через HTTP
     * @param attachments Attachments to prepare for sending
     */
    const prepareAttachmentsToSend = async (message_id: string, dialog: string, password: string, attachments : Attachment[]) : Promise<Attachment[]> => {
        if(attachments.length <= 0){
            return [];
        }
        let prepared : Attachment[] = [];
        try{
            for(let i = 0; i < attachments.length; i++){
                const attachment : Attachment = attachments[i];
                if(attachment.type == AttachmentType.CALL){
                    /**
                     * Звонк загружать не надо, по этому просто отправляем его как есть, там нет blob
                     */
                    prepared.push(attachment);
                    continue;
                }
                if(attachment.type == AttachmentType.MESSAGES){
                    let reply : MessageReply[] = JSON.parse(attachment.blob);
                    for(let j = 0; j < reply.length; j++){
                        for(let k = 0; k < reply[j].attachments.length; k++){
                            reply[j].attachments[k].blob = "";
                        }
                    }
                    prepared.push({
                        ...attachment,
                        blob: await encodeWithPassword(password, JSON.stringify(reply))
                    });
                    continue;
                }
                if((attachment.type == AttachmentType.IMAGE 
                    || attachment.type == AttachmentType.AVATAR) && attachment.preview == ""){
                    /**
                     * Загружаем превью blurhash для изображения
                     */
                    const blurhash = await base64ImageToBlurhash(attachment.blob);
                    attachment.preview = blurhash;
                }
                doTimestampUpdateImMessageWhileAttachmentsSend(message_id, dialog);
                const content = await encodeWithPassword(password, attachment.blob);
                const upid = attachment.id;
                info(`Uploading attachment with upid: ${upid}`);
                info(`Attachment content length: ${content.length}`);
                let tag = await uploadFile(upid, content);
                info(`Uploaded attachment with upid: ${upid}, received tag: ${tag}`);
                if(intervalsRef.current != null){
                    clearInterval(intervalsRef.current);
                }
                const preparedAttachment : Attachment = {
                    ...attachment,
                    transport: {
                        transport_server: transportServer || "",
                        transport_tag: tag
                    },
                    preview: attachment.preview,
                    blob: ""
                };
                await updateTransportAfterUploading(dialog, message_id, preparedAttachment);
                prepared.push(preparedAttachment);
            }
            return prepared;
        }catch(e){
            return prepared;
        }
    }

    return {
        prepareAttachmentsToSend
    }
}