package egovframework.util.excel;

import org.checkerframework.checker.nullness.qual.Nullable;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 다중 시트 엑셀 파일 생성 클래스
 *
 * <p>이 클래스는 {@link BaseSxssfExcelFile}을 상속하여 여러 시트를 포함하는 엑셀 파일을 생성합니다.
 * 하나의 엑셀 파일에 여러 종류의 데이터를 각각 다른 시트로 구성할 수 있습니다.</p>
 *
 * <p><b>사용 예제 1: 다중 시트 엑셀 다운로드</b></p>
 * <pre>{@code
 * @GetMapping("/download-multi.xlsx")
 * public void downloadMultiSheetExcel(HttpServletResponse response) throws IOException {
 *     // 시트 1: 사용자 목록
 *     List<UserVO> userList = userService.selectUserList();
 *     ExcelSheetData userSheet = ExcelSheetData.of(userList, UserVO.class, "사용자 목록");
 *
 *     // 시트 2: 부서 목록
 *     List<DeptVO> deptList = deptService.selectDeptList();
 *     ExcelSheetData deptSheet = ExcelSheetData.of(deptList, DeptVO.class, "부서 목록");
 *
 *     // 다중 시트 그룹 생성
 *     ExcelSheetDataGroup dataGroup = ExcelSheetDataGroup.of(
 *         List.of(userSheet, deptSheet)
 *     );
 *
 *     // 파일 다운로드
 *     response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
 *     response.setHeader("Content-Disposition", "attachment; filename=\"통합리포트.xlsx\"");
 *     new SxssfMultiSheetExcelFile(dataGroup, response);
 * }
 * }</pre>
 *
 * <p><b>사용 예제 2: 암호화된 다중 시트 엑셀 다운로드</b></p>
 * <pre>{@code
 * @GetMapping("/download-multi-encrypted.xlsx")
 * public void downloadEncryptedMultiSheetExcel(HttpServletResponse response) throws IOException {
 *     ExcelSheetDataGroup dataGroup = createMultiSheetData();
 *
 *     response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
 *     response.setHeader("Content-Disposition", "attachment; filename=\"통합리포트.xlsx\"");
 *     new SxssfMultiSheetExcelFile(dataGroup, response, "password123");
 * }
 * }</pre>
 *
 * <p><b>주의사항:</b></p>
 * <ul>
 *   <li>각 시트는 서로 다른 VO 클래스를 사용할 수 있습니다.</li>
 *   <li>각 VO 클래스마다 {@link ExcelSheet} 어노테이션으로 시트명을 지정해야 합니다.</li>
 *   <li>시트가 많을수록 메모리 사용량이 증가할 수 있습니다.</li>
 * </ul>
 *
 * @see BaseSxssfExcelFile
 * @see ExcelSheetDataGroup
 * @see ExcelSheetData
 * @author eGovFrame
 */
public class SxssfMultiSheetExcelFile extends BaseSxssfExcelFile {

	// ==================== 생성자 ====================

	/**
	 * HTTP 응답으로 다중 시트 엑셀 파일을 다운로드합니다 (암호화 없음).
	 *
	 * <p>이 생성자는 암호화 없이 다중 시트 엑셀 파일을 HTTP 응답으로 다운로드합니다.</p>
	 *
	 * @param dataGroup 다중 시트 데이터 그룹
	 * @param response HTTP 응답 객체
	 * @throws IOException 파일 생성 또는 출력 중 오류 발생 시
	 */
	public SxssfMultiSheetExcelFile(ExcelSheetDataGroup dataGroup, HttpServletResponse response)
			throws IOException {
		this(dataGroup, response, null);
	}

	/**
	 * HTTP 응답으로 다중 시트 엑셀 파일을 다운로드합니다 (암호화 지원).
	 *
	 * <p>이 생성자는 선택적으로 암호화하여 다중 시트 엑셀 파일을 HTTP 응답으로 다운로드합니다.</p>
	 *
	 * @param dataGroup 다중 시트 데이터 그룹
	 * @param response HTTP 응답 객체
	 * @param password 암호화 비밀번호 (null이면 암호화하지 않음)
	 * @throws IOException 파일 생성 또는 출력 중 오류 발생 시
	 */
	public SxssfMultiSheetExcelFile(ExcelSheetDataGroup dataGroup, HttpServletResponse response,
									 @Nullable String password) throws IOException {
		renderAllSheets(dataGroup);
		writeWithEncryption(response.getOutputStream(), password);
	}

	// ==================== private 헬퍼 메서드 ====================

	/**
	 * 모든 시트를 렌더링합니다.
	 *
	 * <p>데이터 그룹에 포함된 각 시트 데이터를 순회하면서 개별 시트를 생성합니다.
	 * 각 시트마다 제목, 헤더, 데이터 행을 렌더링하고 새로운 시트로 이동합니다.</p>
	 *
	 * @param dataGroup 다중 시트 데이터 그룹
	 */
	private void renderAllSheets(ExcelSheetDataGroup dataGroup) {
		for (ExcelSheetData data : dataGroup.getExcelSheetData()) {
			// 현재 시트 참조 초기화 (새 시트 생성 준비)
			resetSheetContext();

			// 메타데이터 생성
			ExcelMetadata metadata = ExcelMetadataFactory.getInstance().createMetadata(data.getType());

			// 시트 내용 렌더링 (제목, 헤더, 데이터)
			renderSheetContent(data, metadata);
		}
	}

	/**
	 * 시트 컨텍스트를 초기화합니다.
	 *
	 * <p>새로운 시트 생성을 위해 현재 시트 참조와 제목 행 오프셋을 초기화합니다.
	 * 이렇게 하면 renderSheetContent 메서드가 새로운 시트를 생성하게 됩니다.</p>
	 */
	private void resetSheetContext() {
		sheet = null;
		titleRowOffset = 0;
	}
}
