/**
 * JTZL_WebIRC_Chat - Editable Username Component
 *
 * @package   JTZL_WebIRC_Chat
 * @copyright Copyright (c) 2025, JT. G.
 * @license   GPL-3.0+
 * @since     3.0.0
 */

import * as React from 'react';
import { useState, useCallback, useRef, useEffect } from 'react';
import { Edit2 } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Button } from './button';
import { Input } from './input';

/**
 * Props interface for EditableUsername component.
 *
 * @since 3.0.0
 */
export interface EditableUsernameProps {
	/** Current nickname to display */
	currentNick: string;
	/** Callback when nickname change is requested */
	onNickChange: (newNick: string) => void;
	/** Whether IRC connection is active */
	isConnected: boolean;
	/** Additional CSS classes */
	className?: string;
	/** User persistence configuration from WordPress */
	userPersistence?: {
		isLoggedIn: boolean;
		ajaxUrl: string;
		nonce: string;
		savedNickname?: string;
	};
}

/**
 * Validation state interface for nickname validation.
 *
 * @since 3.0.0
 */
interface ValidationState {
	isValid: boolean;
	error?: string;
}

/**
 * Edit state interface for managing component state.
 *
 * @since 3.0.0
 */
interface EditState {
	isEditing: boolean;
	tempValue: string;
	isLoading: boolean;
	validationState: ValidationState;
}

/**
 * Validate IRC nickname according to RFC standards.
 *
 * @since 3.0.0
 *
 * @param nick - Nickname to validate
 * @return ValidationState - Validation result with error message if invalid
 */
function validateNickname(nick: string): ValidationState {
	const trimmedNick = nick.trim();

	// Check if empty
	if (!trimmedNick) {
		return {
			isValid: false,
			error: 'Nickname cannot be empty',
		};
	}

	// Check length (IRC standard is typically 30 characters max)
	if (trimmedNick.length > 30) {
		return {
			isValid: false,
			error: 'Nickname must be 30 characters or less',
		};
	}

	// Check if starts with a number
	if (/^\d/.test(trimmedNick)) {
		return {
			isValid: false,
			error: 'Nickname cannot start with a number',
		};
	}

	// Check for invalid characters (allow a-z, A-Z, 0-9, and `_ - [ ] \ ` ^ { } |`).
	if (!/^[a-zA-Z0-9_\-\[\]\\`^{}|]+$/.test(trimmedNick)) {
		return {
			isValid: false,
			error: 'Nickname contains invalid characters',
		};
	}

	return { isValid: true };
}

/**
 * Editable username component that allows inline editing of IRC nicknames.
 * Provides display and edit modes with validation and accessibility features.
 *
 * @since 3.0.0
 *
 * @param props - Component props
 * @return JSX.Element - Rendered editable username component
 */
export const EditableUsername = React.forwardRef<
	HTMLDivElement,
	EditableUsernameProps
>(
	(
		{ currentNick, onNickChange, isConnected, className, userPersistence },
		ref
	) => {
		// Component state
		const [editState, setEditState] = useState<EditState>({
			isEditing: false,
			tempValue: currentNick,
			isLoading: false,
			validationState: { isValid: true },
		});

		// Refs for focus management
		const inputRef = useRef<HTMLInputElement>(null);
		const displayRef = useRef<HTMLButtonElement>(null);

		/**
		 * Save nickname to WordPress user meta for logged-in users.
		 *
		 * @since 0.5.5
		 *
		 * @param nickname - Nickname to save
		 */
		const saveNicknameToWordPress = useCallback(
			async (nickname: string) => {
				if (!userPersistence?.isLoggedIn || !userPersistence.ajaxUrl) {
					return;
				}

				try {
					const formData = new FormData();
					formData.append('action', 'chat_webirc_save_nickname');
					formData.append('nickname', nickname);
					formData.append('nonce', userPersistence.nonce);

					const response = await fetch(userPersistence.ajaxUrl, {
						method: 'POST',
						body: formData,
					});

					const result = await response.json();

					if (!result.success) {
						// Silently fail for WordPress save - this is not critical to IRC functionality
						// Could be logged to a proper logging service in production
					}
				} catch {
					// Silently fail for WordPress save - this is not critical to IRC functionality
					// Could be logged to a proper logging service in production
				}
			},
			[userPersistence]
		);

		/**
		 * Enter edit mode and focus input.
		 *
		 * @since 3.0.0
		 */
		const enterEditMode = useCallback(() => {
			if (!isConnected) return;

			setEditState((prev) => ({
				...prev,
				isEditing: true,
				tempValue: currentNick,
				validationState: { isValid: true },
			}));

			// Focus input after state update
			setTimeout(() => {
				if (inputRef.current) {
					inputRef.current.focus();
					inputRef.current.select();
				}
			}, 0);
		}, [currentNick, isConnected]);

		/**
		 * Exit edit mode without saving changes.
		 *
		 * @since 3.0.0
		 */
		const exitEditMode = useCallback(() => {
			setEditState((prev) => ({
				...prev,
				isEditing: false,
				tempValue: currentNick,
				validationState: { isValid: true },
			}));

			// Return focus to display element
			setTimeout(() => {
				if (displayRef.current) {
					displayRef.current.focus();
				}
			}, 0);
		}, [currentNick]);

		/**
		 * Handle input value changes with real-time validation.
		 *
		 * @since 3.0.0
		 */
		const handleInputChange = useCallback(
			(event: React.ChangeEvent<HTMLInputElement>) => {
				const newValue = event.target.value;
				const validationState = validateNickname(newValue);

				setEditState((prev) => ({
					...prev,
					tempValue: newValue,
					validationState,
				}));
			},
			[]
		);

		/**
		 * Submit nickname change if validation passes.
		 *
		 * @since 3.0.0
		 */
		const handleSubmit = useCallback(() => {
			const { tempValue, validationState } = editState;

			if (!validationState.isValid) {
				return;
			}

			const trimmedValue = tempValue.trim();
			if (trimmedValue === currentNick) {
				// No change, just exit edit mode
				exitEditMode();
				return;
			}

			// Set loading state and submit change
			setEditState((prev) => ({
				...prev,
				isLoading: true,
			}));

			onNickChange(trimmedValue);

			// Save to WordPress for logged-in users (fire and forget)
			saveNicknameToWordPress(trimmedValue);

			// Set a timeout to handle cases where server doesn't respond.
			const timeoutId = setTimeout(() => {
				setEditState((prev) => {
					if (prev.isLoading) {
						// Server didn't respond, exit loading state but stay in edit mode.
						return {
							...prev,
							isLoading: false,
							validationState: {
								isValid: false,
								error: 'Server did not respond. Please try again.',
							},
						};
					}
					return prev;
				});
			}, 10000); // 10 second timeout

			// Clear timeout on unmount or when the nick change is processed.
			return () => clearTimeout(timeoutId);

			// Stay in edit mode with loading state until server confirms the change.
			// The component will exit edit mode when currentNick updates (server confirmation).
		}, [
			editState,
			currentNick,
			onNickChange,
			exitEditMode,
			saveNicknameToWordPress,
		]);

		/**
		 * Handle keyboard events for edit mode.
		 *
		 * @since 3.0.0
		 */
		const handleKeyDown = useCallback(
			(event: React.KeyboardEvent<HTMLInputElement>) => {
				switch (event.key) {
					case 'Enter':
						event.preventDefault();
						handleSubmit();
						break;
					case 'Escape':
						event.preventDefault();
						exitEditMode();
						break;
				}
			},
			[handleSubmit, exitEditMode]
		);

		/**
		 * Handle click outside to cancel edit mode.
		 *
		 * @since 3.0.0
		 */
		const handleBlur = useCallback(
			(event: React.FocusEvent) => {
				// Check if focus is moving to one of our buttons
				const relatedTarget = event.relatedTarget as HTMLElement;
				if (
					relatedTarget &&
					relatedTarget.closest('[data-editable-username]')
				) {
					return; // Don't exit if focus is moving within our component
				}

				// On mobile, relatedTarget is often null when clicking buttons.
				// Use a small delay to allow button clicks to process first.
				setTimeout(() => {
					exitEditMode();
				}, 150);
			},
			[exitEditMode]
		);

		// Load saved nickname for logged-in users on mount
		useEffect(() => {
			if (
				userPersistence?.isLoggedIn &&
				userPersistence.savedNickname &&
				(!currentNick || currentNick.startsWith('wp'))
			) {
				// If user is logged in and has a saved nickname, and either no current nick is set
				// or the current nick is a default generated one, use the saved nickname
				onNickChange(userPersistence.savedNickname);
			}
		}, [userPersistence, currentNick, onNickChange]);

		// Handle server response to nickname change
		useEffect(() => {
			if (editState.isEditing && editState.isLoading) {
				// Check if the server confirmed our nickname change
				const trimmedAttemptedValue = editState.tempValue.trim();
				if (currentNick === trimmedAttemptedValue) {
					// Success! Server confirmed our exact nickname
					setEditState((prev) => ({
						...prev,
						isEditing: false,
						isLoading: false,
						tempValue: currentNick,
						validationState: { isValid: true },
					}));
				} else if (currentNick !== editState.tempValue.trim()) {
					// Server changed our nickname (e.g., due to conflict)
					// Update temp value and exit edit mode
					setEditState((prev) => ({
						...prev,
						isEditing: false,
						isLoading: false,
						tempValue: currentNick,
						validationState: { isValid: true },
					}));
				}
			} else if (!editState.isEditing) {
				// Update temp value when not editing
				setEditState((prev) => ({
					...prev,
					tempValue: currentNick,
				}));
			}
		}, [
			currentNick,
			editState.isEditing,
			editState.isLoading,
			editState.tempValue,
		]);

		// Render display mode
		if (!editState.isEditing) {
			return (
				<div
					ref={ref}
					className={cn('inline-flex items-center', className)}
					data-editable-username
				>
					<button
						ref={displayRef}
						onClick={enterEditMode}
						disabled={!isConnected}
						className={cn(
							'inline-flex items-center text-sm font-bold',
							'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
							'disabled:opacity-50 disabled:cursor-not-allowed',
							isConnected && 'cursor-pointer text-foreground'
						)}
						aria-label="Edit nickname"
						title={
							isConnected
								? 'Click to edit nickname'
								: 'Connect to edit nickname'
						}
					>
						{currentNick}
						{isConnected && (
							<Edit2 className="h-3 w-3 opacity-60 ml-0.5" />
						)}
					</button>
				</div>
			);
		}

		// Render edit mode
		return (
			<div
				ref={ref}
				className={cn('inline-flex items-center gap-2', className)}
				data-editable-username
			>
				<div className="flex flex-col">
					<Input
						ref={inputRef}
						value={editState.tempValue}
						onChange={handleInputChange}
						onKeyDown={handleKeyDown}
						onBlur={handleBlur}
						disabled={editState.isLoading}
						aria-invalid={!editState.validationState.isValid}
						aria-describedby={
							editState.validationState.error
								? 'nickname-error'
								: undefined
						}
						className={cn(
							'text-sm h-8 min-w-[120px]',
							!editState.validationState.isValid &&
								'border-destructive'
						)}
						placeholder="Enter nickname"
					/>
					{editState.validationState.error && (
						<span
							id="nickname-error"
							className="text-xs text-destructive mt-1"
							role="alert"
						>
							{editState.validationState.error}
						</span>
					)}
				</div>
				<div className="flex gap-1">
					<Button
						size="sm"
						onClick={handleSubmit}
						onMouseDown={(e) => e.preventDefault()}
						disabled={
							!editState.validationState.isValid ||
							editState.isLoading
						}
						className="h-8 px-2"
					>
						{editState.isLoading ? 'Saving...' : 'Save'}
					</Button>
					<Button
						size="sm"
						variant="outline"
						onClick={exitEditMode}
						onMouseDown={(e) => e.preventDefault()}
						disabled={editState.isLoading}
						className="h-8 px-2"
					>
						Cancel
					</Button>
				</div>
			</div>
		);
	}
);

EditableUsername.displayName = 'EditableUsername';
