import { kinds } from "nostr-tools";
import { getOrComputeCachedValue } from "./cache.js";
import { getHiddenTags, isHiddenTagsUnlocked, notifyEventUpdate, unlockHiddenTags, } from "./index.js";
import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, mergeAddressPointers, mergeEventPointers, } from "./pointers.js";
export const BookmarkPublicSymbol = Symbol.for("bookmark-public");
export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden");
/** Parses an array of tags into a {@link Bookmarks} object */
export function parseBookmarkTags(tags) {
    const notes = tags.filter((t) => t[0] === "e" && t[1]).map(getEventPointerFromETag);
    const articles = tags
        .filter((t) => t[0] === "a" && t[1])
        .map(getAddressPointerFromATag)
        .filter((addr) => addr.kind === kinds.LongFormArticle);
    const hashtags = tags.filter((t) => t[0] === "t" && t[1]).map((t) => t[1]);
    const urls = tags.filter((t) => t[0] === "r" && t[1]).map((t) => t[1]);
    return { notes, articles, hashtags, urls };
}
/** Merges any number of {@link Bookmarks} objects */
export function mergeBookmarks(...bookmarks) {
    const notes = new Map();
    const articles = new Map();
    const hashtags = new Set();
    const urls = new Set();
    for (const bookmark of bookmarks) {
        if (!bookmark)
            continue;
        for (const note of bookmark.notes) {
            const existing = notes.get(note.id);
            if (existing)
                notes.set(note.id, mergeEventPointers(existing, note));
            else
                notes.set(note.id, note);
        }
        for (const article of bookmark.articles) {
            const coord = getCoordinateFromAddressPointer(article);
            const existing = articles.get(coord);
            if (existing)
                articles.set(coord, mergeAddressPointers(existing, article));
            else
                articles.set(coord, article);
        }
        for (const hashtag of bookmark.hashtags)
            hashtags.add(hashtag);
        for (const url of bookmark.urls)
            urls.add(url);
    }
    return {
        notes: Array.from(notes.values()),
        articles: Array.from(articles.values()),
        hashtags: Array.from(hashtags),
        urls: Array.from(urls),
    };
}
/** Returns all the bookmarks of the event */
export function getBookmarks(bookmark) {
    const hidden = getHiddenBookmarks(bookmark);
    if (hidden)
        return mergeBookmarks(hidden, getPublicBookmarks(bookmark));
    else
        return getPublicBookmarks(bookmark);
}
/** Returns the public bookmarks of the event */
export function getPublicBookmarks(bookmark) {
    return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags));
}
/** Checks if the hidden bookmarks are unlocked */
export function isHiddenBookmarksUnlocked(bookmark) {
    return isHiddenTagsUnlocked(bookmark) && Reflect.has(bookmark, BookmarkHiddenSymbol);
}
export function getHiddenBookmarks(bookmark) {
    if (isHiddenBookmarksUnlocked(bookmark))
        return bookmark[BookmarkHiddenSymbol];
    //get hidden tags
    const tags = getHiddenTags(bookmark);
    if (!tags)
        return undefined;
    // parse bookmarks
    const bookmarks = parseBookmarkTags(tags);
    // set cached value
    Reflect.set(bookmark, BookmarkHiddenSymbol, bookmarks);
    return bookmarks;
}
/** Unlocks the hidden bookmarks on a bookmarks event */
export async function unlockHiddenBookmarks(bookmark, signer) {
    if (isHiddenBookmarksUnlocked(bookmark))
        return bookmark[BookmarkHiddenSymbol];
    // unlock hidden tags
    await unlockHiddenTags(bookmark, signer);
    // get hidden bookmarks
    const bookmarks = getHiddenBookmarks(bookmark);
    if (!bookmarks)
        throw new Error("Failed to unlock hidden bookmarks");
    // notify event store
    notifyEventUpdate(bookmark);
    return bookmarks;
}
