/**
 * ChannelDetailsDrawer — mobile bottom sheet opened by tapping the
 * channel name in the ChannelHeader. Shows a Members/Pins tab switcher
 * with the channel's member list and pinned messages.
 */
import { useEffect, useMemo, useState } from 'react';
import { Users, PushPin } from '@phosphor-icons/react';
import { useQuery } from 'convex/react';
import { BottomSheet } from '@discord-clone/ui';
import { api } from '../../../../../convex/_generated/api';
import { usePlatform } from '../../platform';
import { MemberListContainer } from '../member/MemberListContainer';
import { PinnedMessageRow, ReachedEndNotice, type PinnedMessage } from './PinnedMessageRow';
import type { AttachmentMetadata } from './EncryptedAttachment';

type DrawerTab = 'members' | 'pins';

interface ChannelDetailsDrawerProps {
	isOpen: boolean;
	onClose: () => void;
	channelId: string;
	channelName?: string;
	channelType?: string;
}

const tabBarStyle: React.CSSProperties = {
	display: 'flex',
	gap: 8,
	padding: '4px 12px 12px',
	borderBottom: '1px solid var(--border-subtle, rgba(255,255,255,0.06))',
};

const tabButtonStyle = (active: boolean): React.CSSProperties => ({
	flex: 1,
	display: 'flex',
	alignItems: 'center',
	justifyContent: 'center',
	gap: 8,
	padding: '10px 12px',
	borderRadius: 10,
	border: 'none',
	background: active ? 'var(--bg-hover, rgba(255,255,255,0.08))' : 'transparent',
	color: active ? 'var(--text-primary, #fff)' : 'var(--text-muted, #a0a0a8)',
	fontSize: 14,
	fontWeight: 600,
	cursor: 'pointer',
});

const tabBodyStyle: React.CSSProperties = {
	padding: '12px',
	minHeight: 240,
};

const emptyStateStyle: React.CSSProperties = {
	padding: '24px 12px',
	textAlign: 'center',
	color: 'var(--text-muted, #a0a0a8)',
	fontSize: 14,
};

export function ChannelDetailsDrawer({
	isOpen,
	onClose,
	channelId,
	channelName,
	channelType,
}: ChannelDetailsDrawerProps) {
	const [activeTab, setActiveTab] = useState<DrawerTab>('members');

	// Reset to Members whenever the drawer re-opens so a previously
	// selected Pins tab doesn't follow across channel changes.
	useEffect(() => {
		if (isOpen) setActiveTab('members');
	}, [isOpen, channelId]);

	const title = channelName ?? 'Channel';

	return (
		<BottomSheet isOpen={isOpen} onClose={onClose} title={title}>
			<div style={tabBarStyle}>
				<button
					type="button"
					style={tabButtonStyle(activeTab === 'members')}
					onClick={() => setActiveTab('members')}
					role="tab"
					aria-selected={activeTab === 'members'}
				>
					<Users size={18} weight="fill" />
					<span>Members</span>
				</button>
				<button
					type="button"
					style={tabButtonStyle(activeTab === 'pins')}
					onClick={() => setActiveTab('pins')}
					role="tab"
					aria-selected={activeTab === 'pins'}
				>
					<PushPin size={18} weight="fill" />
					<span>Pins</span>
				</button>
			</div>

			<div style={tabBodyStyle}>
				{activeTab === 'members' ? (
					<MemberListContainer channelId={channelId} variant="drawer" />
				) : (
					<PinsTabContent
						channelId={channelId}
						isOpen={isOpen && activeTab === 'pins'}
						onClose={onClose}
					/>
				)}
				{/* channelType currently unused; reserved for future voice-specific UI */}
				<span style={{ display: 'none' }}>{channelType}</span>
			</div>
		</BottomSheet>
	);
}

// ── Pins tab ────────────────────────────────────────────────────────

const TAG_LENGTH = 32;

interface PinsTabContentProps {
	channelId: string;
	isOpen: boolean;
	onClose: () => void;
}

function PinsTabContent({ channelId, isOpen, onClose }: PinsTabContentProps) {
	const { crypto } = usePlatform();

	const userId =
		typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null;
	const privateKeyPem =
		typeof sessionStorage !== 'undefined'
			? sessionStorage.getItem('privateKey')
			: null;

	const allKeys = useQuery(
		api.channelKeys.getKeysForUser,
		userId && isOpen ? ({ userId: userId as any } as any) : 'skip',
	);

	// Merge all encrypted key bundles → { channelId: keyHex }
	const [channelKey, setChannelKey] = useState<string | null>(null);
	useEffect(() => {
		let cancelled = false;
		if (!allKeys || !privateKeyPem) {
			setChannelKey(null);
			return;
		}
		(async () => {
			const merged: Record<string, string> = {};
			for (const item of allKeys) {
				try {
					const bundleJson = await crypto.privateDecrypt(
						privateKeyPem,
						(item as any).encrypted_key_bundle,
					);
					Object.assign(merged, JSON.parse(bundleJson));
				} catch (err) {
					console.error('Failed to decrypt key bundle:', err);
				}
			}
			if (cancelled) return;
			setChannelKey(merged[channelId] ?? null);
		})();
		return () => {
			cancelled = true;
		};
	}, [allKeys, privateKeyPem, channelId, crypto]);

	const pinnedRaw = useQuery(
		api.messages.listPinned,
		isOpen
			? ({
					channelId: channelId as any,
					userId: (userId as any) ?? undefined,
				} as any)
			: 'skip',
	);

	const [decryptedMap, setDecryptedMap] = useState<Map<string, string>>(new Map());

	useEffect(() => {
		if (!channelKey || !pinnedRaw) return;
		let cancelled = false;
		(async () => {
			const next = new Map(decryptedMap);
			let changed = false;
			for (const msg of pinnedRaw as any[]) {
				const id = msg.id as string;
				if (next.has(id)) continue;
				if (!msg.ciphertext || msg.ciphertext.length < TAG_LENGTH) {
					next.set(id, '[Invalid Encrypted Message]');
					changed = true;
					continue;
				}
				const tag = msg.ciphertext.slice(-TAG_LENGTH);
				const contentHex = msg.ciphertext.slice(0, -TAG_LENGTH);
				try {
					const plaintext = await crypto.decryptData(
						contentHex,
						channelKey,
						msg.nonce,
						tag,
					);
					if (cancelled) return;
					next.set(id, plaintext);
					changed = true;
				} catch {
					next.set(id, '[Unable to decrypt]');
					changed = true;
				}
			}
			if (changed && !cancelled) setDecryptedMap(next);
		})();
		return () => {
			cancelled = true;
		};
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [pinnedRaw, channelKey]);

	const pinned: PinnedMessage[] = useMemo(() => {
		if (!pinnedRaw) return [];
		return (pinnedRaw as any[])
			.slice()
			.sort((a, b) => {
				const at = a.created_at ? new Date(a.created_at).getTime() : 0;
				const bt = b.created_at ? new Date(b.created_at).getTime() : 0;
				return bt - at;
			})
			.map((msg) => {
				const id = msg.id as string;
				const rawContent = decryptedMap.get(id) ?? '';
				let text = rawContent;
				const attachments: AttachmentMetadata[] = [];
				try {
					const parsed = JSON.parse(rawContent);
					if (parsed && typeof parsed === 'object') {
						if (Array.isArray(parsed)) {
							for (const item of parsed) {
								if (item?.type === 'attachment' && item.url && item.key && item.iv) {
									attachments.push(item as AttachmentMetadata);
								}
							}
							text = '';
						} else if (parsed.type === 'attachment' && parsed.url && parsed.key && parsed.iv) {
							attachments.push(parsed as AttachmentMetadata);
							text = '';
						} else if (parsed.text !== undefined) {
							text = String(parsed.text);
						}
					}
				} catch {
					// plain text — leave as-is
				}
				return {
					id,
					authorName: msg.displayName || msg.username || 'User',
					authorAvatarUrl: msg.avatarUrl ?? null,
					content: text,
					timestamp: msg.created_at
						? new Date(msg.created_at).getTime()
						: Date.now(),
					attachments,
				} as PinnedMessage;
			});
	}, [pinnedRaw, decryptedMap]);

	const handleJumpTo = (messageId: string) => {
		window.dispatchEvent(
			new CustomEvent('brycord:scroll-to-message', {
				detail: { channelId, messageId },
			}),
		);
		onClose();
	};

	if (pinnedRaw === undefined) {
		return <div style={emptyStateStyle}>Loading pinned messages…</div>;
	}

	if (pinned.length === 0) {
		return <div style={emptyStateStyle}>No pinned messages yet.</div>;
	}

	return (
		<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
			{pinned.map((msg) => (
				<PinnedMessageRow
					key={msg.id}
					message={msg}
					onClick={() => handleJumpTo(msg.id)}
				/>
			))}
			<ReachedEndNotice />
		</div>
	);
}
