import { Feed } from "feed";
import { In, IsNull } from "typeorm";
import config from "@/config/index.js";
import type { User } from "@/models/entities/user.js";
import { Notes, DriveFiles, UserProfiles, Users } from "@/models/index.js";

export default async function (user: User, threadDepth = 5, history = 20, noteintitle = false, renotes = true, replies = true) {
	const author = {
		link: `${config.url}/@${user.username}`,
		email: `${user.username}@${config.host}`,
		name: user.name || user.username
	};

	const profile = await UserProfiles.findOneByOrFail({ userId: user.id });

	const searchCriteria = {
		userId: user.id,
		visibility: In(['public', 'home']),
	};

	if (!renotes) {
		searchCriteria.renoteId = IsNull();
	}

	if (!replies) {
		searchCriteria.replyId = IsNull();
	}

	const notes = await Notes.find({
		where: searchCriteria,
		order: { createdAt: -1 },
		take: history,
	});

	const feed = new Feed({
		id: author.link,
		title: `${author.name} (@${user.username}@${config.host})`,
		updated: notes[0].createdAt,
		generator: 'Calckey',
		description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` ยท ${profile.description}` : ''}`,
		link: author.link,
		image: await Users.getAvatarUrl(user),
		feedLinks: {
			json: `${author.link}.json`,
			atom: `${author.link}.atom`,
		},
		author,
		copyright: user.name || user.username,
	});

	for (const note of notes) {
		let contentStr = await noteToString(note, true);
		let next = note.renoteId ? note.renoteId : note.replyId;
		let depth = threadDepth;
		while (depth > 0 && next) {
			const finding = await findById(next);
			contentStr += finding.text;
			next = finding.next;
			depth -= 1;
		}

		let title = `${author.name} `;
		if (note.renoteId) {
			title += 'renotes';
		} else if (note.replyId) {
			title += 'replies';
		} else {
			title += 'says';
		}
		if (noteintitle) {
			const content = note.cw ?? note.text;
			if (content) {
				title += `: ${content}`;
			} else {
				title += 'something';
			}
		}

		feed.addItem({
			title: title.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '').substring(0,100),
			link: `${config.url}/notes/${note.id}`,
			date: note.createdAt,
			description: note.cw ? note.cw.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '') : undefined,
			content: contentStr.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '')
		});
	}

	async function noteToString (note, isTheNote = false) {
		const author = isTheNote ? null : await Users.findOneBy({ id: note.userId });
		let outstr = author ? `${author.name}(@${author.username}@${author.host ? author.host : config.host}) ${(note.renoteId ? 'renotes' : (note.replyId ? 'replies' : 'says'))}: <br>` : '';
		const files = note.fileIds.length > 0 ? await DriveFiles.findBy({
			id: In(note.fileIds),
		}) : [];
		let fileEle = '';
		for (const file of files) {
			if (file.type.startsWith('image/')) {
				fileEle += ` <br><img src="${DriveFiles.getPublicUrl(file)}">`;
			} else if (file.type.startsWith('audio/')) {
				fileEle += ` <br><audio controls src="${DriveFiles.getPublicUrl(file)}" type="${file.type}">`;
			} else if (file.type.startsWith('video/')) {
				fileEle += ` <br><video controls src="${DriveFiles.getPublicUrl(file)}" type="${file.type}">`;
			} else {
				fileEle += ` <br><a href="${DriveFiles.getPublicUrl(file)}" download="${file.name}">${file.name}</a>`;
			}
		}
		outstr += `${note.cw ? note.cw + '<br>' : ''}${note.text || ''}${fileEle}`;
		if (isTheNote) {
			outstr += ` <span class="${(note.renoteId ? 'renote_note' : (note.replyId ? 'reply_note' : 'new_note'))} ${(fileEle.indexOf('img src') !== -1 ? 'with_img' : 'without_img')}"></span>`;
		}
		return outstr;
	}

	async function findById (id) {
		let text = '';
		let next = null;
		const findings = await Notes.findOneBy({ id: id, visibility: In(['public', 'home']) });
		if (findings) {
			text += `<hr>`;
			text += await noteToString(findings);
			next = findings.renoteId ? findings.renoteId : findings.replyId;
		}
		return { text, next };
	}

	return feed;
}