/**
 * JTZL_WebIRC_Chat - Message Input Component with Autocomplete
 *
 * @package   JTZL_WebIRC_Chat
 * @copyright Copyright (c) 2025, JT. G.
 * @license   GPL-3.0+
 * @since     3.0.0
 */

import {
	useState,
	useRef,
	useCallback,
	forwardRef,
	useEffect,
	useImperativeHandle,
	memo,
} from 'react';
import { Input } from '../ui/input';
import { Button } from '../ui/button';

interface IRCCommand {
	command: string;
	description: string;
	usage: string;
	category?: string;
}

interface User {
	nick: string;
	status?: 'online' | 'away' | 'offline';
}

interface MessageInputProps {
	value: string;
	onChange: (value: string) => void;
	onSubmit: (message: string) => void;
	disabled?: boolean;
	placeholder?: string;
	users?: User[];
	className?: string;
	imageUploadButton?: React.ReactNode;
}

export interface MessageInputRef {
	focus: () => void;
}

interface AutocompleteItem {
	type: 'command' | 'user';
	value: string;
	display: string;
	description?: string;
}

/**
 * IRC commands with descriptions for autocomplete.
 *
 * @since 3.0.0
 */
const IRC_COMMANDS: IRCCommand[] = [
	{
		command: 'join',
		description: 'Join a channel',
		usage: '/join #channel',
		category: 'Basic',
	},
	{
		command: 'part',
		description: 'Leave current or specified channel',
		usage: '/part [#channel]',
		category: 'Basic',
	},
	{
		command: 'leave',
		description: 'Alias for /part',
		usage: '/leave [#channel]',
		category: 'Basic',
	},
	{
		command: 'nick',
		description: 'Change nickname',
		usage: '/nick newname',
		category: 'Basic',
	},
	{
		command: 'quit',
		description: 'Disconnect from server',
		usage: '/quit [message]',
		category: 'Basic',
	},
	{
		command: 'msg',
		description: 'Send private message',
		usage: '/msg <nick> <message>',
		category: 'Communication',
	},
	{
		command: 'privmsg',
		description: 'Alias for /msg',
		usage: '/privmsg <nick> <message>',
		category: 'Communication',
	},
	{
		command: 'notice',
		description: 'Send notice message',
		usage: '/notice <target> <message>',
		category: 'Communication',
	},
	{
		command: 'me',
		description: 'Send action message',
		usage: '/me <action>',
		category: 'Communication',
	},
	{
		command: 'query',
		description: 'Open private chat context',
		usage: '/query <nick>',
		category: 'Communication',
	},
	{
		command: 'whois',
		description: 'Get detailed user information',
		usage: '/whois <nick>',
		category: 'Information',
	},
	{
		command: 'who',
		description: 'List users in channel',
		usage: '/who [channel]',
		category: 'Information',
	},
	{
		command: 'list',
		description: 'List available channels',
		usage: '/list [pattern]',
		category: 'Information',
	},
	{
		command: 'names',
		description: 'List nicknames in channel',
		usage: '/names [channel]',
		category: 'Information',
	},
	{
		command: 'topic',
		description: 'View or set channel topic',
		usage: '/topic [text]',
		category: 'Information',
	},
	{
		command: 'away',
		description: 'Set away status',
		usage: '/away [message]',
		category: 'Status',
	},
	{
		command: 'back',
		description: 'Remove away status',
		usage: '/back',
		category: 'Status',
	},
	{
		command: 'ping',
		description: 'Ping user',
		usage: '/ping <nick>',
		category: 'Status',
	},
	{
		command: 'time',
		description: 'Get server time',
		usage: '/time [server]',
		category: 'Status',
	},
	{
		command: 'version',
		description: 'Get version info',
		usage: '/version [nick]',
		category: 'Status',
	},
	{
		command: 'ctcp',
		description: 'Send CTCP command',
		usage: '/ctcp <nick> <command>',
		category: 'Advanced',
	},
	{
		command: 'finger',
		description: 'Send FINGER request',
		usage: '/finger <nick>',
		category: 'Advanced',
	},
	{
		command: 'motd',
		description: 'View message of the day',
		usage: '/motd [server]',
		category: 'Server',
	},
	{
		command: 'admin',
		description: 'View server admin info',
		usage: '/admin [server]',
		category: 'Server',
	},
	{
		command: 'info',
		description: 'View server information',
		usage: '/info [server]',
		category: 'Server',
	},
	{
		command: 'stats',
		description: 'View server statistics',
		usage: '/stats <query> [server]',
		category: 'Server',
	},
	{
		command: 'links',
		description: 'List server links',
		usage: '/links [mask]',
		category: 'Server',
	},
	{
		command: 'lusers',
		description: 'List user statistics',
		usage: '/lusers [mask]',
		category: 'Server',
	},
	{
		command: 'userhost',
		description: 'Get user host information',
		usage: '/userhost <nick>',
		category: 'Server',
	},
	{
		command: 'ison',
		description: 'Check if users are online',
		usage: '/ison <nick> [nick2]...',
		category: 'Server',
	},
	{
		command: 'raw',
		description: 'Send raw IRC command',
		usage: '/raw <IRC command>',
		category: 'Advanced',
	},
	{
		command: 'help',
		description: 'Show available commands',
		usage: '/help',
		category: 'Basic',
	},
];

/**
 * MessageInput component with autocomplete support for IRC commands and user mentions.
 *
 * @since 3.0.0
 */
const MessageInputComponent = forwardRef<MessageInputRef, MessageInputProps>(
	(
		{
			value,
			onChange,
			onSubmit,
			disabled = false,
			placeholder = 'Type a message or /help…',
			users = [],
			className,
			imageUploadButton,
		},
		ref
	) => {
		const [showAutocomplete, setShowAutocomplete] = useState(false);
		const [autocompleteItems, setAutocompleteItems] = useState<
			AutocompleteItem[]
		>([]);
		const [selectedIndex, setSelectedIndex] = useState(0);
		const [autocompletePosition, setAutocompletePosition] = useState({
			start: 0,
			end: 0,
		});

		const autocompleteRef = useRef<HTMLDivElement>(null);
		const inputRef = useRef<HTMLInputElement>(null);
		const blurTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);

		// Cleanup timeout on unmount to prevent memory leak.
		useEffect(() => {
			return () => {
				if (blurTimeoutRef.current) {
					clearTimeout(blurTimeoutRef.current);
				}
			};
		}, []);

		// Expose focus method to parent component
		useImperativeHandle(
			ref,
			() => ({
				focus: () => {
					if (inputRef.current) {
						inputRef.current.focus();
						// Position cursor at the end of the input
						const length = inputRef.current.value.length;
						inputRef.current.setSelectionRange(length, length);
					}
				},
			}),
			[]
		);

		/**
		 * Get autocomplete suggestions based on current input.
		 *
		 * @since 3.0.0
		 */
		const getAutocompleteSuggestions = useCallback(
			(
				input: string,
				cursorPos: number
			): {
				items: AutocompleteItem[];
				position: { start: number; end: number };
			} | null => {
				// Find the word at cursor position
				const beforeCursor = input.slice(0, cursorPos);

				// Check for command autocomplete (starts with /)
				const commandMatch = beforeCursor.match(/\/(\w*)$/);
				if (commandMatch) {
					const commandPrefix = commandMatch[1].toLowerCase();
					const matchingCommands = IRC_COMMANDS.filter((cmd) =>
						cmd.command.toLowerCase().startsWith(commandPrefix)
					);

					if (matchingCommands.length > 0) {
						return {
							items: matchingCommands.map((cmd) => ({
								type: 'command',
								value: `/${cmd.command}`,
								display: `/${cmd.command}`,
								description: cmd.description,
							})),
							position: {
								start:
									beforeCursor.length -
									commandMatch[0].length,
								end: cursorPos,
							},
						};
					}
				}

				// Check for user mention autocomplete (starts with @)
				const mentionMatch = beforeCursor.match(/@(\w*)$/);
				if (mentionMatch && users.length > 0) {
					const mentionPrefix = mentionMatch[1].toLowerCase();
					const matchingUsers = users.filter((user) =>
						user.nick.toLowerCase().startsWith(mentionPrefix)
					);

					if (matchingUsers.length > 0) {
						return {
							items: matchingUsers.map((user) => ({
								type: 'user',
								value: `@${user.nick}`,
								display: user.nick,
								description: user.status
									? `Status: ${user.status}`
									: undefined,
							})),
							position: {
								start:
									beforeCursor.length -
									mentionMatch[0].length,
								end: cursorPos,
							},
						};
					}
				}

				return null;
			},
			[users]
		);

		/**
		 * Update autocomplete suggestions based on current input.
		 *
		 * @since 3.0.0
		 */
		const updateAutocomplete = useCallback(
			(inputValue: string) => {
				const input = inputRef.current;
				if (!input) return;

				const cursorPos = input.selectionStart || inputValue.length;
				const suggestions = getAutocompleteSuggestions(
					inputValue,
					cursorPos
				);

				if (suggestions && suggestions.items.length > 0) {
					setAutocompleteItems(suggestions.items);
					setAutocompletePosition(suggestions.position);
					setSelectedIndex(0);
					setShowAutocomplete(true);
				} else {
					setShowAutocomplete(false);
					setAutocompleteItems([]);
				}
			},
			[getAutocompleteSuggestions]
		);

		/**
		 * Handle input change with autocomplete update.
		 *
		 * @since 3.0.0
		 */
		const handleInputChange = useCallback(
			(newValue: string) => {
				onChange(newValue);
				updateAutocomplete(newValue);
			},
			[onChange, updateAutocomplete]
		);

		/**
		 * Handle autocomplete selection.
		 *
		 * @since 3.0.0
		 */
		const selectAutocompleteItem = useCallback(
			(item: AutocompleteItem) => {
				const beforeReplacement = value.slice(
					0,
					autocompletePosition.start
				);
				const afterReplacement = value.slice(autocompletePosition.end);

				let replacement = item.value;

				// Add space after command or mention
				if (item.type === 'command') {
					replacement += ' ';
				} else if (item.type === 'user') {
					replacement += ' ';
				}

				const newValue =
					beforeReplacement + replacement + afterReplacement;
				const newCursorPos =
					beforeReplacement.length + replacement.length;

				onChange(newValue);
				setShowAutocomplete(false);

				// Set cursor position after replacement
				setTimeout(() => {
					if (inputRef.current) {
						inputRef.current.focus();
						inputRef.current.setSelectionRange(
							newCursorPos,
							newCursorPos
						);
					}
				}, 0);
			},
			[value, autocompletePosition, onChange]
		);

		/**
		 * Handle keyboard navigation in autocomplete.
		 *
		 * @since 3.0.0
		 */
		const handleKeyDown = useCallback(
			(e: React.KeyboardEvent<HTMLInputElement>) => {
				if (!showAutocomplete || autocompleteItems.length === 0) {
					if (e.key === 'Enter') {
						e.preventDefault();
						onSubmit(value);
					}
					return;
				}

				switch (e.key) {
					case 'ArrowDown':
						e.preventDefault();
						setSelectedIndex((prev) =>
							prev < autocompleteItems.length - 1 ? prev + 1 : 0
						);
						break;

					case 'ArrowUp':
						e.preventDefault();
						setSelectedIndex((prev) =>
							prev > 0 ? prev - 1 : autocompleteItems.length - 1
						);
						break;

					case 'Tab':
					case 'Enter':
						e.preventDefault();
						if (autocompleteItems[selectedIndex]) {
							selectAutocompleteItem(
								autocompleteItems[selectedIndex]
							);
						}
						break;

					case 'Escape':
						e.preventDefault();
						setShowAutocomplete(false);
						break;
				}
			},
			[
				showAutocomplete,
				autocompleteItems,
				selectedIndex,
				selectAutocompleteItem,
				onSubmit,
				value,
			]
		);

		/**
		 * Handle form submission.
		 *
		 * @since 3.0.0
		 */
		const handleSubmit = useCallback(
			(e: React.FormEvent) => {
				e.preventDefault();
				if (showAutocomplete && autocompleteItems[selectedIndex]) {
					selectAutocompleteItem(autocompleteItems[selectedIndex]);
				} else {
					onSubmit(value);
				}
			},
			[
				showAutocomplete,
				autocompleteItems,
				selectedIndex,
				selectAutocompleteItem,
				onSubmit,
				value,
			]
		);

		/**
		 * Handle input focus to ensure proper focus management.
		 *
		 * @since 3.0.0
		 */
		const handleFocus = useCallback(() => {
			// Update autocomplete when input gains focus
			updateAutocomplete(value);
		}, [value, updateAutocomplete]);

		/**
		 * Handle input blur to hide autocomplete.
		 *
		 * @since 3.0.0
		 */
		const handleBlur = useCallback(
			(e: React.FocusEvent<HTMLInputElement>) => {
				// Don't hide if focus is moving to autocomplete dropdown
				if (
					autocompleteRef.current &&
					autocompleteRef.current.contains(e.relatedTarget as Node)
				) {
					return;
				}

				// Delay hiding to allow clicking on autocomplete items
				blurTimeoutRef.current = setTimeout(() => {
					setShowAutocomplete(false);
				}, 150);
			},
			[]
		);

		return (
			<div className={`${className || ''}`}>
				<form
					className="flex gap-2 items-center mb-3 w-full max-w-full min-w-0"
					onSubmit={handleSubmit}
					autoComplete="off"
				>
					<label className="sr-only" htmlFor="irc-input">
						Message
					</label>
					<div id="input-help" className="sr-only">
						Type / for commands or @ to mention users. Use arrow
						keys to navigate suggestions, Tab or Enter to select.
					</div>
					{imageUploadButton && (
						<div className="flex-shrink-0">{imageUploadButton}</div>
					)}
					<div className="flex-1 relative min-w-0 max-w-full">
						<Input
							ref={inputRef}
							id="irc-input"
							className="h-10 px-3 text-sm border border-input rounded-md bg-background focus:border-ring focus:ring-2 focus:ring-ring/20 focus:outline-none"
							type="text"
							placeholder={placeholder}
							value={value}
							onChange={(e) => handleInputChange(e.target.value)}
							onKeyDown={handleKeyDown}
							onFocus={handleFocus}
							onBlur={handleBlur}
							disabled={disabled}
							autoComplete="off"
							role="combobox"
							aria-expanded={showAutocomplete}
							aria-haspopup="listbox"
							aria-owns={
								showAutocomplete
									? 'autocomplete-listbox'
									: undefined
							}
							aria-activedescendant={
								showAutocomplete && selectedIndex >= 0
									? `autocomplete-item-${selectedIndex}`
									: undefined
							}
							aria-describedby="input-help"
						/>

						{/* Autocomplete dropdown */}
						{showAutocomplete && autocompleteItems.length > 0 && (
							<div
								ref={autocompleteRef}
								id="autocomplete-listbox"
								className="absolute bottom-full left-0 right-0 mb-1 bg-popover border border-border rounded-md shadow-lg max-h-48 overflow-y-auto z-[999999]"
								role="listbox"
								aria-label="Autocomplete suggestions"
								style={{
									boxShadow:
										'0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
								}}
							>
								{autocompleteItems.map((item, index) => (
									<div
										key={`${item.type}-${item.value}`}
										className={`px-3 py-2 cursor-pointer border-b border-border last:border-b-0 ${
											index === selectedIndex
												? 'bg-accent text-accent-foreground'
												: 'hover:bg-accent/50'
										}`}
										onClick={() =>
											selectAutocompleteItem(item)
										}
										onKeyDown={(e) => {
											if (
												e.key === 'Enter' ||
												e.key === ' '
											) {
												e.preventDefault();
												selectAutocompleteItem(item);
											}
										}}
										onMouseEnter={() =>
											setSelectedIndex(index)
										}
										role="option"
										aria-selected={index === selectedIndex}
										id={`autocomplete-item-${index}`}
										tabIndex={-1}
									>
										<div className="flex items-center justify-between">
											<div className="flex items-center gap-2">
												{item.type === 'command' && (
													<span className="text-xs px-1.5 py-0.5 bg-primary/10 text-primary rounded">
														CMD
													</span>
												)}
												{item.type === 'user' && (
													<>
														<span className="text-xs px-1.5 py-0.5 bg-blue-500/10 text-blue-600 dark:text-blue-400 rounded">
															USER
														</span>
														{/* Status indicator for users */}
														{(() => {
															const user =
																users.find(
																	(u) =>
																		u.nick ===
																		item.display
																);
															const status =
																user?.status;
															if (!status)
																return null;

															let statusColor =
																'bg-gray-500';
															if (
																status ===
																'online'
															) {
																statusColor =
																	'bg-green-500';
															} else if (
																status ===
																'away'
															) {
																statusColor =
																	'bg-yellow-500';
															}

															return (
																<div className="flex items-center gap-1">
																	<div
																		className={`w-2 h-2 rounded-full ${statusColor}`}
																		aria-hidden="true"
																	/>
																	<span className="text-xs text-muted-foreground">
																		Status:{' '}
																		{status}
																	</span>
																</div>
															);
														})()}
													</>
												)}
												<span className="font-medium">
													{item.display}
												</span>
											</div>
										</div>
										{item.description && (
											<div className="text-xs text-muted-foreground mt-1">
												{item.description}
											</div>
										)}
									</div>
								))}
							</div>
						)}
					</div>

					<Button
						type="submit"
						size="icon"
						className="h-10 w-10 flex-shrink-0"
						aria-label="Send message"
						disabled={disabled || !value.trim()}
					>
						<svg
							width="16"
							height="16"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							strokeWidth="2"
						>
							<path d="m22 2-7 20-4-9-9-4z" />
							<path d="M22 2 11 13" />
						</svg>
					</Button>
				</form>
			</div>
		);
	}
);

MessageInputComponent.displayName = 'MessageInputComponent';

// Export memoized version to prevent unnecessary re-renders
export const MessageInput = memo(MessageInputComponent);
