package com.interplug.qcast.util;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jxls.exception.ParsePropertyException;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class ExcelUtil {

  /**
   * jxls을 이용한 엑셀데이터 축출
   *
   * @param request HttpServletRequest
   * @param response HttpServletResponse
   * @param map 엑셀 출력데이터
   * @param list 엑셀 출력 목록 데이터
   * @param templateFileName 템플릿 파일명
   * @throws ParsePropertyException
   * @throws InvalidFormatException
   */
  public byte[] download(
      HttpServletRequest request,
      HttpServletResponse response,
      Map<String, Object> map,
      List<Map<String, Object>> list,
      String templateFileName)
      throws ParsePropertyException, InvalidFormatException {

    byte[] excelBytes = null;

    try {

      String templateFilePath = "template/excel/" + templateFileName;
      InputStream templateStream =
          PdfUtil.class.getClassLoader().getResourceAsStream(templateFilePath);

      try (InputStream is = new BufferedInputStream(templateStream)) {
        Context context = new Context();
        context.putVar("data", map);
        context.putVar("list", list);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        JxlsHelper.getInstance().processTemplate(is, byteArrayOutputStream, context);

        excelBytes = byteArrayOutputStream.toByteArray();
      }
    } catch (Exception e) {
      log.debug(e.getMessage());
    }

    return excelBytes;
  }

  /**
   * jxls을 이용한 엑셀다운로드
   *
   * @param request HttpServletRequest
   * @param response HttpServletResponse
   * @param map 엑셀 출력데이터
   * @param list 엑셀 출력 목록 데이터
   * @param fileName 다운로드 파일명
   * @param templateFileName 템플릿 파일명
   * @throws ParsePropertyException
   * @throws InvalidFormatException
   */
  public void download(
      HttpServletRequest request,
      HttpServletResponse response,
      Map<String, Object> map,
      List<Map<String, Object>> list,
      String fileName,
      String templateFileName)
      throws ParsePropertyException, InvalidFormatException {
    try {

      String templateFilePath = "template/excel/" + templateFileName;
      InputStream templateStream =
          PdfUtil.class.getClassLoader().getResourceAsStream(templateFilePath);

      InputStream is = new BufferedInputStream(templateStream);

      response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
      response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");

      try (OutputStream os = response.getOutputStream()) {
        Context context = new Context();
        context.putVar("data", map);
        context.putVar("list", list);

        JxlsHelper.getInstance().processTemplate(is, os, context);
      }
    } catch (Exception e) {
      log.debug(e.getMessage());
    }
  }

  /**
   * Object => Map 변환 함수
   *
   * @param vo Object
   * @return Map<String, Object> Map 변환 정보
   */
  public Map<String, Object> convertVoToMap(Object vo) {
    Map<String, Object> result = new HashMap<String, Object>();

    try {
      BeanInfo info = Introspector.getBeanInfo(vo.getClass());
      for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
        Method reader = pd.getReadMethod();
        if (reader != null) {
          result.put(pd.getName(), reader.invoke(vo));
        }
      }
    } catch (Exception e) {
      log.error("convertVoToMap >>> " + e.getMessage());
    }
    return result;
  }

  /**
   * List<Object> => List<Map> 변환 함수
   *
   * @param target List<Object>
   * @return List<Map<String, Object>> List<Map> 변환 정보
   */
  public static <T> List<Map<String, Object>> convertListToMap(Collection<T> target) {
    List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();

    for (T element : target) {
      Map<String, Object> resultMap = new HashMap<String, Object>();
      Field[] fieldList = element.getClass().getDeclaredFields();
      if (fieldList != null && fieldList.length > 0) {
        try {
          for (int i = 0; i < fieldList.length; i++) {
            String curInsName = fieldList[i].getName();
            Field field = element.getClass().getDeclaredField(curInsName);
            field.setAccessible(true);
            Object targetValue = field.get(element);

            resultMap.put(curInsName, targetValue);
          }
          resultList.add(resultMap);
        } catch (Exception e) {
          log.error("convertListToMap >>> " + e.getMessage());
        }
      }
    }
    return resultList;
  }
}
