import { useState, useRef, useEffect } from 'react';
import { FiSend, FiPaperclip, FiMic, FiX, FiCheck } from 'react-icons/fi';
import { FileUpload } from './file-upload';
import { FilePreview, FileMetadata } from './file-preview';
import { AudioWaveform } from './audio-waveform';
import { useAudioVisualizer } from '../../hooks/use-audio-visualizer';
import { Spinner } from '../common/spinner';

/**
 * ChatMessageInput - Componente completo e independiente para el envío de mensajes en chats
 *
 * Este componente maneja todo el flujo de envío de mensajes:
 * - Input de texto con auto-resize y diseño minimalista
 * - Grabación de audio con visualización de ondas en tiempo real
 * - Transcripción de audio a texto mediante callback externo (opcional)
 * - Adjuntar archivos múltiples
 * - Preview de archivos adjuntos
 * - Preview de audio grabado (cuando no se usa transcripción)
 * - Envío de mensajes (texto, archivos y/o audio)
 *
 * El componente es completamente independiente del layout padre y usa `flex-shrink-0`
 * para mantenerse fijo en la parte inferior. No requiere configuración especial del contenedor padre.
 *
 * **Transcripción de Audio:**
 * Si se proporciona `onAudioTranscribe`, cuando se detiene la grabación de audio, el componente:
 * 1. Muestra un spinner mientras transcribe
 * 2. Llama al callback con el Blob de audio
 * 3. Establece automáticamente el texto transcrito en el input
 * 4. Limpia el audio grabado (ya que se usa el texto)
 *
 * Si no se proporciona `onAudioTranscribe`, el comportamiento es el mismo que antes:
 * el audio se mantiene como Blob y se envía junto con el mensaje.
 */
export interface ChatMessageInputProps {
	value: string;
	onChange: (value: string) => void;
	onSend: (message: string, files?: File[], audioBlob?: Blob) => void;
	onFileChange?: (files: File[]) => void;
	/**
	 * Optional callback to transcribe audio to text.
	 * If provided, when audio recording stops, it will be called to transcribe the audio.
	 * The transcribed text will be automatically set in the input field.
	 *
	 * @param audioBlob - The recorded audio blob
	 * @returns Promise<string> - The transcribed text
	 */
	onAudioTranscribe?: (audioBlob: Blob) => Promise<string>;
	disabled?: boolean;
	isLoading?: boolean;
	placeholder?: string;
}

export function ChatMessageInput({
	value,
	onChange,
	onSend,
	onFileChange,
	onAudioTranscribe,
	disabled = false,
	isLoading = false,
	placeholder = 'Type your message...',
}: ChatMessageInputProps) {
	const [files, setFiles] = useState<File[]>([]);
	const [showFileUpload, setShowFileUpload] = useState(false);
	const [isRecording, setIsRecording] = useState(false);
	const [isTranscribing, setIsTranscribing] = useState(false);
	const [recordedAudio, setRecordedAudio] = useState<Blob | null>(null);
	const [recordingTime, setRecordingTime] = useState(0);
	const textareaRef = useRef<HTMLTextAreaElement>(null);
	const mediaRecorderRef = useRef<MediaRecorder | null>(null);
	const audioChunksRef = useRef<Blob[]>([]);
	const streamRef = useRef<MediaStream | null>(null);
	const timerRef = useRef<NodeJS.Timeout | null>(null);
	const { audioData, startVisualization, stopVisualization } = useAudioVisualizer();

	useEffect(() => {
		if (textareaRef.current) {
			textareaRef.current.style.height = 'auto';
			const scrollHeight = textareaRef.current.scrollHeight;
			const minHeight = 24;
			const maxHeight = 200;
			const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
			textareaRef.current.style.height = `${newHeight}px`;

			if (scrollHeight > maxHeight) {
				textareaRef.current.style.overflowY = 'auto';
			} else {
				textareaRef.current.style.overflowY = 'hidden';
			}
		}
	}, [value]);

	const handleSend = () => {
		if ((!value.trim() && files.length === 0 && !recordedAudio) || disabled || isLoading) {
			return;
		}

		onSend(value.trim(), files.length > 0 ? files : undefined, recordedAudio || undefined);

		onChange('');
		setFiles([]);
		setRecordedAudio(null);
		setShowFileUpload(false);
		if (onFileChange) {
			onFileChange([]);
		}
	};

	const handleKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
		if (e.key === 'Enter' && !e.shiftKey) {
			e.preventDefault();
			handleSend();
		}
	};

	const handleFilesChange = (newFiles: File[]) => {
		setFiles(newFiles);
		if (onFileChange) {
			onFileChange(newFiles);
		}
	};

	const removeFile = (index: number) => {
		const updatedFiles = files.filter((_, i) => i !== index);
		setFiles(updatedFiles);
		if (onFileChange) {
			onFileChange(updatedFiles);
		}
	};

	const startRecording = async () => {
		try {
			const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
			streamRef.current = stream;
			startVisualization(stream);

			const mediaRecorder = new MediaRecorder(stream, {
				mimeType: 'audio/webm',
			});
			mediaRecorderRef.current = mediaRecorder;
			audioChunksRef.current = [];

			mediaRecorder.ondataavailable = event => {
				if (event.data.size > 0) {
					audioChunksRef.current.push(event.data);
				}
			};

			mediaRecorder.onstop = async () => {
				const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
				stopVisualization();
				if (streamRef.current) {
					streamRef.current.getTracks().forEach(track => track.stop());
				}

				if (onAudioTranscribe && audioBlob.size > 0) {
					setIsTranscribing(true);
					try {
						const transcribedText = await onAudioTranscribe(audioBlob);
						onChange(transcribedText);
						setRecordedAudio(null);
					} catch (error) {
						console.error('Error transcribing audio:', error);
						setRecordedAudio(audioBlob);
					} finally {
						setIsTranscribing(false);
					}
				} else {
					setRecordedAudio(audioBlob);
				}
			};

			mediaRecorder.start();
			setIsRecording(true);
			setRecordingTime(0);

			timerRef.current = setInterval(() => {
				setRecordingTime(prev => prev + 1);
			}, 1000);
		} catch (error) {
			console.error('Error starting recording:', error);
			stopVisualization();
		}
	};

	const stopRecording = () => {
		if (mediaRecorderRef.current && isRecording) {
			mediaRecorderRef.current.stop();
			setIsRecording(false);
			if (timerRef.current) {
				clearInterval(timerRef.current);
				timerRef.current = null;
			}
		}
	};

	const cancelRecording = () => {
		if (mediaRecorderRef.current && isRecording) {
			mediaRecorderRef.current.stop();
			audioChunksRef.current = [];
		}
		if (streamRef.current) {
			streamRef.current.getTracks().forEach(track => track.stop());
		}
		stopVisualization();
		setIsRecording(false);
		setRecordingTime(0);
		if (timerRef.current) {
			clearInterval(timerRef.current);
			timerRef.current = null;
		}
	};

	const removeRecordedAudio = () => {
		setRecordedAudio(null);
	};

	const formatTime = (seconds: number): string => {
		const mins = Math.floor(seconds / 60);
		const secs = seconds % 60;
		return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
	};

	useEffect(() => {
		return () => {
			if (timerRef.current) {
				clearInterval(timerRef.current);
			}
			if (streamRef.current) {
				streamRef.current.getTracks().forEach(track => track.stop());
			}
			stopVisualization();
		};
	}, []);

	return (
		<div className='border-t border-gray-200 bg-white flex-shrink-0'>
			{files.length > 0 && (
				<div className='p-3 border-b border-gray-200 bg-gray-50'>
					<div className='flex items-center justify-between mb-2'>
						<span className='text-sm text-gray-600'>
							{files.length} {files.length === 1 ? 'file' : 'files'} selected
						</span>
						<button
							onClick={() => {
								setFiles([]);
								if (onFileChange) onFileChange([]);
							}}
							className='text-xs text-gray-500 hover:text-gray-700 transition-colors'
						>
							Clear all
						</button>
					</div>
					<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2'>
						{files.map((file, index) => {
							const fileMetadata: FileMetadata = {
								type: file.type.startsWith('audio/')
									? 'audio'
									: file.type.startsWith('image/')
									? 'image'
									: file.type.includes('pdf') ||
									  file.type.includes('word') ||
									  file.type.includes('text')
									? 'document'
									: file.type.includes('spreadsheet') ||
									  file.type.includes('excel') ||
									  file.name.endsWith('.csv')
									? 'spreadsheet'
									: 'other',
								mimeType: file.type,
								size: file.size,
								originalName: file.name,
								url: URL.createObjectURL(file),
							};
							return (
								<FilePreview
									key={index}
									file={fileMetadata}
									onRemove={() => removeFile(index)}
									showRemove={true}
									compact={true}
								/>
							);
						})}
					</div>
				</div>
			)}

			{recordedAudio && !isRecording && (
				<div className='p-3 border-b border-gray-200 bg-gray-50'>
					<div className='flex items-center gap-3'>
						<audio controls className='flex-1 max-w-md'>
							<source src={URL.createObjectURL(recordedAudio)} type='audio/webm' />
						</audio>
						<button
							onClick={removeRecordedAudio}
							className='p-2 hover:bg-gray-200 rounded transition-colors'
							aria-label='Remove audio'
						>
							<FiX className='w-4 h-4 text-gray-600' />
						</button>
					</div>
				</div>
			)}

			{showFileUpload && (
				<div className='p-3 border-b border-gray-200 bg-gray-50'>
					<div className='flex items-center justify-between mb-2'>
						<h3 className='text-sm font-medium text-gray-900'>Upload Files</h3>
						<button
							onClick={() => setShowFileUpload(false)}
							className='p-1 hover:bg-gray-200 rounded transition-colors'
							aria-label='Close file upload'
						>
							<FiX className='w-4 h-4 text-gray-600' />
						</button>
					</div>
					<FileUpload onFilesChange={handleFilesChange} maxFiles={10} disabled={disabled || isLoading} />
				</div>
			)}

			<div className='px-4 py-3'>
				{isRecording ? (
					<div className='flex items-center gap-3 bg-gray-800 rounded-2xl px-4 py-3'>
						<button
							onClick={cancelRecording}
							className='p-1.5 hover:bg-gray-700 rounded-full transition-colors flex-shrink-0'
							aria-label='Cancel recording'
						>
							<FiX className='w-5 h-5 text-gray-300' />
						</button>

						<div className='flex-1 flex items-center gap-3 min-w-0'>
							<AudioWaveform
								audioData={audioData}
								width={200}
								height={32}
								barCount={20}
								color='#9ca3af'
							/>
							<span className='text-sm text-gray-400 font-mono flex-shrink-0'>
								{formatTime(recordingTime)}
							</span>
						</div>

						<button
							onClick={stopRecording}
							className='p-1.5 bg-white rounded-full transition-colors flex-shrink-0 hover:bg-gray-100'
							aria-label='Stop and confirm recording'
						>
							<FiCheck className='w-5 h-5 text-gray-800' />
						</button>
					</div>
				) : (
					<div className='flex items-end gap-2 bg-white border border-gray-300 rounded-2xl px-3 py-2 shadow-sm focus-within:ring-2 focus-within:ring-sidebar-bg focus-within:border-sidebar-bg transition-all'>
						<button
							onClick={() => {
								setShowFileUpload(!showFileUpload);
							}}
							disabled={disabled || isLoading || isTranscribing}
							className='p-1.5 hover:bg-gray-100 rounded-full transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0 mb-0.5'
							aria-label='Attach file'
						>
							<FiPaperclip className='w-5 h-5 text-gray-500' />
						</button>

						<textarea
							ref={textareaRef}
							value={value}
							onChange={e => onChange(e.target.value)}
							onKeyPress={handleKeyPress}
							placeholder={placeholder}
							disabled={disabled || isLoading || isRecording || isTranscribing}
							rows={1}
							className='
								flex-1 bg-transparent border-0 resize-none
								focus:outline-none
								disabled:opacity-50 disabled:cursor-not-allowed
								text-gray-900 placeholder-gray-400
								py-1 px-1
								leading-6
								overflow-y-auto
								[&::-webkit-scrollbar]:w-2
								[&::-webkit-scrollbar-track]:bg-transparent
								[&::-webkit-scrollbar-thumb]:bg-gray-300
								[&::-webkit-scrollbar-thumb]:rounded-full
								[&::-webkit-scrollbar-thumb]:hover:bg-gray-400
							'
							style={{
								height: 'auto',
								minHeight: '24px',
								maxHeight: '200px',
								scrollbarWidth: 'thin',
								scrollbarColor: '#d1d5db transparent',
							}}
						/>

						<div className='flex items-center gap-1 flex-shrink-0 mb-0.5'>
							{isTranscribing ? (
								<div className='flex items-center justify-center p-1.5'>
									<Spinner size='sm' />
								</div>
							) : recordedAudio && !isRecording ? (
								<div className='flex items-center gap-1.5 px-2'>
									<div className='w-2 h-2 bg-green-500 rounded-full animate-pulse' />
									<span className='text-xs text-gray-500'>Audio</span>
								</div>
							) : (
								<>
									<button
										onClick={startRecording}
										disabled={disabled || isLoading}
										className='p-1.5 hover:bg-gray-100 rounded-full transition-colors disabled:opacity-50 disabled:cursor-not-allowed'
										aria-label='Record audio'
									>
										<FiMic className='w-5 h-5 text-gray-500' />
									</button>

									<button
										onClick={handleSend}
										disabled={
											disabled ||
											isLoading ||
											(!value.trim() && files.length === 0 && !recordedAudio)
										}
										className='
											p-1.5 bg-sidebar-bg text-white rounded-full
											hover:bg-sidebar-hover transition-colors
											disabled:opacity-50 disabled:cursor-not-allowed
										'
										aria-label='Send message'
									>
										<FiSend className='w-5 h-5' />
									</button>
								</>
							)}
						</div>
					</div>
				)}
			</div>
		</div>
	);
}
