'use client'

import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect, useMemo } from 'react';
import { IconShieldAdd } from '@/app/ui/icons/icons';
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/fileUpload';
import { useTranslations } from 'next-intl';
import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
import { useQueryClient } from '@tanstack/react-query';

interface SelectedFile {
	file: File;
	preview: string | undefined;
	name: string;
	size: string;
	dimensions?: {
		width: number;
		height: number;
	};
	uploadId?: string;
	progress: number;
	status: 'pending' | 'uploading' | 'completed' | 'error' | 'cancelled';
	error?: string;
}

interface FileUploadInitResponse {
	upload_id: string;
	file_name: string;
	total_chunks: number;
	chunk_size: number;
	status: string;
}

interface UploadSectionFile {
	fileType: string;
	allowedExtensions: string[];
	maxFileSize: number;
	maxParallelUploads?: number;
}

export default function UploadSectionFile({
	fileType,
	allowedExtensions,
	maxFileSize,
	maxParallelUploads = 3
}: UploadSectionFile) {
	const fileInputRef = useRef<HTMLInputElement>(null);
	const [isDragging, setIsDragging] = useState<boolean>(false);
	const [selectedFiles, setSelectedFiles] = useState<SelectedFile[]>([]);
	const [error, setError] = useState<string | null>(null);
	const [activeUploads, setActiveUploads] = useState<Set<string>>(new Set()); // ID активных загрузок
	const queryClient = useQueryClient();
	const isCancelledRef = useRef<Map<string, boolean>>(new Map()); // Для отслеживания отмен по файлам
	const selectedFilesRef = useRef<SelectedFile[]>(selectedFiles);

	const t = useTranslations('Global');

	const acceptString = useMemo(() => {
		if (!allowedExtensions || !Array.isArray(allowedExtensions)) {
			return '';
		}
		return allowedExtensions.map(e => `.${e}`).join(', ');
	}, [allowedExtensions]);

	const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => {
		if (!allowedExtensions.includes(file.type as string)) {
			const extension = file.name.split('.').pop()?.toLowerCase();
			if (!extension || !allowedExtensions.includes(extension as string)) {
				return {
					isValid: false,
					errorMessage: t('unsupported-file-format')
				};
			}
		}

		const MAX_SIZE = maxFileSize;

		if (file.size > MAX_SIZE) {
			return {
				isValid: false,
				errorMessage: t('the-file-is-too-large')
			};
		}

		return { isValid: true };
	};

	const getImageDimensions = (file: File): Promise<{ width: number; height: number }> => {
		return new Promise((resolve, reject) => {
			const img = new Image();
			const objectUrl = URL.createObjectURL(file);

			img.onload = () => {
				const dimensions = {
					width: img.width,
					height: img.height
				};
				URL.revokeObjectURL(objectUrl);
				resolve(dimensions);
			};

			img.onerror = () => {
				URL.revokeObjectURL(objectUrl);
				reject(new Error(t('error-uploading-file')));
			};

			img.src = objectUrl;
		});
	};

	const handleFilesSelect = useCallback(async (files: File[]): Promise<void> => {
		setError(null);

		const newFiles: SelectedFile[] = [];

		for (const file of files) {
			const validation = validateFile(file);

			if (!validation.isValid) {
				setError(prev => prev ? `${prev}, ${validation.errorMessage}` : validation.errorMessage || t('unknown-validation-error'));
				continue;
			}

			let dimensions: {
				width: number,
				height: number,
			} | undefined = undefined;
			if (fileType === "image") {
				try {
					dimensions = await getImageDimensions(file) as { width: number, height: number };
					if (dimensions?.width < 100 || dimensions?.height < 100 ||
						dimensions?.width > 10000 || dimensions?.height > 10000) {
						setError(prev => prev ? `${prev}, ${t('image-size')}: ${dimensions?.width}x${dimensions?.height}. ${t('acceptable-range')}: 100x100 - 10000x10000 ${t('pixels')}.` :
							`${t('image-size')}: ${dimensions?.width}x${dimensions?.height}. ${t('acceptable-range')}: 100x100 - 10000x10000 ${t('pixels')}.`);
						continue;
					}
				} catch (err) {
					setError(prev => prev ? `${prev}, ${t('error-reading-image')}` : t('error-reading-image'));
					continue;
				}
			}

			newFiles.push({
				file,
				name: file.name,
				size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
				dimensions,
				preview: file.size < 10 * 1024 * 1024 ? URL.createObjectURL(file) : undefined,
				progress: 0,
				status: 'pending'
			});
		}

		setSelectedFiles(prev => [...prev, ...newFiles]);
	}, [fileType, t]);

	const handleFileInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
		const files = Array.from(event.target.files || []);
		handleFilesSelect(files);

		if (fileInputRef.current) {
			fileInputRef.current.value = '';
		}
	};


	const handleDragOver = (event: DragEvent<HTMLDivElement>): void => {
		event.preventDefault();
		setIsDragging(true);
	};

	const handleDragLeave = (): void => {
		setIsDragging(false);
	};


	const handleDrop = (event: DragEvent<HTMLDivElement>): void => {
		event.preventDefault();
		setIsDragging(false);

		const files = Array.from(event.dataTransfer.files);
		handleFilesSelect(files);
	};

	const handleClearFile = async (fileId?: string): Promise<void> => {
		if (fileId) {
			const fileToClear = selectedFiles.find(f => f.uploadId === fileId || f.name === fileId);
			if (fileToClear?.preview) {
				URL.revokeObjectURL(fileToClear.preview);
			}

			if (fileToClear?.uploadId) {
				await cancelUpload(fileToClear.uploadId);
				isCancelledRef.current.set(fileToClear.uploadId, true);
				setActiveUploads(prev => {
					const newSet = new Set(prev);
					newSet.delete(fileToClear.uploadId!);
					return newSet;
				});
			}

			setSelectedFiles(prev => prev.filter(f =>
				f.uploadId !== fileId && f.name !== fileId
			));
		} else {
			selectedFiles.forEach(file => {
				if (file.preview) {
					URL.revokeObjectURL(file.preview);
				}
			});

			const cancelPromises = Array.from(activeUploads).map(uploadId =>
				cancelUpload(uploadId)
			);
			await Promise.allSettled(cancelPromises);

			isCancelledRef.current.clear();
			setActiveUploads(new Set());
			setSelectedFiles([]);
		}

		setError(null);
	};

	const uploadSingleFile = async (fileInfo: SelectedFile): Promise<void> => {
		const fileId = `${fileInfo.name}-${Date.now()}`;
		isCancelledRef.current.set(fileId, false);

		try {
			setSelectedFiles(prev => prev.map(f =>
				f.name === fileInfo.name ? { ...f, status: 'uploading' } : f
			));

			const extension = fileInfo.file.name.split('.').pop() || '';
			const initMessageBody = {
				file_name: fileInfo.file.name,
				file_type: fileType,
				extension: extension,
				file_size: fileInfo.file.size
			};

			const response = await fileUpload(initMessageBody) as FileUploadInitResponse;

			if (!response?.upload_id) {
				throw new Error('Failed to get upload_id');
			}

			setSelectedFiles(prev => prev.map(f =>
				f.name === fileInfo.name ? { ...f, uploadId: response.upload_id } : f
			));

			setActiveUploads(prev => new Set(prev).add(response.upload_id));

			const CHUNK_SIZE = response.chunk_size;
			const totalChunks = response.total_chunks;

			for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
				if (isCancelledRef.current.get(fileId)) {
					return;
				}

				const start = chunkIndex * CHUNK_SIZE;
				const end = Math.min(start + CHUNK_SIZE, fileInfo.file.size);
				const chunk = fileInfo.file.slice(start, end);
				const formData = new FormData();

				formData.append('upload_id', response.upload_id);
				formData.append('chunk_number', chunkIndex.toString());
				formData.append('chunk', chunk);

				const chunkResponse = await chunkUpload(formData);

				if (chunkResponse.errorMesage !== null) {
					throw chunkResponse.errorMesage;
				}

				const progress = Math.floor((chunkIndex + 1) / totalChunks * 100);
				setSelectedFiles(prev => prev.map(f =>
					f.name === fileInfo.name ? { ...f, progress } : f
				));
			}

			const chunkStatus = await checkChunkStatus(response.upload_id);
			if (chunkStatus.message_body.missing_chunks === 0) {
				setSelectedFiles(prev => prev.map(f =>
					f.name === fileInfo.name ? { ...f, status: 'completed', progress: 100 } : f
				));

				setActiveUploads(prev => {
					const newSet = new Set(prev);
					newSet.delete(response.upload_id);
					return newSet;
				});

				await queryClient.invalidateQueries({ queryKey: ['userFilesData'] });
				await queryClient.invalidateQueries({ queryKey: ['userFilesInfo'] });
				await queryClient.invalidateQueries({ queryKey: ['userData'] });
			} else {
				throw new Error('Not all chunks were uploaded');
			}

		} catch (error) {
			if (!isCancelledRef.current.get(fileId)) {
				const errorMessage = (() => {
					switch (error) {
						case 'duplicate-file':
							return t('error-duplicate-file');
						case 'user-not-have-tokens-for-protect':
							return t('error-user-not-have-tokens-for-protect');
						default:
							return t('error-uploading-file');
					}
				})();

				setSelectedFiles(prev => prev.map(f =>
					f.name === fileInfo.name ? { ...f, status: 'error', error: errorMessage } : f
				));
			}
		}
	};

	useEffect(() => {
		selectedFilesRef.current = selectedFiles;
	}, [selectedFiles]);

	const handleStartAllUploads = useCallback(async (): Promise<void> => {
		const uploadNextBatch = async () => {
			const currentPending = selectedFilesRef.current.filter(f => f.status === 'pending');
			const batch = currentPending.slice(0, maxParallelUploads);

			if (batch.length === 0) return;

			await Promise.all(batch.map(file => uploadSingleFile(file)));

			if (selectedFilesRef.current.some(f => f.status === 'pending')) {
				await uploadNextBatch();
			}
		};

		await uploadNextBatch();
	}, [maxParallelUploads]);

	const getTotalProgress = useMemo(() => {
		if (selectedFiles.length === 0) return 0;
		const total = selectedFiles.reduce((sum, file) => sum + file.progress, 0);
		return Math.floor(total / selectedFiles.length);
	}, [selectedFiles]);

	const completedCount = useMemo(() =>
		selectedFiles.filter(f => f.status === 'completed').length,
		[selectedFiles]
	);

	const uploadingCount = useMemo(() =>
		selectedFiles.filter(f => f.status === 'uploading').length,
		[selectedFiles]
	);


	/* 	useEffect(() => {
			// Обработка закрытия вкладки
			// Тут можно попробовать изучить вопрос с navigator.sendBeacon('', blob)
			// он нужен для того что бы послать запрос на бек при закрытии вкладки,
			// но у него есть свои особенности и стандартные запросы не работают.
			const handleUnload = () => {
				if (activeUploads.size) {
					//const data = JSON.stringify({ uploadId, reason: 'tab_closed' });
					//const blob = new Blob([data], { type: 'application/json' });
					//navigator.sendBeacon('/api/v1/data/cancel', blob);
					console.log('sendBeacon');
				}
			};
			if (activeUploads.size) {
				window.addEventListener('unload', handleUnload);
			}
	
			return () => {
				window.removeEventListener('unload', handleUnload);
			};
		}, [activeUploads.size]); */

	useNavigationBlocker({
		shouldBlock: !!activeUploads.size,
		message: t('have-unsaved-changes'),
		onConfirm: async () => {
			console.log('User confirmed navigation');
			if (activeUploads.size) {
				await handleClearFile();
			}
		},
		onCancel: () => {
			console.log('User cancelled navigation');
		}
	});


	return (
		<div className="upload-section" >
			<h3>
				{t('upload-for-protection', {
					fileType: t(fileType.toLocaleLowerCase())
				})}
			</h3>

			< div
				className={`drag-drop-zone ${isDragging ? 'dragging' : ''}`}
				onDragOver={handleDragOver}
				onDragLeave={handleDragLeave}
				onDrop={handleDrop}
			>
				<IconShieldAdd />
				<h4>
					{t('drag-the-file-here')}
				</h4>

				{
					fileType === "image" && (
						<p className="description" >
							{t('image-resolution')}: 100x100 - 10000x10000 {t('pixels')}
						</p>
					)
				}
				<p className="description" >
					{t('file-format')}: {acceptString}
				</p>
				< p className="description" >
					{t('file-size')}: {t('to')} {(maxFileSize / 1024 / 1024)} {t('mb')}
				</p>

				< input
					ref={fileInputRef}
					type="file"
					accept={acceptString}
					style={{ display: 'none' }}
					onChange={handleFileInputChange}
					aria-label="Выбор файла для защиты"
					multiple // Добавляем multiple для выбора нескольких файлов
				/>

				<button
					className="btn btn-primary"
					onClick={() => fileInputRef.current?.click()}
					type="button"
					disabled={uploadingCount > maxParallelUploads}
				>
					{t('select-files-to-protect')}
				</button>

				{
					error && (
						<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg" >
							<p className="text-red-600 font-medium" > {error} </p>
						</div>
					)
				}

				{
					selectedFiles.length > 0 && (
						<div className="mt-6" >
							<div className="selected-file-action-panel" >
								<div>
									<p className="selected-file-action-info" >
										<span>{t('total-files')}: {selectedFiles.length}</span>
										<span>{t('uploading')}: {uploadingCount}</span>
										<span>{t('completed')}: {completedCount}</span>
									</p>
									<div className="w-full bg-gray-200 rounded-full h-2.5 mt-2">
										<div
											className="bg-blue-600 h-2.5 rounded-full transition-all duration-300"
											style={{ width: `${getTotalProgress}%` }
											}
										>
										</div>
									</div>
								</div>

								<div className="flex gap-2">
									<button
										className={`btn btn-primary`}
										onClick={handleStartAllUploads}
										type="button"
										disabled={uploadingCount > 0 || selectedFiles.every(f => f.status !== 'pending')}
									>
										{t('upload-all')}
									</button>
									< button
										className="btn btn-primary"
										onClick={() => handleClearFile()}
										type="button"
									>
										{t('clear-all')}
									</button>
								</div>
							</div>

							<div className="selected-file-wrapper">
								{
									selectedFiles.map((file, index) => (
										<div
											key={`${file.name}-${index}`}
											className={`selected-file ${file.status === 'completed' ? 'done' : ''} ${file.status === 'error' ? 'error' : ''}`}
										>
											<div className="selected-file-file-info">
												<div>
													<p className="text-gray-700" >
														<span className="font-medium" > {t('file')}: </span> {file.name}
													</p>
													< p className="text-gray-700" >
														<span className="font-medium" > {t('size')}: </span> {file.size}
													</p>
													< p className="text-gray-700" >
														<span className="font-medium" > {t('status')}: </span>
														<span className={`ml-2 ${file.status === 'completed' ? 'text-green-600' :
															file.status === 'uploading' ? 'text-blue-600' :
																file.status === 'error' ? 'text-red-600' :
																	'text-gray-600'
															}`}>
															{
																file.status === 'completed' ? t('completed') :
																	file.status === 'uploading' ? t('uploading') :
																		file.status === 'error' ? t('error') :
																			t('pending')
															}
														</span>
													</p>
													{
														file.error && (
															<p className="text-red-600 text-sm mt-1" > {file.error} </p>
														)
													}
												</div>
												<div className="selected-file-right-block">
													<span className={
														`upload-progress ${file.status === 'completed' ? 'text-green-600' :
															file.status === 'uploading' ? 'text-blue-600' :
																'text-gray-600'
														}`
													}>
														{file.progress} %
													</span>
													< button
														className="btn btn-cancel"
														onClick={() => handleClearFile(file.uploadId || file.name)}
														type="button"
													>
														{t('remove')}
													</button>
												</div>
											</div>

											{
												(fileType === 'image' && file.preview) && (
													<img
														src={file.preview}
														alt="Предпросмотр загруженного изображения"
														className="uploaded-image"
														onError={() => setError('Не удалось загрузить превью изображения')
														}
													/>
												)}

											{
												file.status === 'pending' && (
													<div className="mt-2 text-center" >
														<button
															className="btn btn-confirm"
															onClick={() => uploadSingleFile(file)
															}
															type="button"
															disabled={uploadingCount >= maxParallelUploads}
														>
															{t('start-upload')}
														</button>
													</div>
												)}
										</div>
									))}
							</div>
						</div>
					)}
			</div>
		</div>
	);
}