import { mergeDataArray, checkPostIds, getAllIds, getPostsByIds } from "db";
import { log, logID } from "log";
async function bytesToBase64DataUrl(bytes, type = "application/octet-stream") {
    return await new Promise((resolve, reject) => {
        const reader = Object.assign(new FileReader(), {
            onload: () => resolve(reader.result),
            onerror: () => reject(reader.error),
        });
        reader.readAsDataURL(new File([bytes], "", { type }));
    });
}
async function arrayBufferToBase64(buffer) {
    var bytes = new Uint8Array(buffer);
    return (await bytesToBase64DataUrl(bytes)).replace("data:application/octet-stream;base64,", "");
}
async function base64ToArrayBuffer(base64String) {
    let response;
    try {
        response = await fetch("data:application/octet-stream;base64," + base64String);
    }
    catch (e) {
        console.log("error", e, base64String);
        return null;
    }
    let arrayBuffer = await response.arrayBuffer();
    return arrayBuffer;
}
export class Sync {
    constructor() {
        this.isArchivePeer = false;
        this.userID = "";
        this.userPeers = new Map();
        this.userIDsToSync = new Set();
        this.syncSuperlog = false;
        this.userBlockList = new Set([
            '5d63f0b2-a842-41bf-bf06-e0e4f6369271',
            '5f1b85c4-b14c-454c-8df1-2cacc93f8a77',
            // 'bba3ad24-9181-4e22-90c8-c265c80873ea'
        ]);
        this.postBlockList = new Set([
            '1c71f53c-c467-48e4-bc8c-39005b37c0d5',
            '64203497-f77b-40d6-9e76-34d17372e72a',
            '243130d8-4a41-471e-8898-5075f1bd7aec',
            'e01eff89-5100-4b35-af4c-1c1bcb007dd0',
            '194696a2-d850-4bb0-98f7-47416b3d1662',
            'f6b21eb1-a0ff-435b-8efc-6a3dd70c0dca',
            'dd1d92aa-aa24-4166-a925-94ba072a9048',
            '290dbb4f-6ce1-491a-b90d-51d8efcd3d60',
            '3b32d0eb-94d5-49c4-a43b-0959a9fbb015'
        ]);
        // async getPostIdsForUserHandler(data: any) {
        //     let message = data.message;
        //     let postIds = await getAllIds(message.user_id) ?? [];
        //     postIds = postIds.filter((postID: string) => !this.postBlockList.has(postID));
        //     if (postIds.length === 0) {
        //       console.log.apply(null, log(`Net: I know about user ${logID(message.user_id)} but I have 0 posts, so I'm not sending any to to peer ${logID(data.from)}`));;
        //       return;
        //     }
        //     console.log.apply(null, log(`Net: Sending ${postIds.length} post Ids for user ${logID(message.user_id)} to peer ${logID(data.from)}`));
        //     let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_post_ids_for_user_response", post_ids: postIds, user_id: message.user_id } }
        //     this.send(responseMessage);
        //   }
    }
    setArchive(isArchive) {
        this.isArchivePeer = isArchive;
    }
    setUserID(userID) {
        this.userID = userID;
        this.userIDsToSync = new Set(this.getFollowing(userID));
    }
    shouldSyncUserID(userID) {
        let shouldSyncAllUsers = this.isArchivePeer;
        if (shouldSyncAllUsers) {
            return true;
        }
        return this.userIDsToSync.has(userID);
    }
    getPeersForUser(userID) {
        let peers = this.userPeers.get(userID);
        if (!peers) {
            return [];
        }
        return [...peers.keys()];
    }
    addUserPeer(userID, peerID) {
        this.syncSuperlog && console.log.apply(null, log(`[sync] addUserPeer user:${logID(userID)} peer:${logID(peerID)}`));
        ;
        if (!this.userPeers.has(userID)) {
            this.userPeers.set(userID, new Set());
        }
        let peers = this.userPeers.get(userID);
        peers.add(peerID);
        // this.syncSuperlog && console.log.apply(null, log(this.userPeers));
    }
    deleteUserPeer(peerIDToDelete) {
        for (const peers of this.userPeers.values()) {
            for (const peerID of peers) {
                if (peerID === peerIDToDelete) {
                    peers.delete(peerIDToDelete);
                }
            }
        }
    }
    //     shouldSyncUserID(userID: string) {
    //     if (app.isHeadless) {
    //       return true;
    //     }
    //     return this.UserIDsTothis.has(userID);
    //   }
    async getKnownUsers() {
        let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
        knownUsers = knownUsers
            .filter(userID => this.shouldSyncUserID(userID))
            .filter(userID => !this.userBlockList.has(userID))
            .filter(async (userID) => (await getAllIds(userID)).length > 0); // TODO:EASYOPT getting all the IDs is unecessary, replace it with a test to get a single ID.
        return knownUsers;
    }
    getFollowing(userID) {
        let following = ['a0e42390-08b5-4b07-bc2b-787f8e5f1297']; // Follow BMO by default :)
        following.push(this.userID);
        // Hazel 
        if (userID == '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5') {
            following.push(...[
                '8f6802be-c3b6-46c1-969c-5f90cbe01479', // Fiona
                '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel
            ]);
        }
        // Rob
        if (userID === 'b38b623c-c3fa-4351-9cab-50233c99fa4e') {
            following.push(...[
                '6d774268-16cd-4e86-8bbe-847a0328893d', // Sean
                '05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin
                'bba3ad24-9181-4e22-90c8-c265c80873ea', // Harry
                '8f6802be-c3b6-46c1-969c-5f90cbe01479', // Fiona
                '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel
            ]);
        }
        // Martin
        if (userID === '05a495a0-0dd8-4186-94c3-b8309ba6fc4c') {
            following.push(...[
                'b38b623c-c3fa-4351-9cab-50233c99fa4e', // Rob
            ]);
        }
        // Fiona
        if (userID === '8f6802be-c3b6-46c1-969c-5f90cbe01479') {
            following.push(...[
                'b38b623c-c3fa-4351-9cab-50233c99fa4e', // Rob
                '05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin
                '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel
            ]);
        }
        return following;
    }
    async getPostIdsForUser(userID) {
        let postIds = await getAllIds(userID) ?? [];
        postIds = postIds.filter((postID) => !this.postBlockList.has(postID));
        if (postIds.length === 0) {
            console.log.apply(null, log(`Net: I know about user ${logID(userID)} but I have 0 posts`));
            return null;
        }
        return postIds;
    }
    async checkPostIds(userID, peerID, postIDs) {
        let startTime = performance.now();
        let neededPostIds = await checkPostIds(userID, postIDs);
        this.syncSuperlog && console.log.apply(null, log(`[sync] ID Check for user ${logID(userID)} with IDs from peer[${logID(peerID)}] took ${(performance.now() - startTime).toFixed(2)}ms`));
        if (neededPostIds.length > 0) {
            this.syncSuperlog && console.log.apply(null, log(`[sync] Need posts (${neededPostIds.length}) for user[${logID(userID)}] from peer[${logID(peerID)}]`));
            ;
        }
        else {
            this.syncSuperlog && console.log.apply(null, log(`[sync] Don't need any posts for user[${logID(userID)}] from peer[${logID(peerID)}]`));
            ;
        }
        // if (postIds.length === 0) {
        //   return [];
        // }
        return neededPostIds;
    }
    async getPostsForUser(userID, postIDs) {
        let posts = await getPostsByIds(userID, postIDs) ?? [];
        console.log.apply(null, log(`[sync] got ${posts.length} posts for user ${logID(userID)}`));
        ;
        //   app.timerStart();
        let output = [];
        console.log.apply(null, log("Serializing images"));
        for (let post of posts) {
            let newPost = post.data;
            if (newPost.image_data) {
                // let compressedData = await wsConnection.compressArrayBuffer(newPost.image_data);
                // console.log.apply(null, log((newPost.image_data.byteLength - compressedData.byteLength) / 1024 / 1024);
                // TODO don't do this, use Blobs direclty!
                // https://developer.chrome.com/blog/blob-support-for-Indexeddb-landed-on-chrome-dev
                newPost.image_data = await arrayBufferToBase64(newPost.image_data);
            }
            // let megs = JSON.stringify(newPost).length/1024/1024;
            // console.log.apply(null, log(`getPostsForUserHandler id:${newPost.post_id} post length:${megs}`);
            output.push(newPost);
        }
        return output;
        //   console.log.apply(null, log(`getPostsForUser`,output));
    }
    async writePostForUser(userID, post) {
        // HACK: Some posts have insanely large images, so I'm gonna skip them.
        // Once we support delete then we we could delete these posts in a sensible way.
        if (this.postBlockList.has(post.post_id)) {
            console.log.apply(null, log(`Skipping blocked post: ${post.post_id}`));
            ;
            return;
        }
        // HACK - some posts had the wrong author ID
        if (userID === this.userID) {
            post.author_id = this.userID;
        }
        post.post_timestamp = new Date(post.post_timestamp);
        if (post.image_data) {
            let imageDataArrayBuffer = await base64ToArrayBuffer(post.image_data);
            if (imageDataArrayBuffer === null) {
                this.syncSuperlog && console.log(`[sync] Failed to create arraybuffer for image for post userID:${userID} postID:${post.post_id} `);
                return;
            }
            post.image_data = imageDataArrayBuffer;
            // skip posts with images for now.
            // return;
        }
        console.log.apply(null, log(`Merging same user peer posts...`));
        await mergeDataArray(userID, [post]);
    }
}
//# sourceMappingURL=Sync.js.map