import { Brackets } from "typeorm";
import { Followings, Users } from "@/models/index.js";
import { USER_ACTIVE_THRESHOLD } from "@/const.js";
import type { User } from "@/models/entities/user.js";
import define from "../../define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";

export const meta = {
	tags: ["users"],

	requireCredential: false,
	requireCredentialPrivateMode: true,

	description: "Search for a user by username and/or host.",

	res: {
		type: "array",
		optional: false,
		nullable: false,
		items: {
			type: "object",
			optional: false,
			nullable: false,
			ref: "User",
		},
	},
} as const;

export const paramDef = {
	type: "object",
	properties: {
		username: { type: "string", nullable: true },
		host: { type: "string", nullable: true },
		limit: { type: "integer", minimum: 1, maximum: 100, default: 10 },
		maxDaysSinceLastActive: {
			type: "integer",
			minimum: 1,
			maximum: 1000,
			nullable: true
		},
		detail: { type: "boolean", default: true },
	},
	anyOf: [{ required: ["username"] }, { required: ["host"] }],
} as const;

// TODO: avatar,bannerをJOINしたいけどエラーになる

export default define(meta, paramDef, async (ps, me) => {
	const activeThreshold = ps.maxDaysSinceLastActive
		? new Date(Date.now() - 1000 * 60 * 60 * 24 * ps.maxDaysSinceLastActive)
		: null;

	if (ps.host) {
		const q = Users.createQueryBuilder("user")
			.where("user.isSuspended = FALSE")
			.andWhere("user.host LIKE :host", {
				host: `${sqlLikeEscape(ps.host.toLowerCase())}%`,
			});

		if (ps.username) {
			q.andWhere("user.usernameLower LIKE :username", {
				username: `${sqlLikeEscape(ps.username.toLowerCase())}%`,
			});
		}

		q.andWhere("user.updatedAt IS NOT NULL");
		q.orderBy("user.updatedAt", "DESC");

		const users = await q.take(ps.limit).getMany();

		return await Users.packMany(users, me, { detail: ps.detail });
	} else if (ps.username) {
		let users: User[] = [];

		if (me) {
			const followingQuery = Followings.createQueryBuilder("following")
				.select("following.followeeId")
				.where("following.followerId = :followerId", { followerId: me.id });

			const query = Users.createQueryBuilder("user")
				.where(`user.id IN (${followingQuery.getQuery()})`)
				.andWhere("user.isSuspended = FALSE")
				.andWhere("user.usernameLower LIKE :username", {
					username: `${sqlLikeEscape(ps.username.toLowerCase())}%`,
				});

			if (activeThreshold) {
				query.andWhere(
					new Brackets((qb) => {
						qb.where("user.updatedAt IS NULL").orWhere(
							"user.updatedAt > :activeThreshold",
							{ activeThreshold: activeThreshold },
						);
					}),
				);
			}

			query.setParameters(followingQuery.getParameters());

			users = await query
				.orderBy("user.usernameLower", "ASC")
				.take(ps.limit)
				.getMany();

			if (users.length < ps.limit) {
				const otherQuery = await Users.createQueryBuilder("user")
					.where(`user.id NOT IN (${followingQuery.getQuery()})`)
					.andWhere("user.isSuspended = FALSE")
					.andWhere("user.usernameLower LIKE :username", {
						username: `${sqlLikeEscape(ps.username.toLowerCase())}%`,
					})
					.andWhere("user.updatedAt IS NOT NULL");

				otherQuery.setParameters(followingQuery.getParameters());

				const otherUsers = await otherQuery
					.orderBy("user.updatedAt", "DESC")
					.take(ps.limit - users.length)
					.getMany();

				users = users.concat(otherUsers);
			}
		} else {
			users = await Users.createQueryBuilder("user")
				.where("user.isSuspended = FALSE")
				.andWhere("user.usernameLower LIKE :username", {
					username: `${sqlLikeEscape(ps.username.toLowerCase())}%`,
				})
				.andWhere("user.updatedAt IS NOT NULL")
				.orderBy("user.updatedAt", "DESC")
				.take(ps.limit - users.length)
				.getMany();
		}

		return await Users.packMany(users, me, { detail: !!ps.detail });
	}

	return [];
});
