/**
 * JTZL_WebIRC_Chat - Avatar Upload Component
 *
 * @package   JTZL_WebIRC_Chat
 * @copyright Copyright (c) 2025, JT. G.
 * @license   GPL-3.0+
 * @since     3.1.0
 */

import {
	useState,
	useRef,
	useCallback,
	useMemo,
	type ChangeEvent,
} from 'react';
import { Button } from '../ui/button';
import { Avatar, AvatarImage, AvatarFallback } from '../ui/avatar';
import {
	type ImageValidationConfig,
	DEFAULT_IMAGE_VALIDATION_CONFIG,
} from '../../types/image';
import { validateImageFiles } from '../../lib/image-validation';
import { formatFileSize } from '../../lib/image-processing';

/**
 * Props for AvatarUpload component.
 *
 * @since 3.1.0
 */
interface AvatarUploadProps {
	currentAvatar?: string;
	onAvatarChange: (avatarUrl: string) => void;
	onUploadError?: (error: string) => void;
	size?: 'sm' | 'md' | 'lg';
	disabled?: boolean;
	className?: string;
	fallbackInitials?: string;
}

/**
 * Upload state for avatar.
 *
 * @since 3.1.0
 */
interface AvatarUploadState {
	isUploading: boolean;
	progress: number;
	error?: string;
	previewUrl?: string;
}

/**
 * AvatarUpload component for profile image management.
 *
 * @since 3.1.0
 */
export function AvatarUpload({
	currentAvatar,
	onAvatarChange,
	onUploadError,
	size = 'md',
	disabled = false,
	className = '',
	fallbackInitials = 'U',
}: AvatarUploadProps) {
	const [uploadState, setUploadState] = useState<AvatarUploadState>({
		isUploading: false,
		progress: 0,
	});
	const [showCropPreview, setShowCropPreview] = useState(false);
	const [selectedFile, setSelectedFile] = useState<File | null>(null);

	const fileInputRef = useRef<HTMLInputElement>(null);
	const xhrRef = useRef<XMLHttpRequest | null>(null);

	/**
	 * Validation configuration for avatars.
	 *
	 * @since 3.1.0
	 */
	const validationConfig: ImageValidationConfig = useMemo(
		() => ({
			...DEFAULT_IMAGE_VALIDATION_CONFIG,
			maxFiles: 1,
			maxFileSize: 5 * 1024 * 1024, // 5MB for avatars.
		}),
		[]
	);

	/**
	 * Get avatar size classes.
	 *
	 * @since 3.1.0
	 */
	const sizeClasses = useMemo(() => {
		switch (size) {
			case 'sm':
				return 'h-8 w-8';
			case 'lg':
				return 'h-16 w-16';
			case 'md':
			default:
				return 'h-12 w-12';
		}
	}, [size]);

	/**
	 * Upload avatar to server.
	 *
	 * @since 3.1.0
	 */
	const uploadAvatar = useCallback(
		async (file: File): Promise<string> => {
			setUploadState({
				isUploading: true,
				progress: 0,
			});

			// Check if config is available.
			if (!window.webircChatConfig?.nonce) {
				const error =
					'Configuration not available. Please refresh the page.';
				setUploadState({
					isUploading: false,
					progress: 0,
					error,
				});
				if (onUploadError) {
					onUploadError(error);
				}
				throw new Error(error);
			}

			try {
				// Create FormData for upload.
				const formData = new FormData();
				formData.append('file', file);
				formData.append('action', 'chat_webirc_upload_avatar');
				formData.append('nonce', window.webircChatConfig.nonce);

				// Upload with XMLHttpRequest for progress tracking.
				const url = await new Promise<string>((resolve, reject) => {
					const xhr = new XMLHttpRequest();
					xhrRef.current = xhr;

					xhr.upload.addEventListener('progress', (e) => {
						if (e.lengthComputable) {
							const progress = Math.round(
								(e.loaded / e.total) * 100
							);
							setUploadState((prev) => ({
								...prev,
								progress,
							}));
						}
					});

					xhr.addEventListener('load', () => {
						xhrRef.current = null;
						if (xhr.status === 200) {
							try {
								const response = JSON.parse(xhr.responseText);
								if (response.success && response.data?.url) {
									resolve(response.data.url);
								} else {
									// Extract error message from response.
									const errorMsg =
										response.data?.message ||
										response.message ||
										'Upload failed';
									reject(new Error(errorMsg));
								}
							} catch {
								reject(
									new Error(
										`Invalid server response: ${xhr.responseText.substring(0, 100)}`
									)
								);
							}
						} else {
							// Try to parse error response.
							try {
								const response = JSON.parse(xhr.responseText);
								const errorMsg =
									response.data?.message ||
									response.message ||
									xhr.statusText;
								reject(
									new Error(
										`Upload failed (${xhr.status}): ${errorMsg}`
									)
								);
							} catch {
								reject(
									new Error(
										`Upload failed (${xhr.status}): ${xhr.statusText}`
									)
								);
							}
						}
					});

					xhr.addEventListener('error', () => {
						xhrRef.current = null;
						reject(new Error('Network error during upload'));
					});

					xhr.addEventListener('abort', () => {
						xhrRef.current = null;
						reject(new Error('Upload cancelled'));
					});

					xhr.open(
						'POST',
						window.webircChatConfig?.ajaxUrl ??
							'/wp-admin/admin-ajax.php'
					);
					xhr.send(formData);
				});

				setUploadState({
					isUploading: false,
					progress: 100,
				});

				return url;
			} catch (error) {
				const errorMessage =
					error instanceof Error
						? error.message
						: String(error) || 'Upload failed';

				// Log error for debugging.
				// eslint-disable-next-line no-console
				console.error('Avatar upload error:', error);

				setUploadState({
					isUploading: false,
					progress: 0,
					error: errorMessage,
				});

				if (onUploadError) {
					onUploadError(errorMessage);
				}

				throw error;
			}
		},
		[onUploadError]
	);

	/**
	 * Handle file selection.
	 *
	 * @since 3.1.0
	 */
	const handleFileSelect = useCallback(
		async (file: File) => {
			// Validate file.
			const validationResults = await validateImageFiles(
				[file],
				validationConfig
			);

			if (!validationResults[0].isValid) {
				const errorMessage =
					validationResults[0].message || 'Invalid file';
				setUploadState((prev) => ({
					...prev,
					error: errorMessage,
				}));

				if (onUploadError) {
					onUploadError(errorMessage);
				}
				return;
			}

			// Create preview URL.
			const previewUrl = URL.createObjectURL(file);
			setSelectedFile(file);
			setUploadState((prev) => ({
				...prev,
				previewUrl,
				error: undefined,
			}));
			setShowCropPreview(true);
		},
		[validationConfig, onUploadError]
	);

	/**
	 * Handle file input change.
	 *
	 * @since 3.1.0
	 */
	const handleInputChange = useCallback(
		(e: ChangeEvent<HTMLInputElement>) => {
			const file = e.target.files?.[0];
			if (file) {
				handleFileSelect(file);
			}
			// Reset input value to allow selecting the same file again.
			e.target.value = '';
		},
		[handleFileSelect]
	);

	/**
	 * Trigger file input click.
	 *
	 * @since 3.1.0
	 */
	const handleButtonClick = useCallback(() => {
		// Debug: Log config availability.
		if (!window.webircChatConfig?.nonce) {
			// eslint-disable-next-line no-console
			console.error(
				'WebIRC Chat: Configuration not available. Nonce:',
				window.webircChatConfig?.nonce
			);
		}
		fileInputRef.current?.click();
	}, []);

	/**
	 * Confirm and upload avatar.
	 *
	 * @since 3.1.0
	 */
	const handleConfirmUpload = useCallback(async () => {
		if (!selectedFile) {
			return;
		}

		try {
			const avatarUrl = await uploadAvatar(selectedFile);
			onAvatarChange(avatarUrl);
			setShowCropPreview(false);
			setSelectedFile(null);

			// Clean up preview URL.
			if (uploadState.previewUrl) {
				URL.revokeObjectURL(uploadState.previewUrl);
			}
		} catch {
			// Error already handled in uploadAvatar.
		}
	}, [selectedFile, uploadAvatar, onAvatarChange, uploadState.previewUrl]);

	/**
	 * Cancel avatar upload.
	 *
	 * @since 3.1.0
	 */
	const handleCancelUpload = useCallback(() => {
		// Cancel ongoing upload if any.
		if (xhrRef.current) {
			xhrRef.current.abort();
			xhrRef.current = null;
		}

		// Clean up preview URL.
		if (uploadState.previewUrl) {
			URL.revokeObjectURL(uploadState.previewUrl);
		}

		setShowCropPreview(false);
		setSelectedFile(null);
		setUploadState({
			isUploading: false,
			progress: 0,
		});
	}, [uploadState.previewUrl]);

	return (
		<div className={`relative inline-block ${className}`}>
			<input
				ref={fileInputRef}
				type="file"
				accept="image/jpeg,image/png,image/gif,image/webp"
				onChange={handleInputChange}
				disabled={disabled || uploadState.isUploading}
				className="sr-only"
				aria-label="Select avatar image"
			/>

			<div className="relative group">
				<Avatar className={sizeClasses}>
					{currentAvatar && (
						<AvatarImage src={currentAvatar} alt="User avatar" />
					)}
					<AvatarFallback className="bg-primary text-primary-foreground">
						{fallbackInitials}
					</AvatarFallback>
				</Avatar>

				{/* Upload overlay */}
				<button
					type="button"
					onClick={handleButtonClick}
					disabled={disabled || uploadState.isUploading}
					className="absolute inset-0 flex items-center justify-center bg-black/50 rounded-full opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer disabled:cursor-not-allowed"
					aria-label="Change avatar"
				>
					<svg
						width="16"
						height="16"
						viewBox="0 0 24 24"
						fill="none"
						stroke="white"
						strokeWidth="2"
						strokeLinecap="round"
						strokeLinejoin="round"
					>
						<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
						<circle cx="12" cy="13" r="4" />
					</svg>
				</button>

				{/* Upload progress indicator */}
				{uploadState.isUploading && (
					<div className="absolute inset-0 flex items-center justify-center bg-black/70 rounded-full">
						<div className="text-white text-xs font-medium">
							{uploadState.progress}%
						</div>
					</div>
				)}
			</div>

			{/* Error message */}
			{uploadState.error && (
				<div className="absolute top-full left-0 mt-1 text-xs text-destructive whitespace-nowrap">
					{uploadState.error}
				</div>
			)}

			{/* Crop preview modal */}
			{showCropPreview && uploadState.previewUrl && (
				<div className="fixed inset-0 z-[999999] bg-black/80 flex items-center justify-center p-4">
					<div className="bg-background rounded-lg shadow-xl max-w-md w-full p-6">
						<h3 className="text-lg font-semibold mb-4">
							Preview Avatar
						</h3>

						{/* Circular preview */}
						<div className="flex justify-center mb-4">
							<div className="relative">
								<Avatar className="h-32 w-32">
									<AvatarImage
										src={uploadState.previewUrl}
										alt="Avatar preview"
									/>
								</Avatar>
								<div className="absolute inset-0 rounded-full border-2 border-primary pointer-events-none" />
							</div>
						</div>

						{/* File info */}
						{selectedFile && (
							<div className="text-sm text-muted-foreground mb-4 text-center">
								<div className="truncate">
									{selectedFile.name}
								</div>
								<div>{formatFileSize(selectedFile.size)}</div>
							</div>
						)}

						{/* Action buttons */}
						<div className="flex gap-2">
							<Button
								type="button"
								variant="outline"
								onClick={handleCancelUpload}
								disabled={uploadState.isUploading}
								className="flex-1"
							>
								Cancel
							</Button>
							<Button
								type="button"
								onClick={handleConfirmUpload}
								disabled={uploadState.isUploading}
								className="flex-1"
							>
								{uploadState.isUploading
									? `Uploading... ${uploadState.progress}%`
									: 'Upload'}
							</Button>
						</div>
					</div>
				</div>
			)}
		</div>
	);
}
