import * as XLSX from 'xlsx-js-style';
import { ExcelDownButton } from '../../../styles/ModuleComponents';
import { useCallback, useEffect, useState } from 'react';

const ExcelDownloadButton = ({ tableRef, data, fileName = 'download.xlsx', sheetName = 'Sheet1', onLoadingChange }) => {
	const [isDownloading, setIsDownloading] = useState(false);
	const [lastProgress, setLastProgress] = useState(0);

	// 타임아웃 감지 및 처리
	useEffect(() => {
		let timeoutTimer;

		if (isDownloading && lastProgress >= 95) {
			// 최종 단계에서 타임아웃 감지 타이머 설정
			timeoutTimer = setTimeout(() => {
				// 진행 상태가 여전히 변하지 않았다면 타임아웃으로 간주
				if (isDownloading && lastProgress >= 95) {
					console.log("Excel download timeout detected, completing process");
					setIsDownloading(false);
					if (onLoadingChange) {
						onLoadingChange({ loading: false, progress: 100 });
					}
				}
			}, 15000); // 15초 타임아웃
		}

		return () => {
			if (timeoutTimer) clearTimeout(timeoutTimer);
		};
	}, [isDownloading, lastProgress, onLoadingChange]);

	const isNumeric = (value) => {
		// 숫자 또는 숫자 문자열인지 확인
		return !isNaN(value) && !isNaN(parseFloat(value));
	};

	// 테두리 스타일 정의
	const borderStyle = {
		style: "thin",
		color: { rgb: "000000" }
	};

	// 기본 셀 스타일
	const baseCellStyle = {
		font: {
			name: "맑은 고딕",
			sz: 11
		},
		border: {
			top: borderStyle,
			bottom: borderStyle,
			left: borderStyle,
			right: borderStyle
		}
	};

	// 헤더 스타일
	const headerStyle = {
		...baseCellStyle,
		font: {
			...baseCellStyle.font,
			bold: true
		},
		alignment: {
			horizontal: 'center',
			vertical: 'center'
		},
		fill: {
			fgColor: { rgb: "d9e1f2" },
			patternType: "solid"
		}
	};

	// 기본 데이터 셀 스타일
	const dataStyle = {
		...baseCellStyle,
		alignment: {
			horizontal: 'left',
			vertical: 'center',
			wrapText: true
		}
	};

	const flattenObject = (obj, prefix = '') => {
		return Object.keys(obj).reduce((acc, key) => {
			const prefixedKey = prefix ? `${prefix}.${key}` : key;

			if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
				Object.assign(acc, flattenObject(obj[key], prefixedKey));
			} else if (Array.isArray(obj[key])) {
				// 배열은 JSON 문자열로 변환
				acc[prefixedKey] = JSON.stringify(obj[key]);
			} else {
				acc[prefixedKey] = obj[key];
			}

			return acc;
		}, {});
	};

	const updateLoadingState = (newProgress) => {
		setLastProgress(newProgress);
		if (onLoadingChange && typeof onLoadingChange === 'function') {
			onLoadingChange({loading: true, progress: newProgress});
		}
	};

	const downloadTableExcel = async () => {
		return new Promise((resolve, reject) => {
			try {
				if (!tableRef || !tableRef.current) {
					reject(new Error('테이블 참조가 없습니다.'));
					return;
				}

				// Worker에 전달할 데이터 추출
				updateLoadingState(10);

				// 메인 스레드에서 데이터 추출
				const tableElement = tableRef.current;
				const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
				const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');

				// 일반 행만 포함 (상세 행 제외)
				const normalBodyRows = Array.from(bodyRows).filter(row => {
					const hasTdWithColspan = Array.from(row.cells).some(cell => cell.hasAttribute('colspan'));
					return !hasTdWithColspan;
				});

				// 헤더 데이터 추출
				const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);

				// 바디 데이터 추출 및 숫자 타입 처리
				const bodyData = normalBodyRows.map(row =>
					Array.from(row.cells).map(cell => {
						const value = cell.textContent;
						return isNumeric(value) ? parseFloat(value) : value;
					})
				);

				updateLoadingState(30);

				// 큰 데이터셋 처리를 위해 setTimeout으로 이벤트 루프 차단 방지
				setTimeout(() => {
					try {
						// 워크북 생성
						const wb = XLSX.utils.book_new();
						updateLoadingState(50);

						// 처리는 여러 단계로 나누어 이벤트 루프 차단 최소화
						setTimeout(() => {
							try {
								// 데이터에 스타일 적용
								const wsData = [
									// 헤더 행
									headers.map(h => ({
										v: h,
										s: headerStyle
									}))
								];

								// 데이터 행 추가 (메모리 사용량 최소화를 위해 별도 처리)
								const chunkSize = 1000; // 한 번에 처리할 행 수
								let currentIndex = 0;

								function processDataChunk() {
									updateLoadingState(50 + Math.floor((currentIndex / bodyData.length) * 30));

									const end = Math.min(currentIndex + chunkSize, bodyData.length);
									for (let i = currentIndex; i < end; i++) {
										wsData.push(
											bodyData[i].map(cell => ({
												v: cell,
												s: dataStyle
											}))
										);
									}

									currentIndex = end;

									if (currentIndex < bodyData.length) {
										// 아직 처리할 데이터가 남아있으면 다음 청크 처리 예약
										setTimeout(processDataChunk, 0);
									} else {
										// 모든 데이터 처리 완료 후 워크시트 생성
										finishExcelCreation(wsData, headers, wb);
									}
								}

								// 첫 번째 청크 처리 시작
								processDataChunk();

							} catch (error) {
								reject(error);
							}
						}, 0);

						function finishExcelCreation(wsData, headers, wb) {
							try {
								updateLoadingState(80);

								// 워크시트 생성
								const ws = XLSX.utils.aoa_to_sheet(wsData);

								// 열 너비 설정 (최소 8, 최대 50)
								ws['!cols'] = headers.map((_, index) => {
									// 데이터의 일부만 샘플링하여 열 너비 계산 (전체 계산 시 성능 문제)
									const samplingSize = Math.min(bodyData.length, 500);
									const sampledRows = [];

									for (let i = 0; i < samplingSize; i++) {
										const randomIndex = Math.floor(Math.random() * bodyData.length);
										sampledRows.push(bodyData[randomIndex]);
									}

									const maxLength = Math.max(
										headers[index].length * 2,
										...sampledRows.map(row => {
											if (row[index] === undefined) return 0;
											return String(row[index] || '').length * 1.2;
										})
									);

									return { wch: Math.max(8, Math.min(50, maxLength)) };
								});

								updateLoadingState(90);

								// 최종 다운로드는 별도 타임아웃에서 수행하여 UI 업데이트 가능하게 함
								setTimeout(() => {
									try {
										// 워크시트를 워크북에 추가
										XLSX.utils.book_append_sheet(wb, ws, sheetName);

										updateLoadingState(95);

										// 파일 다운로드 전 마지막 UI 업데이트를 위한 지연
										setTimeout(() => {
											try {
												// 엑셀 파일 다운로드
												XLSX.writeFile(wb, fileName);
												updateLoadingState(100);
												resolve();
											} catch (error) {
												reject(error);
											}
										}, 100);
									} catch (error) {
										reject(error);
									}
								}, 0);
							} catch (error) {
								reject(error);
							}
						}

					} catch (error) {
						reject(error);
					}
				}, 0);

			} catch (error) {
				reject(error);
			}
		});
	};

	const chunkArray = (array, chunkSize) => {
		const chunks = [];
		for (let i = 0; i < array.length; i += chunkSize) {
			chunks.push(array.slice(i, i + chunkSize));
		}
		return chunks;
	};

	const processDataChunk = async (chunk, headers, allDataRows, processedCount, totalCount) => {
		return new Promise((resolve) => {
			setTimeout(() => {
				// 각 청크의 데이터를 처리
				const rowsData = chunk.map(item => {
					return headers.map(header => {
						const value = item[header] !== undefined ? item[header] : '';
						return {
							v: value,
							s: dataStyle
						};
					});
				});

				// 진행률 계산 및 콜백 호출
				const newProgress = Math.min(95, Math.round(((processedCount + chunk.length) / totalCount) * 80) + 15);
				updateLoadingState(newProgress);

				// 처리된 데이터 행들을 전체 데이터 배열에 추가
				allDataRows.push(...rowsData);
				resolve();
			}, 0);
		});
	};

	const downloadDataExcel = async () => {
		return new Promise(async (resolve, reject) => {
			try {
				if (!data || data.length === 0) {
					reject(new Error('다운로드할 데이터가 없습니다.'));
					return;
				}

				updateLoadingState(5);

				// 데이터 플랫 변환 과정을 더 작은 청크로 나누기
				const dataChunkSize = 2000; // 한 번에 처리할 데이터 아이템 수
				const dataChunks = chunkArray(data, dataChunkSize);
				let flattenedData = [];

				for (let i = 0; i < dataChunks.length; i++) {
					await new Promise(resolve => {
						setTimeout(() => {
							// 청크 내 아이템들을 플랫하게 변환
							const chunkData = dataChunks[i].map(item => {
								// 기본 필드
								const baseData = {
									'logTime': item.logTime,
									'GUID': item.userGuid === 'None' ? '' : item.userGuid,
									'Nickname': item.userNickname === 'None' ? '' : item.userNickname,
									'Account ID': item.accountId === 'None' ? '' : item.accountId,
									'Action': item.action,
									'Domain': item.domain === 'None' ? '' : item.domain,
									'Tran ID': item.tranId
								};

								// Actor 데이터 플랫하게 추가
								const actorData = item.header && item.header.Actor ?
									flattenObject(item.header.Actor, 'Actor') : {};

								// Infos 데이터 플랫하게 추가
								let infosData = {};
								if (item.body && item.body.Infos && Array.isArray(item.body.Infos)) {
									item.body.Infos.forEach((info) => {
										infosData = {
											...infosData,
											...flattenObject(info, `Info`)
										};
									});
								}

								return {
									...baseData,
									...actorData,
									...infosData
								};
							});

							flattenedData = [...flattenedData, ...chunkData];
							const progress = 5 + Math.floor((i + 1) / dataChunks.length * 10);
							updateLoadingState(progress);
							resolve();
						}, 0);
					});
				}

				// 모든 항목의 모든 키 수집하여 헤더 생성
				const allKeys = new Set();

				// 헤더 수집도 청크로 나누기
				for (let i = 0; i < flattenedData.length; i += dataChunkSize) {
					await new Promise(resolve => {
						setTimeout(() => {
							const end = Math.min(i + dataChunkSize, flattenedData.length);
							for (let j = i; j < end; j++) {
								Object.keys(flattenedData[j]).forEach(key => allKeys.add(key));
							}
							const progress = 15 + Math.floor((i + dataChunkSize) / flattenedData.length * 5);
							updateLoadingState(progress);
							resolve();
						}, 0);
					});
				}

				const headers = Array.from(allKeys);
				updateLoadingState(20);

				// 워크북 생성
				const wb = XLSX.utils.book_new();

				// 헤더 행 생성
				const headerRow = headers.map(h => ({
					v: h,
					s: headerStyle
				}));

				// 청크로 데이터 나누기
				const chunkSize = 500; // 한 번에 처리할 행의 수 (더 작게 조정)
				const rowChunks = chunkArray(flattenedData, chunkSize);
				const allDataRows = [];

				// 각 청크 처리
				let processedCount = 0;
				for (const chunk of rowChunks) {
					await processDataChunk(chunk, headers, allDataRows, processedCount, flattenedData.length);
					processedCount += chunk.length;

					// 메모리 정리를 위한 가비지 컬렉션 힌트
					if (processedCount % (chunkSize * 10) === 0) {
						// 5000행마다 짧은 지연을 두어 가비지 컬렉션 기회 제공
						await new Promise(resolve => setTimeout(resolve, 10));
					}
				}

				updateLoadingState(95);

				// 워크시트 데이터 구성 및 파일 다운로드를 메인 로직과 분리
				setTimeout(() => {
					try {
						// 워크시트 데이터 구성
						const wsData = [
							// 헤더 행
							headerRow,
							// 데이터 행들
							...allDataRows
						];

						// 워크시트 생성 (메모리 최적화)
						const ws = XLSX.utils.aoa_to_sheet(wsData);

						// 열 너비 설정 (성능 최적화를 위해 샘플링)
						ws['!cols'] = headers.map((header) => {
							// 헤더 길이와 샘플 데이터 길이를 기준으로 열 너비 결정
							const sampleSize = Math.min(flattenedData.length, 500);
							const samples = [];

							for (let i = 0; i < sampleSize; i++) {
								const randomIndex = Math.floor(Math.random() * flattenedData.length);
								const item = flattenedData[randomIndex];
								if (item[header] !== undefined) {
									samples.push(String(item[header]).length * 1.2);
								}
							}

							const maxLength = Math.max(
								header.length * 1.5,
								...samples
							);

							return { wch: Math.max(10, Math.min(50, maxLength)) };
						});

						// 최종 단계 분리
						setTimeout(() => {
							try {
								// 워크시트를 워크북에 추가
								XLSX.utils.book_append_sheet(wb, ws, sheetName);

								// 엑셀 파일 다운로드
								XLSX.writeFile(wb, fileName);
								updateLoadingState(100);

								resolve();
							} catch (error) {
								reject(error);
							}
						}, 100);
					} catch (error) {
						reject(error);
					}
				}, 0);

			} catch (error) {
				reject(error);
			}
		});
	};

	const handleDownload = useCallback(async () => {
		if (isDownloading) return; // 이미 다운로드 중이면 중복 실행 방지

		setIsDownloading(true);
		setLastProgress(0);
		if (onLoadingChange) onLoadingChange({loading: true, progress: 0});

		try {
			if (tableRef) {
				await downloadTableExcel();
			} else if (data) {
				await downloadDataExcel();
			} else {
				alert('유효한 데이터 소스가 없습니다.');
			}
		} catch (error) {
			console.error('Excel download failed:', error);
			alert('엑셀 다운로드 중 오류가 발생했습니다.');
		} finally {
			// 다운로드 완료 후 짧은 지연 시간을 두어 100% 상태를 잠시 보여줌
			setTimeout(() => {
				setIsDownloading(false);
				if (onLoadingChange) onLoadingChange({loading: false, progress: 100});
			}, 500);
		}
	}, [tableRef, data, fileName, sheetName, isDownloading, onLoadingChange]);

	return (
		<ExcelDownButton onClick={handleDownload} disabled={isDownloading}>
			{isDownloading ? '다운로드 중...' : '엑셀 다운로드'}
		</ExcelDownButton>
	);
};

export default ExcelDownloadButton;