package egovframework.util.excel;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@Slf4j
public class ExcelHandler<T> {

	public List<T> handleExcelUpload(List<MultipartFile> mFiles, Class<T> clazz) {
		List<T> dataList = new ArrayList<>();

		mFiles.forEach(file -> {
			try (InputStream inputStream = file.getInputStream()) {
				dataList.addAll(parseExcel(inputStream, clazz));
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		});

		return dataList;
	}

	private List<T> parseExcel(InputStream inputStream, Class<T> clazz) throws IOException {
		Workbook workbook = new XSSFWorkbook(inputStream);
		Sheet sheet = workbook.getSheetAt(0); // 첫 번째 시트를 가져옴

		// 헤더 정보 추출
		Row headerRow = sheet.getRow(0);
		List<String> headers = StreamSupport.stream(headerRow.spliterator(), false)
			.map(Cell::getStringCellValue)
			.collect(Collectors.toList());

		List<T> dataList = StreamSupport.stream(sheet.spliterator(), false)
			.skip(1) // 첫 번째 행은 헤더이므로 건너뜁니다.
			.filter(this::isRowNotEmpty) // 빈 행이 아닌 경우에만 처리합니다.
			.map(row -> mapRowToDto(row, clazz, headers))
			.collect(Collectors.toList());

		workbook.close();
		return dataList;
	}

	private boolean isRowNotEmpty(Row row) {
		Iterator<Cell> cellIterator = row.cellIterator();
		while (cellIterator.hasNext()) {
			Cell cell = cellIterator.next();
			if (cell.getCellType() != CellType.BLANK) {
				return true; // 빈 셀이 아닌 경우에만 true를 반환합니다.
			}
		}
		return false; // 모든 셀이 비어 있으면 false를 반환합니다.
	}

	private T mapRowToDto(Row row, Class<T> clazz, List<String> excelHeaderList) {
		T dataDto;
		try {
			dataDto = clazz.getDeclaredConstructor().newInstance();

			Iterator<Cell> cellIterator = row.cellIterator();
			while (cellIterator.hasNext()) {
				Cell cell = cellIterator.next();
				String excelHeaderName = excelHeaderList.get(cell.getColumnIndex());

				//각 필드를 순회하며 커스텀 어노테이션인 ExcelHeader값에 맞게 값을 넣어줌
				Field[] dtoFields = clazz.getDeclaredFields();
				for (Field field : dtoFields) {
					if (field.isAnnotationPresent(ExcelColumn.class)) {
						ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
						if (Objects.requireNonNull(annotation).headerName().equals(excelHeaderName)) {
							field.setAccessible(true);
							setFieldValue(field, dataDto, cell);
							break;
						}
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return dataDto;
	}

	private void setFieldValue(Field field, T dataDto, Cell cell) throws IllegalAccessException {
		Class<?> fieldType = field.getType();
		field.setAccessible(true);

		if (fieldType == int.class || fieldType == Integer.class) {
			field.set(dataDto, (int)cell.getNumericCellValue());
		} else if (fieldType == long.class || fieldType == Long.class) {
			field.set(dataDto, (long)cell.getNumericCellValue());
		} else if (fieldType == double.class || fieldType == Double.class) {
			field.set(dataDto, cell.getNumericCellValue());
		} else if (fieldType == boolean.class || fieldType == Boolean.class) {
			field.set(dataDto, cell.getBooleanCellValue());
		} else {
			// if (fieldType == String.class) {
			DataFormatter formatter = new DataFormatter();
			field.set(dataDto, formatter.formatCellValue(cell));
		}
		// 다른 타입에 따른 맵핑을 추가할 수 있습니다.
	}

}
