// interface MyJsonObject {
//   id: string;
//   name: string;
//   email: string;
// }


// Efficiently storing data in indexdb: https://stackoverflow.com/a/62975917

const postStoreName: string = "posts";
let keyBase = "dandelion_posts_v1_"
let key = "";
let version = 1;


interface IDBRequestEvent<T = any> extends Event {
    target: IDBRequest<T>;
}

// IndexedDB uses DOMException, so let's use it for error typing
type DBError = Event & {
    target: { errorCode: DOMException };
};

function upgrade_0to1(db:IDBDatabase) {
    let store = db.createObjectStore(postStoreName, { keyPath: "id", autoIncrement: true });
    store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
    store.createIndex("postIDIndex", "data.post_id", { unique: true });
}


export function openDatabase(userID: string): Promise<IDBDatabase> {
    const dbName = `user_${userID}`

    return new Promise((resolve, reject) => {
        const request: IDBOpenDBRequest = indexedDB.open(dbName, version);

        request.onerror = (event: Event) => {
            const errorEvent = event as IDBRequestEvent;
            reject(`Database error: ${errorEvent.target.error?.message}`);
        };

        request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
            const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;

            upgrade_0to1(db);
           
        };

        request.onsuccess = (event: Event) => {
            const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
            resolve(db);
        };
    });
}

async function getDBTransactionStore(userID:string) {
    const db = await openDatabase(userID);
    const transaction = db.transaction(postStoreName, "readwrite");
    const store = transaction.objectStore(postStoreName);
    return {db, transaction, store}
}


export async function addData(userID: string, data: any): Promise<void> {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);
        const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });

        addRequest.onsuccess = (e: Event) => {
            // console.log('Data has been added:', (e.target as IDBRequest).result);
        };

        addRequest.onerror = (event: Event) => {
            // Use a type assertion to access the specific properties of IDBRequest error event
            const errorEvent = event as IDBRequestEvent;
            console.error('Error in adding data:', errorEvent.target.error?.message);
        };
    } catch (error) {
        console.error('Error in opening database:', error);
    }
}

export async function deleteData(userID: string, postID: string) {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);
        const index = store.index("postIDIndex");

        const getRequest = index.getKey(postID);

        getRequest.onerror = e => console.log((e.target as IDBRequest).error)

        getRequest.onsuccess = e => {
            const key = (e.target as IDBRequest).result;
            if (key === undefined) {
                console.error("Post not found");
                return null;
            }

            const deleteRequest = store.delete(key);
            deleteRequest.onerror = e => { console.error((e.target as IDBRequest).error); return false };
            deleteRequest.onsuccess = () => true;
        }
    } catch (error) {
        console.error('Error in opening database:', error);
    }
}

export async function clearData(userID: string) {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);

        const clearRequest = store.clear();
        clearRequest.onsuccess = (e: Event) => {
            // console.log('Data has been added:', (e.target as IDBRequest).result);
        };

        clearRequest.onerror = (event: Event) => {
            // Use a type assertion to access the specific properties of IDBRequest error event
            const errorEvent = event as IDBRequestEvent;
            console.error('Error in clearing data:', errorEvent.target.error?.message);
        };

    } catch (error) {
        console.error('Error in opening database:', error);
    }
}

export async function addDataArray(userID: string, array: any[]): Promise<void> {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);

        let count = 0;

        array.reverse();

        for (let data of array) {
            const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
            addRequest.onsuccess = (e: Event) => {
                // console.log('Data has been added:', (e.target as IDBRequest).result);
            };

            addRequest.onerror = (event: Event) => {
                // Use a type assertion to access the specific properties of IDBRequest error event
                const errorEvent = event as IDBRequestEvent;
                console.error('Error in adding data:', errorEvent.target.error?.message);
            };

            count++;

            // if (count % 100 === 0) {
            //     console.log(`Added ${count} posts...`);
            // }
        }


    } catch (error) {
        console.error('Error in opening database:', error);
    }
}

export async function checkPostIds(userID: string, post_ids: string[]) {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);

        const index = store.index("postIDIndex");

        transaction.oncomplete = () => {
            // console.log("Transaction completed successfully");
            db.close();
        };

        transaction.onerror = (event) => {
            console.error("Transaction error:", (event.target as any).error);
            db.close();
        };

        let postIdsNeeded: any = [];

        for (let id of post_ids) {
            try {
                let havePost = await new Promise<boolean>((resolve, reject) => {
                    const getRequest = index.getKey(id);

                    getRequest.onerror = (e) => {
                        console.log((e.target as IDBRequest).error);
                        reject(e);
                    };

                    getRequest.onsuccess = async (e) => {
                        const key = (e.target as IDBRequest).result;
                        resolve(key !== undefined)
                    };

                });

                // console.log(post.post_id, havePost);
                if (!havePost) {
                    postIdsNeeded.push(id);
                }

            } catch (error) {
                console.error("Error processing post:", error);
            }
        }

        console.log(`checkPostIds need ${postIdsNeeded.length} posts`);
        return postIdsNeeded;
    } catch (error) {
        console.error("Error in opening database:", error);
    }
}

export async function mergeDataArray(userID: string, array: any[]): Promise<void> {
    try {
        const {db, transaction, store} = await getDBTransactionStore(userID);

        const index = store.index("postIDIndex");

        transaction.oncomplete = () => {
            // console.log("Transaction completed successfully");
            db.close();
        };

        transaction.onerror = (event) => {
            console.error("Transaction error:", (event.target as any).error);
            db.close();
        };

        let postsToWrite: any = [];

        for (let post of array) {
            try {
                let havePost = await new Promise<boolean>((resolve, reject) => {
                    const getRequest = index.getKey(post.post_id);

                    getRequest.onerror = (e) => {
                        console.log((e.target as IDBRequest).error);
                        reject(e);
                    };

                    getRequest.onsuccess = async (e) => {
                        const key = (e.target as IDBRequest).result;
                        resolve(key !== undefined)
                    };

                });

                // console.log(post.post_id, havePost);
                if (!havePost) {
                    postsToWrite.push(post);
                }

            } catch (error) {
                console.error("Error processing post:", error);
            }
        }

        console.log(`Writing ${postsToWrite.length} posts`);

        await addDataArray(userID, postsToWrite);
    } catch (error) {
        console.error("Error in opening database:", error);
    }
}

export async function getPostForUser(userID: string, postID: string): Promise<any | undefined> {
    
}


export async function getData(userID: string, lowerID: Date, upperID: Date): Promise<any | undefined> {
    const { store } = await getDBTransactionStore(userID);
    const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
    const index = store.index("datetimeIndex");

    return new Promise((resolve, reject) => {
        const getAllRequest = index.getAll(keyRangeValue);

        getAllRequest.onsuccess = () => {
            const records = getAllRequest.result.map((item: any) => item.data);
            resolve(records);
        };

        getAllRequest.onerror = () => {
            console.error('Transaction failed:', getAllRequest.error?.message);
            reject(getAllRequest.error);
        };
    });
}



export async function getAllData(userID: string): Promise<any | undefined> {
    const {store} = await getDBTransactionStore(userID);

    return new Promise((resolve, reject) => {
        const getRequest = store.getAll();

        getRequest.onsuccess = () => {
            if (getRequest.result) {
                //   console.log('Retrieved data:', getRequest.result.jsonData);
                // resolve(getRequest.result.jsonData as any);
                resolve(getRequest.result);
            } else {
                console.log('No data record found for key', key);
                resolve(undefined); // explicitly resolve with undefined when no data is found
            }
        };

        getRequest.onerror = (event: Event) => {
            // Use a type assertion to access the specific properties of IDBRequest error event
            const errorEvent = event as IDBRequestEvent;
            console.error('Transaction failed:', errorEvent.target.error?.message);
            reject(errorEvent.target.error); // reject the promise if there's an error
        };
    });
}

export async function getAllIds(userID: string): Promise<any | undefined> {
    const {store} = await getDBTransactionStore(userID);

    const index = store.index("postIDIndex");

    let keys: string[] = [];
    return new Promise((resolve, reject) => {
        let request = index.openKeyCursor();

        request.onsuccess = (event:any) => {
            let cursor = event.target.result;
            if (cursor) {
                keys.push(cursor.key);  
                cursor.continue();  
            } else {
                resolve(keys);
            }
        };

        request.onerror = (event: any) => {
            reject(event);
        };
    });
}

export async function getPostsByIds(userID:string, postIDs:string[]) {
    const {store} = await getDBTransactionStore(userID);
    const index = store.index("postIDIndex");
    let posts = [];

    for (const postID of postIDs) {
        const post = await new Promise((resolve, reject) => {
            let request = index.get(postID);

            request.onsuccess = (event:any) => {
                resolve(event.target.result);  // Resolve with the post
            };

            request.onerror = (event) => {
                reject(event);  // Reject if any error occurs
            };
        });

        if (post) {
            posts.push(post);  // Add the post to the result array if found
        }
    }

    return posts;  // Return the array of posts
}