import { useEffect, useState } from "react";
import { useDownloadStatus } from "../TransportProvider/useDownloadStatus";
import { useUploadStatus } from "../TransportProvider/useUploadStatus";
import { useFileStorage } from "../../hooks/useFileStorage";
import { usePublicKey } from "../AccountProvider/usePublicKey";
import { usePrivatePlain } from "../AccountProvider/usePrivatePlain";
import { decodeWithPassword, encodeWithPassword, generateMd5 } from "../../workers/crypto/crypto";
import { useTransport } from "../TransportProvider/useTransport";
import { useDialogsCache } from "../DialogProvider/useDialogsCache";
import { useConsoleLogger } from "../../hooks/useConsoleLogger";
import { Attachment, AttachmentType } from "../ProtocolProvider/protocol/packets/packet.message";
import { useMemory } from "../MemoryProvider/useMemory";
import { useSaveAvatar } from "../AvatarProvider/useSaveAvatar";
import { AVATAR_PASSWORD_TO_ENCODE } from "@/app/constants";
import { useDialog } from "../DialogProvider/useDialog";
import { useCore } from "@/app/hooks/useCore";
import { MessageProps } from "@/app/components/Messages/Message";
import { useGroups } from "../DialogProvider/useGroups";

export enum DownloadStatus {
    DOWNLOADED,
    NOT_DOWNLOADED,
    PENDING,
    DECRYPTING,
    DOWNLOADING,
    ERROR
}

export function useAttachment(attachment: Attachment, parentMessage: MessageProps) {
    const uploadedPercentage = useUploadStatus(attachment.id);
    const downloadPercentage = useDownloadStatus(attachment.id);
    const [downloadStatus, setDownloadStatus] = useMemory("attachment-downloaded-status-" + attachment.id, DownloadStatus.PENDING, true);
    const [downloadTag, setDownloadTag] = useState(attachment.transport.transport_tag || "");
    const {readFile, writeFile, fileExists, size} = useFileStorage();
    const { downloadFile } = useTransport();
    const publicKey = usePublicKey();
    const privatePlain = usePrivatePlain();
    const {updateAttachmentInDialogCache} = useDialogsCache();
    const {info} = useConsoleLogger('useAttachment');
    const {updateAttachmentsInMessagesByAttachmentId} = useDialog();
    const {getDownloadsPath} = useCore();
    const {hasGroup} = useGroups();
    const {dialog} = useDialog();

    const saveAvatar = useSaveAvatar();

    useEffect(() => {
        calcDownloadStatus();
    }, []);

    const getPreview = () => {
        return attachment.preview;
    }

    const calcDownloadStatus = async () => {
        console.info("ds", attachment);
        if (downloadStatus == DownloadStatus.DOWNLOADED) {
            return;
        }
        if(attachment.transport.transport_tag == ""){
            /**
             * Транспортного тега нет только у сообщений отправленных нами, значит он точно наш
             */
            setDownloadStatus(DownloadStatus.DOWNLOADED);
            return;
        }
        if(attachment.type == AttachmentType.FILE){
            /**
             * Если это файл, то он хранится не в папке медиа,
             * а в загрузках, статус скачивания определяем не только по названию файла,
             * но и по его размеру (если размеры и название совпало, то считаем файл скаченным)
             */
            const preview = getPreview();
            const filesize = parseInt(preview.split("::")[0]);
            const filename = preview.split("::")[1];
            const downloadsPath = await getDownloadsPath();
            let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename;
            const exists = await fileExists(pathInDownloads, false);
            const existsLength = await size(pathInDownloads, false);
            if(exists && existsLength == filesize){
                /**
                 * Если название файла и его размер совпадают (и он существует), то считаем его скаченным
                 */
                setDownloadStatus(DownloadStatus.DOWNLOADED);
                return;
            }
            setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
            return;
        }
        if(attachment.type == AttachmentType.AVATAR){
            /**
             * Если это аватар, то он хранится не в папке медиа,
             * а в папке аватарок
             */
            const fileData = await readFile(`a/${await generateMd5(attachment.id + publicKey)}`);
            if(fileData){
                setDownloadStatus(DownloadStatus.DOWNLOADED);
                return;
            }
            setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
            return;
        }

        const fileData = await readFile(`m/${await generateMd5(attachment.id + publicKey)}`);
        if(fileData){
            setDownloadStatus(DownloadStatus.DOWNLOADED);
            return;
        }
        setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
    }

    const getBlob = async () => {
        if(attachment.blob && attachment.blob != ""){
            return attachment.blob;
        }
        const folder = (attachment.type == AttachmentType.AVATAR) ? "a" : "m";
        const fileData = await readFile(`${folder}/${await generateMd5(attachment.id + publicKey)}`);
        if (!fileData) {
            return "";
        }
        const password = (attachment.type == AttachmentType.AVATAR) ? AVATAR_PASSWORD_TO_ENCODE : privatePlain;
        const decryptedData = await decodeWithPassword(password, Buffer.from(fileData, 'binary').toString());
        return decryptedData;
    }

    const download = async () => {
        if(downloadStatus == DownloadStatus.DOWNLOADED){
            return;
        }
        if (downloadTag == "") {
            return;
        }
        setDownloadStatus(DownloadStatus.DOWNLOADING);
        info("Downloading attachment: " + downloadTag);
        let downloadedBlob = '';
        try {
            downloadedBlob = await downloadFile(attachment.id,
                downloadTag, attachment.transport.transport_server);
        } catch (e) {
            console.info(e);
            info("Error downloading attachment: " + attachment.id);
            setDownloadStatus(DownloadStatus.ERROR);
            return;
        } 
        setDownloadStatus(DownloadStatus.DECRYPTING);
        console.info("decoding with key " + parentMessage.chacha_key_plain);
        //console.info("Decrypted attachment ", Buffer.from(keyPlain, 'binary').toString('hex'));
        const decrypted = await decodeWithPassword(parentMessage.chacha_key_plain, downloadedBlob);
        setDownloadTag("");
        if(attachment.type == AttachmentType.FILE) {
            /**
             * Если это файл то шифрованную копию не пишем,
             * пишем его сразу в загрузки
             */
            const preview = getPreview();
            const filename = preview.split("::")[1];
            const downloadsPath = await getDownloadsPath();
            let buffer = Buffer.from(decrypted.split(",")[1], 'base64');
            let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename;
            /**
             * Пишем файл в загрузки, но перед этим выбираем ему название, если файл в загрузках 
             * уже есть с таким названием то добавляем к названию (1), (2) и так далее, чтобы не перезаписать существующий файл
             */
            let finalPath = pathInDownloads;
            let fileIndex = 1;
            while (await fileExists(finalPath, false)) {
                finalPath = downloadsPath + "/Rosetta Downloads/" + filename.split(".").slice(0, -1).join(".") + ` (${fileIndex})` + "." + filename.split(".").slice(-1);
                fileIndex++;
            }
            await writeFile(finalPath, buffer, false);
            setDownloadStatus(DownloadStatus.DOWNLOADED);
            return;
        }
        if(attachment.type == AttachmentType.AVATAR) {
            /**
             * Аватарки, пишем их в папку аватарок 
             */
            const avatarPath = `a/${await generateMd5(attachment.id + publicKey)}`;
            await writeFile(avatarPath,
                Buffer.from(await encodeWithPassword(AVATAR_PASSWORD_TO_ENCODE, decrypted)));
            setDownloadStatus(DownloadStatus.DOWNLOADED);
            /**
             * Устанавливаем аватарку тому, кто ее прислал.
             */
            let avatarSetTo = parentMessage.from;
            if(hasGroup(dialog)){
                avatarSetTo = dialog;
            }
            saveAvatar(avatarSetTo, avatarPath, decrypted);
            return;
        }
        /**
         * Если это не файл, то обновляем состояние кэша,
         * и пишем шифрованную копию
         */
        updateAttachmentInDialogCache(attachment.id, decrypted);
        updateAttachmentsInMessagesByAttachmentId(attachment.id, decrypted);
        await writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
        Buffer.from(await encodeWithPassword(privatePlain, decrypted)).toString('binary'));
        setDownloadStatus(DownloadStatus.DOWNLOADED);
    }

    return {
        uploadedPercentage,
        downloadPercentage,
        downloadStatus,
        getPreview,
        getBlob,
        download
    };
}