package com.interplug.qcast.biz.file;

import com.interplug.qcast.biz.file.dto.FileRequest;
import com.interplug.qcast.biz.file.dto.FileResponse;
import com.interplug.qcast.config.Exception.ErrorCode;
import com.interplug.qcast.config.Exception.QcastException;
import com.interplug.qcast.config.message.Messages;
import com.interplug.qcast.util.ZipFileManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {

  Messages message;

  private final FileMapper fileMapper;

  @Value("${file.ini.root.path}")
  private String baseDirPath;

  private ZipFileManager zipFileManager;

  /**
   * 파일 업로드
   *
   * @param request
   * @param fileRequest
   * @return
   * @throws Exception
   */
  public List<FileRequest> getUploadFileList(HttpServletRequest request, FileRequest fileRequest)
      throws Exception {
    List<FileRequest> saveFileList = new ArrayList<>();
    List<MultipartFile> multipartFileList = new ArrayList<>();

    if (!MultipartHttpServletRequest.class.isAssignableFrom(request.getClass())) {
      return new ArrayList<>();
    }

    MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
    Map<String, List<MultipartFile>> mapMultipart = multipartRequest.getMultiFileMap();
    if (mapMultipart.isEmpty()) {
      return null;
    }

    String strParamKey;
    for (String s : mapMultipart.keySet()) {
      strParamKey = s;
      multipartFileList.addAll(mapMultipart.get(strParamKey));
    }

    int iFileCnt = multipartFileList.size();
    if (iFileCnt > 0) {
      int iFileSeq = 1;

      String strSeparator = File.separator;
      String strFileFolderPath =
          baseDirPath + strSeparator + fileRequest.getObjectNo() + strSeparator;

      // planNo가 있는 경우
      if (fileRequest.getPlanNo() != null && !fileRequest.getPlanNo().isEmpty()) {
        strFileFolderPath += fileRequest.getPlanNo() + strSeparator;
      }

      log.info("### fileFolderPath : {}", strFileFolderPath);

      // 파일 폴더 생성
      makeFileFolder(strFileFolderPath);

      for (MultipartFile multipartFile : multipartFileList) {
        if (!multipartFile.isEmpty()) {

          // 파일 확장자 발췌 및 확장자 소문자 변환
          String strFileExt = multipartFile.getOriginalFilename();
          if (strFileExt != null && !strFileExt.isEmpty()) {
            strFileExt = strFileExt.substring(strFileExt.lastIndexOf(".") + 1).toLowerCase().trim();
          } else {
            strFileExt = "";
          }

          // 파일 확장자 및 사이즈 검증
          List<String> listExtension = this.getFileExtension();
          if (!validFileExtension(listExtension, strFileExt)) {
            multipartFile.getInputStream().close();
            continue;
          }

          String strSrcFileNm = multipartFile.getOriginalFilename();
          File file = new File(strFileFolderPath, strSrcFileNm);

          multipartFile.transferTo(file);

          FileRequest fileSaveReq = new FileRequest();
          fileSaveReq.setObjectNo(fileRequest.getObjectNo());
          fileSaveReq.setPlanNo(fileRequest.getPlanNo());
          fileSaveReq.setCategory(fileRequest.getCategory());
          fileSaveReq.setFaileName(strSrcFileNm);
          fileSaveReq.setUserId(fileRequest.getUserId());
          saveFileList.add(fileSaveReq);

          iFileSeq++;
        }
      }
    }

    return saveFileList;
  }

  /**
   * 파일 확장자 검증
   *
   * @param listExtension
   * @param strFileExt
   * @return
   * @throws Exception
   */
  private boolean validFileExtension(List<String> listExtension, String strFileExt)
      throws Exception {
    boolean bResult = true;

    try {

      if (strFileExt == null || strFileExt.isEmpty()) {
        return false;
      }

      for (String s : listExtension) {
        strFileExt = strFileExt.toLowerCase();

        // 업로드 가능한 파일 확장자 검증
        if (s.equals(strFileExt)) {
          return true;
        }
      }

    } catch (Exception e) {
      log.error("### FileService - validFileExtension : Exception Error");
      return false;
    }

    return bResult;
  }

  // 업로드 가능한 파일 확장자 조회
  public List<String> getFileExtension() {
    return Arrays.asList(
        "mp3", "wma", "wav", "m4a", "3gp", "hwp", "doc", "docx", "ppt", "pptx", "zip", "xls",
        "xlsx", "pdf", "txt", "jpg", "gif", "png");
  }

  // 파일 폴더 생성
  private void makeFileFolder(String strMakeDir) {
    try {
      File dir = new File(strMakeDir.replace("//", "/"));
      if (!dir.exists()) {
        if (!dir.mkdirs()) {
          log.error("### Failed to create directories: {}", strMakeDir);
          throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
        }
      }
    } catch (Exception e) {
      log.error("### FileService - makeFileFolder : Exception Error");
    }
  }

  // 파일정보 저장 및 삭제
  public Integer setFile(List<FileRequest> saveFilelist, List<FileRequest> deleteFilelist)
      throws Exception {

    Integer iResult = 0;

    // 파일 삭제
    if (deleteFilelist != null && !deleteFilelist.isEmpty()) {
      for (FileRequest fileDeleteReq : deleteFilelist) {
        if (!"".equals(fileDeleteReq.getObjectNo())) {
          fileMapper.deleteFile(fileDeleteReq); // 파일 삭제
        }
      }
    }

    // 파일 저장
    if (saveFilelist != null && !saveFilelist.isEmpty()) {
      for (FileRequest fileInsertReq : saveFilelist) {
        if (!"".equals(fileInsertReq.getObjectNo()) && !"".equals(fileInsertReq.getCategory())) {
          iResult += fileMapper.insertFile(fileInsertReq);
        }
      }
    }

    return iResult;
  }

  /**
   * 파일 1건 다운로드
   *
   * @param request
   * @param response
   * @param fileRequest
   * @throws Exception
   */
  public void downloadFile(
      HttpServletRequest request, HttpServletResponse response, FileRequest fileRequest)
      throws Exception {

    // 필수값 체크
    if (fileRequest.getObjectNo() == null || fileRequest.getObjectNo().isEmpty()) {
      throw new QcastException(
          ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    } else if (fileRequest.getNo() == 0) {
      throw new QcastException(
          ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "No"));
    }

    // 파일정보 조회
    FileResponse fileResponse = fileMapper.selectFile(fileRequest);
    if (fileResponse == null) {
      throw new QcastException(
          ErrorCode.NOT_FOUND, message.getMessage(" common.message.file.download.exists"));
    }

    // 첨부파일 물리적 경로
    String strSeparator = File.separator;
    String filePath = baseDirPath + strSeparator + fileResponse.getObjectNo();
    if (fileResponse.getPlanNo() != null && !fileResponse.getPlanNo().isEmpty()) {
      filePath += strSeparator + fileResponse.getPlanNo();
    }
    filePath += strSeparator + fileResponse.getFaileName();

    File file = new File(filePath);
    if (!file.exists()) {
      log.error("### File not found: {}", filePath);
      throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    String mimeType = URLConnection.guessContentTypeFromName(file.getName());
    if (mimeType == null) {
      mimeType = "application/octet-stream";
    }

    String originalFileName = fileResponse.getFaileName();
    String encodedFileName =
        URLEncoder.encode(originalFileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");

    response.setHeader("Content-Transfer-Encoding", "binary;");
    response.setHeader("Pragma", "no-cache;");
    response.setHeader("Expires", "-1;");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
    response.setContentType(mimeType);
    response.setContentLength((int) file.length());

    try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
      FileCopyUtils.copy(inputStream, response.getOutputStream());
    } catch (IOException e) {
      throw new QcastException(
          ErrorCode.INTERNAL_SERVER_ERROR,
          message.getMessage("common.message.file.download.error"));
    }
  }

  /**
   * 파일 N건 ZIP 다운로드
   *
   * @param request
   * @param response
   * @param fileRequest
   * @throws Exception
   */
  public void downloadZipFile(
      HttpServletRequest request, HttpServletResponse response, FileRequest fileRequest)
      throws Exception {

    // 필수값 체크
    if (fileRequest.getObjectNo() == null || fileRequest.getObjectNo().isEmpty()) {
      throw new QcastException(
          ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    }

    String strZipFileName = fileRequest.getZipFileName();
    if (strZipFileName == null || strZipFileName.isEmpty()) {
      strZipFileName = "Download";
    }

    // 파일 조회
    List<FileResponse> fileList = fileMapper.selectFileList(fileRequest);
    if (fileList == null || fileList.size() == 0) {
      throw new QcastException(
          ErrorCode.NOT_FOUND, message.getMessage(" common.message.file.download.exists"));
    }

    // 첨부파일 물리적 경로
    FileResponse fileResponse = fileList.get(0);
    String zipFilePath = fileResponse.getObjectNo();
    String filePath = baseDirPath + File.separator + fileResponse.getObjectNo() + File.separator;
    if (fileResponse.getPlanNo() != null && !fileResponse.getPlanNo().isEmpty()) {
      zipFilePath += File.separator + fileResponse.getPlanNo();
      filePath += fileResponse.getPlanNo() + File.separator;
    }

    String finalFilePath = filePath;
    String finalZipFilePath = zipFilePath;
    List<Map<String, String>> listFile =
        fileList.stream()
            .map(
                file -> {
                  Map<String, String> map = new HashMap<>();
                  map.put("directory", finalZipFilePath);
                  map.put("filename", finalFilePath + file.getFaileName());
                  return map;
                })
            .collect(Collectors.toList());

    // zip 파일로 변환
    zipFileManager.createZipFile(response, strZipFileName, listFile);
  }

  public Integer deleteFile(FileRequest fileRequest) throws Exception {
    Integer iResult = 0;

    // 첨부파일 논리적 삭제
    iResult = fileMapper.deleteFile(fileRequest); // 파일 삭제

    return iResult;
  }

  public Integer fileUpload(HttpServletRequest request, FileRequest fileRequest) throws Exception {
    List<FileRequest> saveFileList = this.getUploadFileList(request, fileRequest);
    Integer resultCnt = this.setFile(saveFileList, null);
    return resultCnt;
  }
}
