/**
 * InviteAcceptPage — handles `/invite/:code#key=<secret>`.
 *
 * Pulls the encrypted payload from `api.invites.use`, decrypts it
 * with the URL-fragment secret, stashes the decoded channel-key
 * map in sessionStorage under `pendingInviteKeys` / `pendingInviteCode`,
 * then forwards the user to the register page (or to `/channels/@me`
 * if they're already logged in).
 *
 * The fragment secret never hits the server — browsers don't send
 * `#…` to origin servers — so knowledge of it is the capability that
 * unlocks the invite payload.
 */
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useConvex } from 'convex/react';
import { api } from '../../../../../convex/_generated/api';
import { usePlatform } from '../../platform';
import { AuthLayout } from './AuthLayout';

export function InviteAcceptPage() {
	const { code } = useParams<{ code: string }>();
	const navigate = useNavigate();
	const convex = useConvex();
	const { crypto } = usePlatform();
	const [status, setStatus] = useState<'working' | 'error'>('working');
	const [error, setError] = useState<string | null>(null);

	useEffect(() => {
		let cancelled = false;

		const run = async () => {
			if (!code) {
				setStatus('error');
				setError('Missing invite code.');
				return;
			}

			const hash =
				typeof window !== 'undefined' ? window.location.hash : '';
			// Fragment looks like "#key=abcd…"
			const match = hash.match(/[#&]key=([^&]+)/);
			const secret = match ? match[1] : null;
			if (!secret) {
				setStatus('error');
				setError(
					'Invite link is missing its secret. Ask the sender for a fresh link.',
				);
				return;
			}

			try {
				const result = await convex.query(api.invites.use, { code });
				if (cancelled) return;
				if ('error' in result) {
					setStatus('error');
					setError(result.error);
					return;
				}

				// Older invites used to send a placeholder empty payload.
				// Treat that as "no channel keys to import" and continue
				// so the user can still register with the code.
				let keys: Record<string, string> = {};
				if (result.encryptedPayload) {
					try {
						const blob = JSON.parse(result.encryptedPayload) as {
							c: string;
							i: string;
							t: string;
						};
						const plaintext = await crypto.decryptData(
							blob.c,
							secret,
							blob.i,
							blob.t,
						);
						keys = JSON.parse(plaintext) as Record<string, string>;
					} catch {
						setStatus('error');
						setError(
							'Failed to decrypt invite payload. The secret may be wrong or the invite corrupted.',
						);
						return;
					}
				}

				try {
					sessionStorage.setItem('pendingInviteCode', code);
					sessionStorage.setItem('pendingInviteKeys', JSON.stringify(keys));
				} catch {
					/* storage blocked — flow still works if same tab */
				}

				// If the user is already signed in, just take them home —
				// the invite keys are already theirs in that case.
				const loggedIn = !!sessionStorage.getItem('privateKey');
				if (loggedIn) {
					navigate('/channels/@me', { replace: true });
				} else {
					navigate('/register', { replace: true });
				}
			} catch (err: any) {
				if (cancelled) return;
				setStatus('error');
				setError(err?.message ?? 'Failed to accept invite.');
			}
		};

		void run();
		return () => {
			cancelled = true;
		};
	}, [code, convex, crypto, navigate]);

	return (
		<AuthLayout>
			<h1 style={{ color: 'var(--text-primary)' }}>
				{status === 'working' ? 'Accepting invite…' : 'Invite Error'}
			</h1>
			{error && (
				<p
					style={{
						color: 'var(--status-danger, #ed4245)',
						marginTop: 12,
						fontSize: 14,
					}}
				>
					{error}
				</p>
			)}
		</AuthLayout>
	);
}

export default InviteAcceptPage;
