/**
 * ManageRolesPopout — opened from a member profile popout's "Manage
 * Roles" button. Shows a checklist of every role in the server with
 * the target member's current assignments pre-checked. The user
 * toggles rows then clicks Save to commit — the save handler diffs
 * the pending set against the previously-assigned set and fires
 * `api.roles.assign` / `api.roles.unassign` mutations for each
 * added / removed role.
 *
 * `@everyone` is not a real row in the Convex `roles` table, so
 * nothing extra needs filtering here.
 */
import { useEffect, useMemo, useState } from 'react';
import { useMutation, useQuery } from 'convex/react';
import { Check } from '@phosphor-icons/react';
import { Modal, Button } from '@discord-clone/ui';
import { api } from '../../../../../convex/_generated/api';
import type { Id } from '../../../../../convex/_generated/dataModel';
import styles from './ManageRolesPopout.module.css';

interface ManageRolesPopoutMember {
	userId: Id<'userProfiles'>;
	displayName: string;
}

interface RoleDoc {
	_id: Id<'roles'>;
	name: string;
	color?: string;
	position?: number;
}

interface ManageRolesPopoutProps {
	isOpen: boolean;
	onClose: () => void;
	member: ManageRolesPopoutMember | null;
}

export function ManageRolesPopout({
	isOpen,
	onClose,
	member,
}: ManageRolesPopoutProps) {
	// Owner is non-assignable — filter it out of the selectable list
	// so it can't be checked, and the backend mutations refuse it
	// anyway if somebody tries to craft a request manually.
	const allRoles = (
		(useQuery(api.roles.list, {}) ?? []) as RoleDoc[]
	).filter((r) => r.name !== 'Owner');
	const listMembers = useQuery(api.roles.listMembers, {}) as
		| Array<{ id: Id<'userProfiles'>; roles: Array<{ _id: Id<'roles'> }> }>
		| undefined;

	const assignMutation = useMutation(api.roles.assign);
	const unassignMutation = useMutation(api.roles.unassign);

	// The set of role IDs currently assigned on the server (source of
	// truth), and the user's pending selection (draft). Reset whenever
	// the popout is opened for a new member.
	const currentAssignedIds = useMemo<Set<string>>(() => {
		if (!member || !listMembers) return new Set();
		const row = listMembers.find((r) => r.id === member.userId);
		if (!row) return new Set();
		return new Set(row.roles.map((r) => r._id as unknown as string));
	}, [listMembers, member]);

	const [pendingIds, setPendingIds] = useState<Set<string>>(new Set());
	const [saving, setSaving] = useState(false);
	const [error, setError] = useState<string | null>(null);

	useEffect(() => {
		if (isOpen) {
			setPendingIds(new Set(currentAssignedIds));
			setError(null);
		}
	}, [isOpen, currentAssignedIds]);

	if (!member) return null;

	// Show roles highest-position first so the editor reads like the
	// rest of the role UI (sidebar hierarchy).
	const sortedRoles = [...allRoles].sort(
		(a, b) => (b.position || 0) - (a.position || 0),
	);

	const togglePending = (roleId: string) => {
		if (saving) return;
		setPendingIds((prev) => {
			const next = new Set(prev);
			if (next.has(roleId)) next.delete(roleId);
			else next.add(roleId);
			return next;
		});
	};

	const handleSave = async () => {
		if (saving) return;
		setSaving(true);
		setError(null);

		// Diff: every role that's checked now but wasn't before → assign;
		// every role that was checked before but isn't now → unassign.
		const toAssign: string[] = [];
		const toRemove: string[] = [];
		for (const id of pendingIds) {
			if (!currentAssignedIds.has(id)) toAssign.push(id);
		}
		for (const id of currentAssignedIds) {
			if (!pendingIds.has(id)) toRemove.push(id);
		}

		try {
			await Promise.all([
				...toAssign.map((roleId) =>
					assignMutation({
						roleId: roleId as Id<'roles'>,
						userId: member.userId,
					}),
				),
				...toRemove.map((roleId) =>
					unassignMutation({
						roleId: roleId as Id<'roles'>,
						userId: member.userId,
					}),
				),
			]);
			onClose();
		} catch (err: any) {
			setError(err?.message || 'Failed to update roles.');
		} finally {
			setSaving(false);
		}
	};

	return (
		<Modal.Root isOpen={isOpen} onClose={onClose} size="small">
			<Modal.Header title="Manage Roles" onClose={onClose} />
			<Modal.Content className={styles.content}>
				<div className={styles.subtitle}>
					Editing roles for <strong>{member.displayName}</strong>
				</div>
				{sortedRoles.length === 0 ? (
					<div className={styles.empty}>
						No roles have been created in this server yet. Open Server
						Settings → Roles to create one.
					</div>
				) : (
					<div className={styles.list}>
						{sortedRoles.map((role) => {
							const roleIdStr = role._id as unknown as string;
							const assigned = pendingIds.has(roleIdStr);
							return (
								<button
									key={roleIdStr}
									type="button"
									className={`${styles.row} ${assigned ? styles.rowChecked : ''}`}
									onClick={() => togglePending(roleIdStr)}
									disabled={saving}
								>
									<span
										className={styles.roleDot}
										style={{
											backgroundColor:
												role.color ||
												'var(--text-primary-muted, #a0a3a8)',
										}}
									/>
									<span className={styles.roleName}>{role.name}</span>
									<span className={styles.check}>
										{assigned && <Check size={14} weight="bold" />}
									</span>
								</button>
							);
						})}
					</div>
				)}
				{error && <div className={styles.error}>{error}</div>}
			</Modal.Content>
			<Modal.Footer>
				<Button variant="secondary" onClick={onClose} disabled={saving}>
					Cancel
				</Button>
				<Button variant="primary" onClick={handleSave} disabled={saving}>
					{saving ? 'Saving…' : 'Save'}
				</Button>
			</Modal.Footer>
		</Modal.Root>
	);
}
