import { Request, Response } from 'express'
import { Types } from 'mongoose'
import helpers from '../../utils/helpers'
import { CommunityMember } from './models'
import { Follower } from '../followers/models'
import { IUser } from '../users/models'

/** Cursor format: hasPhoto_at_iso_userId. Used for stable cursor pagination. */
function parseCursor(cursorStr: string | undefined): { hasPhoto: number; at: Date; user: Types.ObjectId } | undefined {
	if (!cursorStr || typeof cursorStr !== 'string') return undefined
	const parts = cursorStr.split('_')
	if (parts.length < 3) return undefined
	const [hasPhotoStr, atStr, userIdStr] = parts
	const hasPhoto = +hasPhotoStr
	const at = new Date(atStr)
	if (isNaN(hasPhoto) || hasPhoto < 0 || hasPhoto > 1 || isNaN(at.getTime()) || !userIdStr) return undefined
	try {
		return { hasPhoto, at, user: new Types.ObjectId(userIdStr) }
	} catch {
		return undefined
	}
}

function buildCursor(hasPhoto: number, at: Date, userId: Types.ObjectId): string {
	return `${hasPhoto}_${at.toISOString()}_${userId}`
}

async function getCommunityMembers(req: Request, res: Response) {
	interface Payload {
		community: string
		limit?: string | number
		cursor?: string
	}

	await helpers.runApi(res, async () => {
		const user = res.locals.user as IUser
		let { community, limit = '20', cursor } = req.query as unknown as Payload

		const limitNum = Math.min(Math.max(+limit || 20, 1), 100)
		const communityId = new Types.ObjectId(community)

		// Users the current user already follows (so we exclude them), and exclude self
		const followedIds = await Follower.find({ follower: user._id })
			.select('following')
			.lean()
			.then((docs) => docs.map((d: any) => d.following))
		const excludedUserIds = [user._id, ...followedIds]

		const cursorData = parseCursor(Array.isArray(cursor) ? cursor[0] : cursor)

		const pipeline: any[] = [
			{ $match: { community: communityId } },
			{
				$lookup: {
					from: 'users',
					let: { memberUserId: '$user' },
					pipeline: [
						{ $match: { $expr: { $eq: ['$_id', '$$memberUserId'] } } },
						{
							$match: {
								$or: [
									{ terminated: false },
									{ terminated: { $exists: false } },
								],
							},
						},
						{ $project: { name: 1, username: 1, photo: 1, _id: 1 } },
					],
					as: 'userDoc',
				},
			},
			{ $unwind: { path: '$userDoc', preserveNullAndEmptyArrays: false } },
			{
				$match: {
					// Exclude self + already-followed users
					'userDoc._id': { $nin: excludedUserIds },
					// Require both name and username to be present and non-empty
					'userDoc.name': { $nin: [null, ''] },
					'userDoc.username': { $nin: [null, ''] },
				},
			},
			{
				$addFields: {
					// Treat missing / null / empty-string photo as no photo
					hasPhoto: {
						$cond: {
							if: {
								$ne: [{ $ifNull: ['$userDoc.photo', ''] }, ''],
							},
							then: 1,
							else: 0,
						},
					},
				},
			},
		]

		if (cursorData) {
			pipeline.push({
				$match: {
					$or: [
						{ hasPhoto: { $lt: cursorData.hasPhoto } },
						{
							hasPhoto: cursorData.hasPhoto,
							at: { $lt: cursorData.at },
						},
						{
							hasPhoto: cursorData.hasPhoto,
							at: cursorData.at,
							'userDoc._id': { $lt: cursorData.user },
						},
					],
				},
			})
		}

		pipeline.push(
			{ $sort: { hasPhoto: -1, at: -1, 'userDoc._id': -1 } },
			{ $limit: limitNum + 1 },
			{
				$project: {
					user: {
						_id: '$userDoc._id',
						name: '$userDoc.name',
						username: '$userDoc.username',
						photo: '$userDoc.photo',
					},
					at: 1,
					hasPhoto: 1,
				},
			}
		)

		const docs = await CommunityMember.aggregate(pipeline)

		const hasMore = docs.length > limitNum
		const items = hasMore ? docs.slice(0, limitNum) : docs
		const users = items.map((d: any) => d.user)

		const last = items[items.length - 1]
		const nextCursor =
			hasMore && last
				? buildCursor(last.hasPhoto, last.at, last.user._id)
				: undefined

		return res.json({
			users,
			...(nextCursor ? { nextCursor } : {}),
		})
	})
}

const controllers = {
	getCommunityMembers,
}

export default controllers
