import { In, IsNull } from "typeorm";
import config from "../../../config/index.js";
import { DriveFiles, Notes, Users, Emojis, Polls } from "../../../models/index.js";
import toHtml from "../misc/get-note-html.js";
import renderEmoji from "./emoji.js";
import renderMention from "./mention.js";
import renderHashtag from "./hashtag.js";
import renderDocument from "./document.js";
import sanitizeHtml from "sanitize-html";
export default async function renderNote(note, dive = true, isTalk = false) {
    const getPromisedFiles = async (ids)=>{
        if (!ids || ids.length === 0) return [];
        const items = await DriveFiles.findBy({
            id: In(ids)
        });
        return ids.map((id)=>items.find((item)=>item.id === id)).filter((item)=>item != null);
    };
    let inReplyTo;
    let inReplyToNote;
    if (note.replyId) {
        inReplyToNote = await Notes.findOneBy({
            id: note.replyId
        });
        if (inReplyToNote != null) {
            const inReplyToUser = await Users.findOneBy({
                id: inReplyToNote.userId
            });
            if (inReplyToUser != null) {
                if (inReplyToNote.uri) {
                    inReplyTo = inReplyToNote.uri;
                } else {
                    if (dive) {
                        inReplyTo = await renderNote(inReplyToNote, false);
                    } else {
                        inReplyTo = `${config.url}/notes/${inReplyToNote.id}`;
                    }
                }
            }
        }
    } else {
        inReplyTo = null;
    }
    let quoteId;
    let quoteUrl;
    if (note.renoteId) {
        const renote = await Notes.findOneBy({
            id: note.renoteId
        });
        if (renote) {
            if (renote.userHost) {
                quoteId = renote.uri;
                quoteUrl = renote.url ?? renote.uri;
            } else {
                quoteId = quoteUrl = `${config.url}/notes/${renote.id}`;
            }
        }
    }
    const attributedTo = `${config.url}/users/${note.userId}`;
    const mentions = JSON.parse(note.mentionedRemoteUsers).map((x)=>x.uri);
    let to = [];
    let cc = [];
    if (note.visibility === "public") {
        to = [
            "https://www.w3.org/ns/activitystreams#Public"
        ];
        cc = [
            `${attributedTo}/followers`
        ].concat(mentions);
    } else if (note.visibility === "home") {
        to = [
            `${attributedTo}/followers`
        ];
        cc = [
            "https://www.w3.org/ns/activitystreams#Public"
        ].concat(mentions);
    } else if (note.visibility === "followers") {
        to = [
            `${attributedTo}/followers`
        ];
        cc = mentions;
    } else {
        to = mentions;
    }
    const mentionedUsers = note.mentions.length > 0 ? await Users.findBy({
        id: In(note.mentions)
    }) : [];
    const hashtagTags = (note.tags || []).map((tag)=>renderHashtag(tag));
    const mentionTags = mentionedUsers.map((u)=>renderMention(u));
    const files = await getPromisedFiles(note.fileIds);
    const text = note.text ?? "";
    let poll = null;
    if (note.hasPoll) {
        poll = await Polls.findOneBy({
            noteId: note.id
        });
    }
    const summary = note.cw === "" ? String.fromCharCode(0x200b) : note.cw;
    let content = await toHtml(Object.assign({}, note, {
        text
    }));
    if (quoteId) {
        // wrapping in p.quote-inline lets mastodon automatically strip the link
        const quoteHREFSan = (quoteUrl || quoteId).replaceAll("&", "&amp;").replaceAll('"', "&quot;");
        const quoteTextSan = sanitizeHtml(quoteUrl || quoteId);
        content += `<p class="quote-inline">RE: <a href="${quoteHREFSan}">${quoteTextSan}</p>`;
    }
    const emojis = await getEmojis(note.emojis);
    const apemojis = emojis.map((emoji)=>renderEmoji(emoji));
    const tag = [
        ...hashtagTags,
        ...mentionTags,
        ...apemojis
    ];
    const asPoll = poll ? {
        type: "Question",
        content: await toHtml(Object.assign({}, note, {
            text: text
        })),
        [poll.expiresAt && poll.expiresAt < new Date() ? "closed" : "endTime"]: poll.expiresAt,
        [poll.multiple ? "anyOf" : "oneOf"]: poll.choices.map((text, i)=>({
                type: "Note",
                name: text,
                replies: {
                    type: "Collection",
                    totalItems: poll.votes[i]
                }
            }))
    } : {};
    const asTalk = isTalk ? {
        _misskey_talk: true
    } : {};
    return {
        id: `${config.url}/notes/${note.id}`,
        type: "Note",
        attributedTo,
        summary,
        content,
        _misskey_content: text,
        source: {
            content: text,
            mediaType: "text/x.misskeymarkdown"
        },
        _misskey_quote: quoteId,
        quoteUri: quoteId,
        quoteUrl: quoteId,
        quote: note.canQuote ? quoteId : undefined,
        published: note.createdAt.toISOString(),
        to,
        cc,
        inReplyTo,
        attachment: files.map(renderDocument),
        sensitive: note.cw != null || files.some((file)=>file.isSensitive),
        tag,
        interactionPolicy: note.visibility === "public" || note.visibility === "home" ? {
            canQuote: {
                automaticApproval: "https://www.w3.org/ns/activitystreams#Public"
            }
        } : undefined,
        quoteAuthorization: note.quoteAuthorization || undefined,
        ...asPoll,
        ...asTalk
    };
}
export async function getEmojis(names) {
    if (names == null || names.length === 0) return [];
    const emojis = await Promise.all(names.map((name)=>{
        const parts = name.split("@");
        return Emojis.findOneBy({
            name: parts[0],
            host: parts.length === 2 ? parts[1] : IsNull()
        });
    }));
    return emojis.filter((emoji)=>emoji != null);
}
