import {
	ArrowLeft,
	CaretRight,
	Funnel,
	Hash,
	MagnifyingGlass,
	Phone,
	Plus,
	PushPin,
	SpeakerHigh,
	Users,
	VideoCamera,
	X,
} from '@phosphor-icons/react';
import { Avatar, Tooltip } from '@discord-clone/ui';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useMutation, useQuery } from 'convex/react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useIsMobile } from '../../hooks/useIsMobile';
import { usePlatform } from '../../platform';
import { useOnlineUsers } from '../../contexts/PresenceContext';
import { api } from '../../../../../convex/_generated/api';
import { MemberProfileModal } from '../member/MemberProfileModal';
import { MobileMemberProfileSheet } from '../member/MobileMemberProfileSheet';
import { useKeybinds } from '../../contexts/KeybindContext';
import { ChannelHeaderPinsPopover } from './ChannelHeaderPinsPopover';
import styles from './ChannelHeader.module.css';

const SEARCH_FILTERS = [
	{ key: 'from:', desc: 'user' },
	{ key: 'mentions:', desc: 'user' },
	{ key: 'has:', desc: 'link, embed or file' },
	{ key: 'before:', desc: 'specific date' },
	{ key: 'during:', desc: 'specific date' },
	{ key: 'after:', desc: 'specific date' },
	{ key: 'pinned:', desc: 'true or false' },
];

interface ChannelLike {
	_id?: string;
	name?: string;
	type?: string;
	topic?: string;
}

interface ChannelHeaderProps {
	channel?: ChannelLike | null;
	serverId?: string;
	onOpenChannelDetails?: () => void;
	onOpenSearchDrawer?: () => void;
	membersVisible?: boolean;
	onToggleMembers?: () => void;
	/** Hide the members button entirely. DMs use this since a 1:1
	 *  conversation doesn't need a sidebar member list. */
	hideMembersButton?: boolean;
	/** When true, the viewport is below the members-list breakpoint. The
	 *  Users button becomes a no-op and gets disabled styling. */
	isNarrow?: boolean;
	/** Controlled search input. State lives in ChannelView so the sibling
	 *  SearchPanel can read the same query. */
	searchQuery?: string;
	onSearchChange?: (next: string) => void;
	onSearchClear?: () => void;
}

export function ChannelHeader({
	channel,
	onOpenChannelDetails,
	membersVisible = true,
	onToggleMembers,
	hideMembersButton = false,
	isNarrow = false,
	searchQuery = '',
	onSearchChange,
	onSearchClear,
}: ChannelHeaderProps) {
	const isVoice = channel?.type === 'voice';
	const isDM = channel?.type === 'dm';

	const isMobile = useIsMobile();
	const navigate = useNavigate();
	const location = useLocation();
	const { crypto } = usePlatform();
	const { resolveStatus } = useOnlineUsers();
	const keybinds = useKeybinds();

	const pinsButtonRef = useRef<HTMLButtonElement>(null);
	const dmHeaderButtonRef = useRef<HTMLButtonElement>(null);
	const [pinsAnchor, setPinsAnchor] = useState<DOMRect | null>(null);
	const [profileOpen, setProfileOpen] = useState(false);
	const [showFilters, setShowFilters] = useState(false);
	const searchInputRef = useRef<HTMLInputElement>(null);

	// ── DM participant lookup ──────────────────────────────────────
	// When this channel is a DM we pull the other participant out of
	// `api.dms.listDMs` and resolve their profile via getPublicKeys.
	const myUserId =
		typeof localStorage !== 'undefined'
			? localStorage.getItem('userId')
			: null;
	const dmRows = useQuery(
		api.dms.listDMs,
		isDM && myUserId ? { userId: myUserId as any } : 'skip',
	);
	const allUsers = useQuery(
		api.auth.getPublicKeys,
		isDM ? {} : 'skip',
	) ?? [];

	const otherParticipant = useMemo(() => {
		if (!isDM || !dmRows || !channel?._id) return null;
		const row = (dmRows as any[]).find((r) => r.channel_id === channel._id);
		if (!row) return null;
		const profile = allUsers.find((u) => u.id === row.other_user_id);
		const storedStatus =
			(profile?.status as string | undefined) ||
			(row.other_user_status as string | undefined) ||
			'offline';
		const liveStatus = resolveStatus(storedStatus, row.other_user_id);
		// DM header uses the raw username (not the server display
		// name) so people always know who they're actually talking to.
		const username =
			(profile?.username as string | undefined) ||
			(row.other_username as string | undefined) ||
			'user';
		return {
			userId: row.other_user_id as string,
			displayName: username,
			username,
			avatarUrl: profile?.avatarUrl ?? row.other_user_avatar_url ?? null,
			status: liveStatus,
		};
	}, [isDM, dmRows, allUsers, channel?._id, resolveStatus]);

	const rotateDMKey = useMutation(api.channelKeys.rotateDMKey);

	const handleRotateDMKey = async () => {
		if (!channel?._id || !otherParticipant || !myUserId) {
			throw new Error("Can't rotate — missing DM context.");
		}
		const privateKey =
			typeof sessionStorage !== 'undefined'
				? sessionStorage.getItem('privateKey')
				: null;
		if (!privateKey) {
			throw new Error('No session key available.');
		}
		const me = allUsers.find((u) => u.id === myUserId);
		const other = allUsers.find((u) => u.id === otherParticipant.userId);
		if (!me?.public_identity_key || !other?.public_identity_key) {
			throw new Error("One participant's public key is missing.");
		}
		const newKeyHex = await crypto.randomBytes(32);
		const plaintext = JSON.stringify({ [channel._id]: newKeyHex });
		const [myBundle, otherBundle] = await Promise.all([
			crypto.publicEncrypt(me.public_identity_key, plaintext),
			crypto.publicEncrypt(other.public_identity_key, plaintext),
		]);
		await rotateDMKey({
			channelId: channel._id as any,
			initiatorUserId: myUserId as any,
			entries: [
				{ userId: myUserId as any, encryptedKeyBundle: myBundle },
				{
					userId: otherParticipant.userId as any,
					encryptedKeyBundle: otherBundle,
				},
			],
		});
	};

	const handleOpenProfile = () => {
		setProfileOpen(true);
	};

	// Wire keybinds that affect this header: toggle pins popover,
	// toggle member list. The KeybindProvider dispatches
	// `brycord:keybind:<id>` events on the window.
	useEffect(() => {
		const onTogglePins = () => handleTogglePins();
		const onToggleMembers = () => {
			if (hideMembersButton) return;
			handleToggleMembers();
		};
		window.addEventListener('brycord:keybind:popouts.openPins', onTogglePins);
		window.addEventListener(
			'brycord:keybind:popouts.toggleMembers',
			onToggleMembers,
		);
		return () => {
			window.removeEventListener('brycord:keybind:popouts.openPins', onTogglePins);
			window.removeEventListener(
				'brycord:keybind:popouts.toggleMembers',
				onToggleMembers,
			);
		};
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [hideMembersButton]);

	const setSearchQuery = (next: string) => {
		onSearchChange?.(next);
	};
	const clearSearchQuery = () => {
		if (onSearchClear) onSearchClear();
		else onSearchChange?.('');
	};

	const onInputBlur = () => {
		// 200ms delay — long enough for the chip click to register
		setTimeout(() => setShowFilters(false), 200);
	};

	const handleFilterClick = (key: string) => {
		setSearchQuery(searchQuery ? `${searchQuery} ${key}` : key);
		requestAnimationFrame(() => searchInputRef.current?.focus());
	};

	/**
	 * On mobile, the back button strips the trailing `/channelId` segment
	 * from the current path so we return to the channel list view. For
	 * `/channels/home/:id` this yields `/channels/home`; for
	 * `/channels/@me/:id` → `/channels/@me`. Falls back to `/channels/@me`
	 * if the path doesn't look like a channel URL.
	 */
	const handleMobileBack = () => {
		const path = location.pathname;
		const match = path.match(/^(\/channels\/(?:home|@me|[^/]+))(?:\/[^/]+)?$/);
		navigate(match ? match[1] : '/channels/@me');
	};

	const handleTogglePins = () => {
		if (pinsAnchor) {
			setPinsAnchor(null);
			return;
		}
		const rect = pinsButtonRef.current?.getBoundingClientRect();
		if (rect) setPinsAnchor(rect);
	};

	const handleToggleMembers = () => {
		if (isNarrow) return;
		if (onToggleMembers) {
			onToggleMembers();
		} else {
			window.dispatchEvent(new CustomEvent('brycord:toggle-members'));
		}
	};

	if (isMobile) {
		return (
			<div className={styles.container}>
				<div className={styles.channelInfoMobile}>
					<button
						type="button"
						className={styles.backButton}
						onClick={handleMobileBack}
						aria-label="Back to channels"
					>
						<ArrowLeft size={20} weight="bold" />
					</button>
					{isDM && otherParticipant ? (
						<button
							ref={dmHeaderButtonRef}
							type="button"
							className={styles.channelInfoButton}
							onClick={handleOpenProfile}
						>
							<Avatar
								src={otherParticipant.avatarUrl}
								size={26}
								fallback={otherParticipant.displayName}
								status={otherParticipant.status as any}
							/>
							<span className={styles.name}>
								{otherParticipant.displayName}
							</span>
							<CaretRight size={14} weight="bold" className={styles.caretRight} />
						</button>
					) : (
						<button
							type="button"
							className={styles.channelInfoButton}
							onClick={onOpenChannelDetails}
						>
							{isVoice ? (
								<SpeakerHigh size={22} className={styles.icon} />
							) : (
								<Hash size={22} weight="bold" className={styles.icon} />
							)}
							<span className={styles.name}>{channel?.name || 'Channel'}</span>
							<CaretRight size={14} weight="bold" className={styles.caretRight} />
						</button>
					)}
				</div>
					{otherParticipant && (
					<MemberProfileModal
						isOpen={profileOpen}
						onClose={() => setProfileOpen(false)}
						member={{ userId: otherParticipant.userId }}
						onRotateKey={handleRotateDMKey}
					/>
				)}
			</div>
		);
	}

	return (
		<div className={styles.container}>
			{isDM && otherParticipant ? (
				<button
					ref={dmHeaderButtonRef}
					type="button"
					className={styles.dmHeaderButton}
					onClick={handleOpenProfile}
					aria-label={`Open ${otherParticipant.displayName}'s profile`}
				>
					<Avatar
						src={otherParticipant.avatarUrl}
						size={28}
						fallback={otherParticipant.displayName}
						status={otherParticipant.status as any}
					/>
					<span className={styles.dmHeaderName}>
						{otherParticipant.displayName}
					</span>
				</button>
			) : (
				<div className={styles.channelInfo}>
					{isVoice ? (
						<SpeakerHigh size={20} className={styles.icon} />
					) : (
						<Hash size={20} weight="bold" className={styles.icon} />
					)}
					<span className={styles.name}>{channel?.name || 'Channel'}</span>
					{channel?.topic && (
						<>
							<div className={styles.divider} />
							<span className={styles.topic}>{channel.topic}</span>
						</>
					)}
				</div>
			)}
			<div className={styles.headerButtons}>
				{isDM && (
					<>
						<Tooltip content="Start Voice Call" placement="bottom">
							<button
								type="button"
								className={styles.headerButton}
								aria-label="Start Voice Call"
								disabled
								title="Voice calls coming soon"
							>
								<Phone size={20} />
							</button>
						</Tooltip>
						<Tooltip content="Start Video Call" placement="bottom">
							<button
								type="button"
								className={styles.headerButton}
								aria-label="Start Video Call"
								disabled
								title="Video calls coming soon"
							>
								<VideoCamera size={20} />
							</button>
						</Tooltip>
					</>
				)}
				<Tooltip
					content="Pinned Messages"
					shortcut={keybinds.getCombo('popouts.openPins')}
					placement="bottom"
				>
					<button
						ref={pinsButtonRef}
						type="button"
						className={`${styles.headerButton} ${pinsAnchor ? styles.headerButtonActive : ''}`}
						aria-label="Pinned"
						onClick={handleTogglePins}
					>
						<PushPin size={20} />
					</button>
				</Tooltip>
				{!hideMembersButton && (
					<Tooltip
						content={
							isNarrow
								? 'Window too narrow for member list'
								: 'Member List'
						}
						shortcut={
							isNarrow ? undefined : keybinds.getCombo('popouts.toggleMembers')
						}
						placement="bottom"
					>
						<button
							type="button"
							className={`${styles.headerButton} ${
								membersVisible && !isNarrow ? styles.headerButtonActive : ''
							} ${isNarrow ? styles.headerButtonDisabled : ''}`}
							aria-label="Members"
							aria-disabled={isNarrow || undefined}
							onClick={handleToggleMembers}
						>
							<Users size={20} />
						</button>
					</Tooltip>
				)}

				<div className={styles.searchBarWrapper}>
					<div className={styles.searchBar}>
						<MagnifyingGlass
							size={16}
							weight="regular"
							className={styles.searchBarIcon}
						/>
						<input
							ref={searchInputRef}
							className={styles.searchBarInput}
							placeholder="Search messages"
							value={searchQuery}
							onChange={(e) => setSearchQuery(e.target.value)}
							onFocus={() => setShowFilters(true)}
							onBlur={onInputBlur}
						/>
						{searchQuery && (
							<button
								type="button"
								className={styles.searchBarClear}
								onClick={clearSearchQuery}
								aria-label="Clear search"
							>
								<X size={14} />
							</button>
						)}
					</div>
					{showFilters && !searchQuery && (
						<div className={styles.filterDropdown}>
							<div className={styles.filterSection}>
								<div className={styles.filterSectionHeader}>
									<span className={styles.filterSectionIcon}>
										<Funnel size={12} />
									</span>
									<span>Search Filters</span>
								</div>
								{SEARCH_FILTERS.map((f) => (
									<button
										key={f.key}
										type="button"
										className={styles.filterOption}
										onMouseDown={(e) => {
											// Use mousedown not click so it fires before the input blur
											e.preventDefault();
											handleFilterClick(f.key);
										}}
									>
										<span className={styles.filterBadge}>{f.key}</span>
										<span className={styles.filterDesc}>— {f.desc}</span>
										<Plus size={14} className={styles.filterPlus} />
									</button>
								))}
							</div>
						</div>
					)}
				</div>
			</div>

			{channel?._id && (
				<ChannelHeaderPinsPopover
					isOpen={pinsAnchor !== null}
					anchorRect={pinsAnchor}
					channelId={channel._id}
					onClose={() => setPinsAnchor(null)}
				/>
			)}

			{otherParticipant && (
				isMobile ? (
					<MobileMemberProfileSheet
						isOpen={profileOpen}
						onClose={() => setProfileOpen(false)}
						member={{ userId: otherParticipant.userId }}
						onRotateKey={handleRotateDMKey}
					/>
				) : (
					<MemberProfileModal
						isOpen={profileOpen}
						onClose={() => setProfileOpen(false)}
						member={{ userId: otherParticipant.userId }}
						onRotateKey={handleRotateDMKey}
					/>
				)
			)}
		</div>
	);
}
