/**
 * PollCard — renders a Convex-backed poll inline in the message
 * timeline. Single- and multi-selection polls are distinguished by
 * `poll.allowMultiple` (radio vs checkbox). Disclosed polls show the
 * live tally; undisclosed polls hide counts until the poll is closed.
 *
 * Clicks send (or clear) the viewer's vote via `api.polls.vote` /
 * `api.polls.clearVote`. Because the parent `useQuery(api.polls.get)`
 * is reactive, the tally updates live for every viewer whenever
 * anyone votes.
 */
import { useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useMutation, useQuery } from 'convex/react';
import { Smiley } 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 { EmojiPicker, type EmojiPickerValue } from './EmojiPicker';
import { TwemojiImg } from './TwemojiImg';
import { resolveReactionKeyToUnicode } from '../../utils/emojiLookup';
import styles from './PollCard.module.css';

interface PollCardProps {
	pollId: Id<'polls'>;
}

export function PollCard({ pollId }: PollCardProps) {
	const myUserId =
		typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null;

	const result = useQuery(api.polls.get, {
		pollId,
		userId: (myUserId ?? undefined) as Id<'userProfiles'> | undefined,
	});

	const voteMutation = useMutation(api.polls.vote);
	const clearVoteMutation = useMutation(api.polls.clearVote);
	const closeMutation = useMutation(api.polls.close);
	const addReactionMutation = useMutation(api.polls.addReaction);
	const removeReactionMutation = useMutation(api.polls.removeReaction);

	// Reaction picker — anchored off the Add Reaction button. `null`
	// means the picker is closed.
	const addReactionButtonRef = useRef<HTMLButtonElement | null>(null);
	const [reactPickerPos, setReactPickerPos] = useState<
		{ top: number; left: number } | null
	>(null);

	const [endConfirmOpen, setEndConfirmOpen] = useState(false);
	const [ending, setEnding] = useState(false);
	const [endError, setEndError] = useState<string | null>(null);

	if (!result) {
		return <div className={styles.card}>Loading poll…</div>;
	}

	const { poll, totals, totalVotes, myVote, reactions } = result;
	const isEnded = poll.closed || (poll.closesAt != null && poll.closesAt < Date.now());
	const isMultiple = poll.allowMultiple;
	const countsVisible = poll.disclosed || isEnded;

	const mySelectionSet = new Set<string>(myVote ?? []);

	const handleToggleReaction = (emoji: string, me: boolean) => {
		if (!myUserId) return;
		if (me) {
			void removeReactionMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
				emoji,
			});
		} else {
			void addReactionMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
				emoji,
			});
		}
	};

	const openReactPicker = () => {
		const btn = addReactionButtonRef.current;
		if (!btn) return;
		const rect = btn.getBoundingClientRect();
		setReactPickerPos({
			// Anchor the picker so its bottom edge sits just above the
			// button — matches the message reaction picker placement.
			top: Math.max(8, rect.top - 440),
			left: Math.max(8, rect.left - 240),
		});
	};

	const handlePickReaction = (value: EmojiPickerValue) => {
		if (!myUserId) return;
		// GIF picks are meaningless as reactions — silently drop them.
		if (value.kind === 'gif') {
			setReactPickerPos(null);
			return;
		}
		const emojiKey =
			value.kind === 'custom' ? value.shortcode : value.surrogates;
		// If the viewer already reacted with this emoji, toggle it off
		// instead of inserting a duplicate row (mutation is idempotent
		// either way, but this matches chat behaviour).
		const already = reactions?.some((r) => r.emoji === emojiKey && r.me);
		if (already) {
			void removeReactionMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
				emoji: emojiKey,
			});
		} else {
			void addReactionMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
				emoji: emojiKey,
			});
		}
		setReactPickerPos(null);
	};

	const handleClick = (optionId: string) => {
		if (isEnded) return;
		if (!myUserId) return;

		let next: string[];
		if (isMultiple) {
			const current = new Set(mySelectionSet);
			if (current.has(optionId)) current.delete(optionId);
			else current.add(optionId);
			next = Array.from(current);
		} else {
			next = mySelectionSet.has(optionId) ? [] : [optionId];
		}

		if (next.length === 0) {
			void clearVoteMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
			});
		} else {
			void voteMutation({
				pollId: poll._id,
				userId: myUserId as Id<'userProfiles'>,
				optionIds: next,
			});
		}
	};

	// Bars scale to the leading answer so the winner reaches 100% and
	// the rest scale proportionally — matches the new UI's visuals.
	let maxCount = 0;
	for (const opt of poll.options) {
		const c = totals[opt.id] ?? 0;
		if (c > maxCount) maxCount = c;
	}

	return (
		<div className={styles.card}>
			<div className={styles.question}>{poll.question || 'Poll'}</div>

			{!countsVisible && !isEnded && (
				<div className={styles.meta}>Results hidden until the poll ends</div>
			)}
			{isEnded && (
				<div className={`${styles.meta} ${styles.metaEnded}`}>Poll ended</div>
			)}

			<div className={styles.answers}>
				{poll.options.map((option) => {
					const count = totals[option.id] ?? 0;
					const selected = mySelectionSet.has(option.id);
					const pct = !countsVisible
						? 0
						: maxCount === 0
							? 0
							: (count / maxCount) * 100;
					return (
						<button
							type="button"
							key={option.id}
							className={[
								styles.answer,
								selected ? styles.answerSelected : '',
								isEnded ? styles.answerDisabled : '',
							]
								.filter(Boolean)
								.join(' ')}
							style={{ ['--pct' as any]: `${pct}%` }}
							onClick={() => handleClick(option.id)}
							disabled={isEnded}
							aria-pressed={selected}
						>
							<span
								className={[
									styles.radio,
									isMultiple ? styles.radioCheckbox : '',
									selected ? styles.radioChecked : '',
								]
									.filter(Boolean)
									.join(' ')}
							/>
							<span className={styles.label}>{option.text}</span>
							{countsVisible && (
								<span className={styles.count}>
									{count} {count === 1 ? 'vote' : 'votes'}
								</span>
							)}
						</button>
					);
				})}
			</div>

			<div className={styles.totalRow}>
				<span>
					{totalVotes} {totalVotes === 1 ? 'voter' : 'voters'}
					{isMultiple ? ' · multi-select' : ''}
				</span>
				{myUserId === poll.createdBy && !isEnded && (
					<button
						type="button"
						className={styles.endButton}
						onClick={() => {
							setEndError(null);
							setEndConfirmOpen(true);
						}}
					>
						End Poll
					</button>
				)}
			</div>

			{(reactions.length > 0 || myUserId) && (
				<div className={styles.reactions}>
					{reactions.map((r) => (
						<button
							key={r.emoji}
							type="button"
							className={`${styles.reactionChip} ${r.me ? styles.reactionMe : ''}`}
							onClick={() => handleToggleReaction(r.emoji, r.me)}
						>
							<TwemojiImg
								emoji={resolveReactionKeyToUnicode(r.emoji)}
								size={16}
							/>
							<span className={styles.reactionCount}>{r.count}</span>
						</button>
					))}
					{myUserId && (
						<button
							ref={addReactionButtonRef}
							type="button"
							className={styles.addReactionChip}
							onClick={openReactPicker}
							aria-label="Add reaction"
							title="Add reaction"
						>
							<Smiley size={14} weight="regular" />
						</button>
					)}
				</div>
			)}

			{reactPickerPos &&
				createPortal(
					<div
						style={{
							position: 'fixed',
							top: reactPickerPos.top,
							left: reactPickerPos.left,
							zIndex: 15000,
						}}
						onClick={(e) => e.stopPropagation()}
					>
						<EmojiPicker
							onSelect={handlePickReaction}
							onClose={() => setReactPickerPos(null)}
						/>
					</div>,
					document.body,
				)}

			<Modal.Root
				isOpen={endConfirmOpen}
				onClose={() => {
					if (ending) return;
					setEndConfirmOpen(false);
				}}
				size="small"
			>
				<Modal.Header
					title="End poll?"
					onClose={() => {
						if (ending) return;
						setEndConfirmOpen(false);
					}}
				/>
				<Modal.Content>
					<div className={styles.endModalBody}>
						<p className={styles.endModalDescription}>
							Voting will be locked and the results will be finalised.
							{!poll.disclosed && (
								<> Hidden vote counts will become visible to everyone.</>
							)}
						</p>
						<div className={styles.endModalQuestion}>
							{poll.question || 'Poll'}
						</div>
						{endError && <div className={styles.endModalError}>{endError}</div>}
						<div className={styles.endModalActions}>
							<Button
								variant="secondary"
								size="sm"
								onClick={() => setEndConfirmOpen(false)}
								disabled={ending}
							>
								Cancel
							</Button>
							<Button
								variant="danger"
								size="sm"
								loading={ending}
								onClick={async () => {
									if (!myUserId) return;
									setEnding(true);
									setEndError(null);
									try {
										await closeMutation({
											pollId: poll._id,
											userId: myUserId as Id<'userProfiles'>,
										});
										setEndConfirmOpen(false);
									} catch (err: any) {
										setEndError(err?.message || 'Failed to end the poll.');
									} finally {
										setEnding(false);
									}
								}}
							>
								End Poll
							</Button>
						</div>
					</div>
				</Modal.Content>
			</Modal.Root>
		</div>
	);
}
