import { kinds } from "nostr-tools";
import { GROUPS_LIST_KIND } from "./groups.js";
import { canHaveHiddenContent, getHiddenContent, getHiddenContentEncryptionMethods, hasHiddenContent, isHiddenContentUnlocked, lockHiddenContent, setHiddenContentCache, setHiddenContentEncryptionMethod, unlockHiddenContent, } from "./hidden-content.js";
/** Symbol for caching hidden tags. */
export const HiddenTagsSymbol = Symbol.for("hidden-tags");
/** Various event kinds that can have hidden tags */
export const HiddenTagsKinds = new Set([
    // NIP-51 lists
    setHiddenContentEncryptionMethod(kinds.BookmarkList, "nip04"),
    setHiddenContentEncryptionMethod(kinds.InterestsList, "nip04"),
    setHiddenContentEncryptionMethod(kinds.Mutelist, "nip04"),
    setHiddenContentEncryptionMethod(kinds.CommunitiesList, "nip04"),
    setHiddenContentEncryptionMethod(kinds.PublicChatsList, "nip04"),
    setHiddenContentEncryptionMethod(kinds.SearchRelaysList, "nip04"),
    setHiddenContentEncryptionMethod(GROUPS_LIST_KIND, "nip04"),
    // NIP-51 sets
    setHiddenContentEncryptionMethod(kinds.Bookmarksets, "nip04"),
    setHiddenContentEncryptionMethod(kinds.Relaysets, "nip04"),
    setHiddenContentEncryptionMethod(kinds.Followsets, "nip04"),
    setHiddenContentEncryptionMethod(kinds.Curationsets, "nip04"),
    setHiddenContentEncryptionMethod(kinds.Interestsets, "nip04"),
]);
/** Checks if an event can have hidden tags */
export function canHaveHiddenTags(kind) {
    return canHaveHiddenContent(kind) && HiddenTagsKinds.has(kind);
}
/** Sets the type of encryption to use for hidden tags on a kind */
export function setHiddenTagsEncryptionMethod(kind, method) {
    HiddenTagsKinds.add(setHiddenContentEncryptionMethod(kind, method));
    return kind;
}
/** Checks if an event has hidden tags */
export function hasHiddenTags(event) {
    return canHaveHiddenTags(event.kind) && hasHiddenContent(event);
}
/** Returns either nip04 or nip44 encryption method depending on list kind */
export function getHiddenTagsEncryptionMethods(kind, signer) {
    return getHiddenContentEncryptionMethods(kind, signer);
}
/** Checks if the hidden tags are locked and casts it to the {@link UnlockedHiddenTags} type */
export function isHiddenTagsUnlocked(event) {
    if (!canHaveHiddenTags(event.kind))
        return false;
    return isHiddenContentUnlocked(event) && Reflect.has(event, `HiddenTagsSymbol`);
}
export function getHiddenTags(event) {
    if (!canHaveHiddenTags(event.kind))
        return undefined;
    // If the hidden tags are already unlocked, return the cached value
    if (isHiddenTagsUnlocked(event))
        return event[HiddenTagsSymbol];
    // unlock hidden content is needed
    const content = getHiddenContent(event);
    // Return undefined if the hidden content is not unlocked
    if (content === undefined)
        return undefined;
    // Parse the hidden content as an array of tags
    const parsed = JSON.parse(content);
    // Throw error if content is not an array of tags
    if (!Array.isArray(parsed))
        throw new Error("Content is not an array of tags");
    // Convert array to tags array string[][]
    const tags = parsed.filter((t) => Array.isArray(t)).map((t) => t.map((v) => String(v)));
    // Set the cached value
    Reflect.set(event, HiddenTagsSymbol, tags);
    return tags;
}
/**
 * Decrypts the private list
 * @param event The list event to decrypt
 * @param signer A signer to use to decrypt the tags
 * @param override The encryption method to use instead of the default
 * @throws
 */
export async function unlockHiddenTags(event, signer, override) {
    if (!canHaveHiddenTags(event.kind))
        throw new Error("Event kind does not support hidden tags");
    // Return the cached value if the hidden tags are already unlocked
    if (isHiddenTagsUnlocked(event))
        return event[HiddenTagsSymbol];
    // Unlock hidden content
    await unlockHiddenContent(event, signer, override);
    // Parse the hidden tags
    const tags = getHiddenTags(event);
    if (tags === undefined)
        throw new Error("Failed to unlock hidden tags");
    // Set cache an notify event store
    setHiddenTagsCache(event, tags);
    return tags;
}
/**
 * Sets the hidden tags on an event and updates it if its part of an event store
 * @throws If the event kind does not support hidden tags
 */
export function setHiddenTagsCache(event, tags) {
    if (!canHaveHiddenTags(event.kind))
        throw new Error("Event kind does not support hidden tags");
    // Set the cached value
    Reflect.set(event, HiddenTagsSymbol, tags);
    // Set the cached content
    setHiddenContentCache(event, JSON.stringify(tags));
}
/** Clears the cached hidden tags on an event */
export function lockHiddenTags(event) {
    Reflect.deleteProperty(event, HiddenTagsSymbol);
    lockHiddenContent(event);
}
