package nlib.cmm.fileupload;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import nlib.bbs.service.AttachFileService;
import nlib.bbs.service.AttachFileVO;
import nlib.bbs.service.QnaService;
import nlib.cmm.NlibCommonController;
import nlib.cmm.exception.ErrorMessage;
import nlib.cmm.service.NlibProperty;
import nlib.util.FileUtil;
import nlib.util.StringUtil;
import nlib.util.UUID;

/**
 * <pre>
 * @Class Name : FileUploadController.java
 * 
 * @Description : 파일 업로드를 처리하는 컨트롤러  
 * 
 * 
 * @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
 *
 * </pre>
 *
 * @ ------------    --------    ---------------------------
 * @ 수정일                     수정자             수정내용
 * @ ------------    --------    ---------------------------
 * @ 2021. 7. 15.    KNKIM       최초 생성
 *
 * 
 * @author 이씨플라자 * DIGITALSHIP KNKIM
 * @since 2021. 7. 15.
 * @version 1.0
 *
 */
@Controller
public class FileUploadController extends NlibCommonController {

	private static final Logger log = LoggerFactory.getLogger(FileUploadController.class);

	
	/**
	 * 첨부파일이 저장되는 최상위 위치 
	 */
	private String FILEUPLOAD_BASE_PATH = NlibProperty.getProperty("fileupload.base.path");
	
	/**
	 * 첨부파일 임시 저장 위치 
	 */
	private String FILEUPLOAD_TEMP_SUBPATH = NlibProperty.getProperty("fileupload.temp.subpath");

	/**
	 * 경로 부적합 오류 메시지 
	 */
	private static final String MSG_NOT_VALID_FILE_PATH = "파일 경로가 부적합합니다. 관리자에게 문의하여 주시기 바랍니다."; 

	
	@Resource(name="attachFileService")
	private AttachFileService attachFileService;
	
	/**
	 * 파일업로드(Ajax) 처리
	 * - 화면 : Javascript Free Open Framework DropZone 사용 
	 * 
	 * @param multiRequest
	 * @param subPathKey
	 * @param response : 파일정보를 담은 JSON String  
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value="/fileupload/uploadFilesAjax.do", produces="application/json")
	public ResponseEntity uploadFilesAjax(
			final MultipartHttpServletRequest multiRequest
			, @RequestParam(name="subPathKey") String subPathKey
			, @RequestParam(name="attachFileId", required=false) String attachFileId
			, HttpServletResponse response) {
		
		String message = null;
		
		// TODO : 권한 체크 추가할 것 
		List<AttachFileVO> result = new ArrayList<AttachFileVO>();
		
		try {

			// 최상위 위치 
			if(FileUtil.isEmpty(FILEUPLOAD_BASE_PATH)) {
				message = "첨부파일이 저장되는 최상위 위치정보값이 정확하지 않습니다.";
				log.error(message + " : FILEUPLOAD_BASE_PATH 값 부재");
				ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
				return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
			}
			
			// 서브 위치 
			if(StringUtil.isEmpty(subPathKey)) {
				message = "첨부파일이 저장되는 하위 위치정보값이 정확하지 않습니다.";
				log.error(message + " : 매개변수 subPathKey 값 부재");
				ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
				return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
			}
			String subPath = NlibProperty.getProperty(subPathKey);
			if(StringUtil.isEmpty(subPath)) {
				message = "첨부파일이 저장되는 하위 위치정보값을 찾을 수 없습니다.";
				log.error(message + " : 매개변수 subPathKey의 속성값 부재");
				ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
				return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
			}
			
			// 위치정보 적합 확인 
			if(FileUtil.isNotValid(FILEUPLOAD_BASE_PATH + subPath)) {
				message = MSG_NOT_VALID_FILE_PATH;
				log.error(message + " : " + (FILEUPLOAD_BASE_PATH + subPath));
				ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
				return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
			}
			
			// 파일 업로드 처리 
			final Map<String, MultipartFile> files = multiRequest.getFileMap();
			log.debug("uploadFilesAjax : 진입 > 파일수 = " + (files == null ? " files null " : files.size()));
			
			File dir = new File(FILEUPLOAD_BASE_PATH + subPath);
			if(!dir.exists() || !dir.isDirectory()) dir.mkdirs();
			
			if(!files.isEmpty()) {
				
				Iterator<Entry<String, MultipartFile>> itr = files.entrySet().iterator();
				MultipartFile file;
				String filePath = "";
				AttachFileVO atvo;
				
				// 첨브파일그룹ID 
				if(StringUtil.isEmpty(attachFileId)) {
					attachFileId = UUID.getNewAttachFileId();
				}
				
				while (itr.hasNext()) {
					Entry<String, MultipartFile> entry = itr.next();

					file = entry.getValue();
					String orignlFileNm = file.getOriginalFilename();

					//--------------------------------------
					// 원 파일명이 없는 경우 처리 SKIP 
					// (첨부가 되지 않은 input file type)
					//--------------------------------------
					if (orignlFileNm == null || "".equals(orignlFileNm)) {
						continue;
					}

					// 파일 확장자 
					int index = orignlFileNm.lastIndexOf(".");
					String fileExt = (index < 1 ? "" : orignlFileNm.substring(index + 1));
					
					//String newName = UUID.getPhysicalFileName();
					
					
					int size = (int)file.getSize();
					String streFileNm = UUID.getNewStreFileNm();

					filePath = FILEUPLOAD_BASE_PATH + subPath + "/" + streFileNm;
					log.debug("FILE UPLOAD : filePath=" + filePath);
					
					file.transferTo(new File(FileUtil.filePathBlackList(filePath)));
				
					atvo = new AttachFileVO();
					atvo.setSubPathKey(subPathKey);
					atvo.setAttachFileId(attachFileId);
					atvo.setStreFileNm(streFileNm);
					atvo.setOrignlFileNm(orignlFileNm);
					atvo.setFileExt(fileExt);
					atvo.setFileSize(size);

					result.add(atvo);
				}
			}
		} catch(Exception e) {
			ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다 : " + e.toString());
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
		}
		
		// Convert Json
		ObjectMapper mapper = new ObjectMapper();
		String json;
		try {
			json = mapper.writeValueAsString(result);
			log.debug("RETURN DATA > json:" + json);
		} catch (JsonProcessingException e) {
			json = null;
			e.printStackTrace();
			ErrorMessage errorMessage = new ErrorMessage("ERROR", "JSON 변환 처리에 실패하였습니다 : " + e.toString());
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
		}

		HttpHeaders responseHeaders = new HttpHeaders();
	    ResponseEntity ret = ResponseEntity.ok().headers(responseHeaders).body(json);
		return ret;
	}
	
	/**
	 * 파일 다운로드를 처리한다. 
	 * 
	 * @param params
	 * @param mv
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value="/fileupload/downloadAttachFile.do")
	public ModelAndView downloadAttachFile(HttpServletRequest request, AttachFileVO attachFileVO, ModelAndView mv) throws Exception {
		
		//---------------------------------
		// 권한체크 
		//---------------------------------
		// 공지사항, FAQ : 모두 공개
		// QnA : 공개된 글인 경우, 모두 공개 
		// QnA : 비밀글 (본인에 한정) 
		
		attachFileVO.setMbInfoId(getMbInfoId(request));
		boolean hasAuth = attachFileService.checkAttachFileDownload(attachFileVO);
		
		if(!hasAuth) {
			throw new Exception("잘못된 접근입니다. 파일다운로드를 할 수 있는 권한이 없습니다.");
		}
		
		AttachFileVO retAttachFileVO = attachFileService.selectAttachFile(attachFileVO);
		
		if(retAttachFileVO == null || StringUtil.isEmpty(retAttachFileVO.getAttachFileId())) {
			throw new Exception("잘못된 접근이므로 파일다운로드를 할 수 없습니다."); 
		}
		
		if(StringUtil.isEmpty(retAttachFileVO.getFileStrePath()) || StringUtil.isEmpty(retAttachFileVO.getStreFileNm())) {
			throw new Exception("파일정보가 올바르지 않습니다.");
		}

		if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
			retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
		}
		
		if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
			retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
		}

		String fullPath = retAttachFileVO.getFileStrePath() + retAttachFileVO.getStreFileNm();
		
		log.debug("downloadAttachFile > fullPath = " + fullPath);
		
		if(FileUtil.isNotValid(fullPath)) {
			throw new Exception(MSG_NOT_VALID_FILE_PATH);
		}
		
		File file = new File(FileUtil.filePathBlackList(fullPath));
		if(!file.isFile()) {
			throw new Exception("해당 파일이 존재하지 않습니다. 관리자에게 문의하여 주시기 바랍니다.");
		}
		
		mv.setViewName("downloadView"); // dispatcher-servlet.xml내 BeanNameViewResolver 정의 
		mv.addObject("downloadFile", file);
		mv.addObject("fname", retAttachFileVO.getOrignlFileNm());
		
		return mv;
	}
	
}

