import { useEffect, useState } from 'react';
import { useMutation, useQuery } from 'convex/react';
import { Avatar, Button, Modal } from '@discord-clone/ui';
import { api } from '../../../../../convex/_generated/api';
import type { Id } from '../../../../../convex/_generated/dataModel';
import { useIsMobile } from '../../hooks/useIsMobile';
import { useOnlineUsers } from '../../contexts/PresenceContext';
import { MobileMemberProfileSheet } from './MobileMemberProfileSheet';
import { MemberProfilePopout } from './MemberProfilePopout';
import { MemberProfileModal } from './MemberProfileModal';
import { MemberContextMenu } from './MemberContextMenu';
import { ManageRolesPopout } from './ManageRolesPopout';
import styles from './MemberListContainer.module.css';

interface MemberListContainerProps {
	members?: unknown[];
	channelId: string;
	serverId?: string;
	variant?: 'panel' | 'drawer';
}

interface MemberRow {
	userId: string;
	displayName: string;
	avatarUrl: string | null;
	status: string;
	roleColor: string | null;
}

const DEFAULT_ROLE_COLOR = '#99aab5';

/** Highest-position role that has a non-default colour. Matches the
 *  server-side enrichMessage helper so chat + member list pick the
 *  same tint for the same user. Owner is deliberately skipped so
 *  owners fall through to the next coloured role instead of showing
 *  the bootstrap pink on every server. */
function pickRoleColor(roles: any[] | undefined): string | null {
	if (!Array.isArray(roles)) return null;
	let best: { position: number; color: string } | null = null;
	for (const r of roles) {
		if (r?.name === 'Owner') continue;
		const color = r?.color as string | undefined;
		const position = (r?.position ?? 0) as number;
		if (!color || color.toLowerCase() === DEFAULT_ROLE_COLOR) continue;
		if (!best || position > best.position) best = { position, color };
	}
	return best?.color ?? null;
}

interface PopoutState {
	anchorRect: DOMRect;
	member: MemberRow;
}

export function MemberListContainer({ channelId, variant = 'panel' }: MemberListContainerProps) {
	const result = useQuery(
		api.members.getChannelMembers,
		channelId ? { channelId: channelId as any } : 'skip',
	);
	const { resolveStatus } = useOnlineUsers();
	const isMobile = useIsMobile();

	const [popout, setPopout] = useState<PopoutState | null>(null);
	const [mobileProfileFor, setMobileProfileFor] = useState<MemberRow | null>(null);
	// Right-click context menu position + target member.
	const [contextMenu, setContextMenu] = useState<
		{ x: number; y: number; member: MemberRow } | null
	>(null);
	// Big profile modal opened from the context menu's "View Profile".
	const [profileModalFor, setProfileModalFor] = useState<MemberRow | null>(null);
	const [manageRolesFor, setManageRolesFor] = useState<MemberRow | null>(null);
	// Inline nickname editor — replaces the previous window.prompt
	// alert with a real Modal + input + Save/Cancel buttons.
	const [nicknameTarget, setNicknameTarget] = useState<MemberRow | null>(null);
	const [nicknameDraft, setNicknameDraft] = useState('');
	const [nicknameSaving, setNicknameSaving] = useState(false);
	const [nicknameError, setNicknameError] = useState<string | null>(null);

	// Resolve each member's status against the live presence heartbeat
	// set. A user with stored status `online` but no heartbeat gets
	// shown as offline; an invisible user also collapses to offline.
	const members: MemberRow[] = Array.isArray(result)
		? result.map((m: any) => {
				const storedStatus = m.status || 'offline';
				const userId = m.userId || m.id;
				return {
					userId,
					displayName: m.displayName || m.username || 'User',
					avatarUrl: m.avatarUrl ?? null,
					status: resolveStatus(storedStatus, userId),
					roleColor: pickRoleColor(m.roles),
				};
			})
		: [];

	const online = members.filter((m) => m.status !== 'offline' && m.status !== 'invisible');
	const offline = members.filter((m) => m.status === 'offline' || m.status === 'invisible');

	const containerClass = variant === 'drawer' ? styles.containerDrawer : styles.container;

	const handleOpenPopout = (member: MemberRow, rect: DOMRect) => {
		// On mobile we open the full-screen profile modal instead of
		// the anchored desktop popout so touch layouts get the same
		// surface they already know from the DM header path.
		if (isMobile) {
			setMobileProfileFor(member);
			return;
		}
		setPopout({ anchorRect: rect, member });
	};

	const handleOpenSettings = () => {
		window.dispatchEvent(new CustomEvent('brycord:open-user-settings'));
	};

	const handleMemberContextMenu = (
		member: MemberRow,
		e: React.MouseEvent,
	) => {
		e.preventDefault();
		e.stopPropagation();
		setContextMenu({ x: e.clientX, y: e.clientY, member });
	};

	const setNickname = useMutation(api.auth.setNickname);

	const openNicknameEditor = (member: MemberRow) => {
		setNicknameTarget(member);
		setNicknameDraft(member.displayName);
		setNicknameError(null);
		setNicknameSaving(false);
		setContextMenu(null);
	};

	// Reset the draft whenever a different target is loaded.
	useEffect(() => {
		if (nicknameTarget) {
			setNicknameDraft(nicknameTarget.displayName);
			setNicknameError(null);
		}
	}, [nicknameTarget?.userId]);

	const handleSaveNickname = async () => {
		if (!nicknameTarget || nicknameSaving) return;
		const localUserId =
			typeof localStorage !== 'undefined'
				? localStorage.getItem('userId')
				: null;
		if (!localUserId) return;
		setNicknameSaving(true);
		setNicknameError(null);
		try {
			await setNickname({
				actorUserId: localUserId as any,
				targetUserId: nicknameTarget.userId as any,
				displayName: nicknameDraft.trim(),
			});
			setNicknameTarget(null);
		} catch (err: any) {
			setNicknameError(err?.message ?? 'Failed to set nickname.');
		} finally {
			setNicknameSaving(false);
		}
	};

	const handleMentionMember = (member: MemberRow) => {
		// Broadcast an insert-mention event — ChannelTextarea listens for
		// this and pastes `@username` at the caret. Falling back to a
		// silent no-op if no composer is mounted is fine.
		window.dispatchEvent(
			new CustomEvent('brycord:mention-user', {
				detail: { userId: member.userId, displayName: member.displayName },
			}),
		);
		setContextMenu(null);
	};

	return (
		<div className={containerClass}>
			{online.length > 0 && (
				<MemberSection
					label="Online"
					count={online.length}
					members={online}
					onMemberClick={handleOpenPopout}
					onMemberContextMenu={handleMemberContextMenu}
				/>
			)}
			{offline.length > 0 && (
				<MemberSection
					label="Offline"
					count={offline.length}
					members={offline}
					muted
					onMemberClick={handleOpenPopout}
					onMemberContextMenu={handleMemberContextMenu}
				/>
			)}

			{popout && (
				<MemberProfilePopout
					anchorRect={popout.anchorRect}
					member={popout.member}
					onClose={() => setPopout(null)}
					onOpenProfile={handleOpenSettings}
					onOpenRoles={() => setManageRolesFor(popout.member)}
				/>
			)}

			<MobileMemberProfileSheet
				isOpen={!!mobileProfileFor}
				onClose={() => setMobileProfileFor(null)}
				member={mobileProfileFor ? { userId: mobileProfileFor.userId } : null}
			/>

			<MemberProfileModal
				isOpen={!!profileModalFor}
				onClose={() => setProfileModalFor(null)}
				member={profileModalFor ? { userId: profileModalFor.userId } : null}
			/>

			{contextMenu && (
				<MemberContextMenu
					x={contextMenu.x}
					y={contextMenu.y}
					onViewProfile={() => {
						setProfileModalFor(contextMenu.member);
						setContextMenu(null);
					}}
					onMention={() => handleMentionMember(contextMenu.member)}
					onChangeNickname={() => openNicknameEditor(contextMenu.member)}
					onManageRoles={() => {
						setManageRolesFor(contextMenu.member);
						setContextMenu(null);
					}}
					onClose={() => setContextMenu(null)}
				/>
			)}

			<ManageRolesPopout
				isOpen={!!manageRolesFor}
				onClose={() => setManageRolesFor(null)}
				member={
					manageRolesFor
						? {
								userId: manageRolesFor.userId as Id<'userProfiles'>,
								displayName: manageRolesFor.displayName,
							}
						: null
				}
			/>

			<Modal.Root
				isOpen={!!nicknameTarget}
				onClose={() => {
					if (nicknameSaving) return;
					setNicknameTarget(null);
				}}
				size="small"
			>
				<Modal.Header
					title={`Change Nickname${nicknameTarget ? ` for ${nicknameTarget.displayName}` : ''}`}
					onClose={() => {
						if (nicknameSaving) return;
						setNicknameTarget(null);
					}}
				/>
				<Modal.Content>
					<div
						style={{
							display: 'flex',
							flexDirection: 'column',
							gap: 10,
							padding: '4px 4px 8px',
						}}
					>
						<label
							htmlFor="nickname-input"
							style={{
								fontSize: 12,
								fontWeight: 700,
								letterSpacing: '0.04em',
								textTransform: 'uppercase',
								color: 'var(--text-tertiary)',
							}}
						>
							Nickname
						</label>
						<input
							id="nickname-input"
							type="text"
							value={nicknameDraft}
							onChange={(e) => setNicknameDraft(e.target.value)}
							onKeyDown={(e) => {
								if (e.key === 'Enter') {
									e.preventDefault();
									void handleSaveNickname();
								}
							}}
							placeholder="Enter a nickname"
							autoFocus
							maxLength={64}
							disabled={nicknameSaving}
							style={{
								padding: '10px 12px',
								borderRadius: 6,
								border: '1px solid var(--background-modifier-accent)',
								background: 'var(--background-tertiary)',
								color: 'var(--text-primary)',
								font: 'inherit',
								fontSize: 14,
								outline: 'none',
							}}
						/>
						<div style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>
							Leave empty to clear the nickname and fall back to the
							user's original display name.
						</div>
						{nicknameError && (
							<div
								style={{
									fontSize: 12,
									color: 'var(--status-danger, #ed4245)',
								}}
							>
								{nicknameError}
							</div>
						)}
					</div>
				</Modal.Content>
				<Modal.Footer>
					<Button
						variant="secondary"
						size="sm"
						onClick={() => setNicknameTarget(null)}
						disabled={nicknameSaving}
					>
						Cancel
					</Button>
					<Button
						variant="primary"
						size="sm"
						loading={nicknameSaving}
						onClick={handleSaveNickname}
					>
						Save
					</Button>
				</Modal.Footer>
			</Modal.Root>
		</div>
	);
}

function MemberSection({
	label,
	count,
	members,
	muted,
	onMemberClick,
	onMemberContextMenu,
}: {
	label: string;
	count: number;
	members: MemberRow[];
	muted?: boolean;
	onMemberClick: (member: MemberRow, rect: DOMRect) => void;
	onMemberContextMenu?: (member: MemberRow, e: React.MouseEvent) => void;
}) {
	return (
		<div className={styles.section}>
			<div className={styles.sectionHeader}>
				<span className={styles.sectionLabel}>{label}</span>
				<span className={styles.sectionCount}>{count}</span>
			</div>
			<div className={styles.membersList}>
				{members.map((m) => (
					<button
						key={m.userId}
						type="button"
						className={`${styles.memberItem} ${muted ? styles.memberItemOffline : ''}`}
						onClick={(e) => {
							const rect = e.currentTarget.getBoundingClientRect();
							onMemberClick(m, rect);
						}}
						onContextMenu={(e) => onMemberContextMenu?.(m, e)}
					>
						<div className={styles.memberGrid}>
							<div className={styles.memberContent}>
								<div className={styles.memberAvatar}>
									<Avatar
										src={m.avatarUrl}
										size={32}
										fallback={m.displayName}
										status={m.status as any}
									/>
								</div>
								<div className={styles.memberInfo}>
									<span
										className={styles.memberName}
										style={{ color: m.roleColor ?? undefined }}
									>
										{m.displayName}
									</span>
								</div>
							</div>
						</div>
					</button>
				))}
			</div>
		</div>
	);
}
