import rndstr from "rndstr";
import { Notes, PromoNotes, PromoReads, Users } from "../../../../models/index.js";
import define from "../../define.js";
import { shouldHideEUsersFor } from "../../common/generate-minor-badge-visibility-query.js";
import { readPromo } from "../../common/read-promo.js";
const serviceTags = {
    video: "videoservice",
    audio: "audioservice",
    image: "imageservice",
    karaoke: "karaokeservice",
    lua4frozen: "lua4frozen"
};
const systemTags = new Set([
    "adservice",
    "videoservice",
    "audioservice",
    "imageservice",
    "karaokeservice",
    "lua4frozen"
]);
function isExplicitAd(note) {
    return note.tags.includes("explicit") || note.user?.minorBadges?.includes("E") === true;
}
function canShowExplicit(note, user, hideExplicit) {
    if (!isExplicitAd(note)) return true;
    if (hideExplicit) return false;
    return !(user && shouldHideEUsersFor(user));
}
function hasPollOr(note, fn) {
    return note.hasPoll || fn();
}
function hasFileType(note, prefix) {
    return note.attachedFileTypes.some((type)=>type.startsWith(prefix));
}
function matchesService(note, service) {
    if (service == null) return true;
    if (!note.tags.includes(serviceTags[service])) return false;
    switch(service){
        case "video":
            return hasPollOr(note, ()=>hasFileType(note, "video/"));
        case "audio":
            return hasPollOr(note, ()=>hasFileType(note, "audio/"));
        case "image":
            return hasPollOr(note, ()=>hasFileType(note, "image/"));
        case "karaoke":
            return hasFileType(note, "video/") || hasFileType(note, "audio/") && hasFileType(note, "image/");
        case "lua4frozen":
            return hasPollOr(note, ()=>hasFileType(note, "image/"));
    }
}
export const meta = {
    tags: [
        "notes"
    ],
    requireCredential: false,
    requireCredentialPrivateMode: true
};
export const paramDef = {
    type: "object",
    properties: {
        service: {
            type: "string",
            enum: [
                "video",
                "audio",
                "image",
                "karaoke",
                "lua4frozen"
            ],
            nullable: true
        },
        tag: {
            type: "string",
            nullable: true
        },
        hideExplicit: {
            type: "boolean",
            default: false
        }
    },
    required: []
};
export default define(meta, paramDef, async (ps, user)=>{
    const readDay = new Date().toISOString().slice(0, 10);
    const reads = user ? await PromoReads.findBy({
        userId: user.id,
        readDay
    }) : [];
    const readNoteIds = new Set(reads.map((read)=>read.noteId));
    const promos = (await PromoNotes.find()).filter((promo)=>promo.expiresAt.getTime() > Date.now() && promo.remainingCredits > 0 && !readNoteIds.has(promo.noteId));
    const candidates = [];
    for (const promo of promos){
        const note = await Notes.findOneBy({
            id: promo.noteId
        });
        if (!note?.tags.includes("adservice")) continue;
        note.user = await Users.findOneByOrFail({
            id: note.userId
        });
        if (!canShowExplicit(note, user, ps.hideExplicit)) continue;
        if (!matchesService(note, ps.service ?? null)) continue;
        candidates.push(note);
    }
    if (candidates.length === 0) return null;
    const requestedTag = ps.tag?.trim().toLowerCase().replace(/^#/, "");
    const priority = requestedTag && !systemTags.has(requestedTag) ? candidates.filter((note)=>note.tags.includes(requestedTag)) : [];
    const pool = priority.length > 0 ? priority : candidates;
    const note = pool[Math.floor(Math.random() * pool.length)];
    const promo = promos.find((promo)=>promo.noteId === note.id);
    note._prId_ = rndstr("a-z0-9", 8);
    if (user && promo) {
        await readPromo(note, promo, user);
    }
    return Notes.pack(note, user);
});
