package egovframework.service.impl;

import egovframework.service.NoticeService;
import egovframework.service.mapper.NoticeMapper;
import egovframework.util.CommonPath;
import egovframework.util.PageCriteria;
import egovframework.util.PageMaker;
import egovframework.vo.NoticeVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*;

@Slf4j
@RequiredArgsConstructor
@Service
public class NoticeServiceImpl extends EgovAbstractServiceImpl implements NoticeService {

    private final NoticeMapper noticeMapper;
    private final String ATTACH_FILE_PATH = CommonPath.getFilePath();
    private final String IMAGE_FILE_PATH = CommonPath.getImageUploadPath();
    private final String[] fileExtArray = {"xls", "xlsx", "doc", "docx",
            "ppt", "pptx", "csv", "txt", "hwp", "pdf", "ai", "psd", "zip"};

    @Override
    public Map<String, Object> selectBoardList(HashMap<String, String> paramMap, PageCriteria criteria) {
        criteria.setBoardId(paramMap.get("boardId"));
        int page = 1;
        String searchText = "";
        String searchType = "";

        if (paramMap.get("page") != null) {
            page = Integer.parseInt(paramMap.get("page"));
        }

        if (paramMap.get("searchText") != null) {
            searchText = paramMap.get("searchText");
        }

        if (paramMap.get("searchText") != null) {
            searchType = paramMap.get("searchType");
        }

        criteria.setPage(page);
        criteria.setSearchText(searchText);
        criteria.setSearchType(searchType);

        PageMaker pageMaker = new PageMaker(page, this.noticeMapper.getNoticeTotalCount(paramMap));
        List<NoticeVO> noticeList = this.noticeMapper.getNoticeList(criteria);;

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("noticeList", noticeList);
        resultMap.put("pageMaker", pageMaker);

        return resultMap;
    }

    @Override
    public NoticeVO selectBoardView(HashMap<String, String> paramMap) {
        NoticeVO result = this.getNotice(paramMap);
        if (result.getAttachFile() != null) {
            String attachFile = result.getAttachFile();
            String[] fileArr = attachFile.split("[|]", -1);
            List<String> attachFiles = new ArrayList<>();
            if (fileArr.length > 0) {
                for (String file : fileArr) {
                    attachFiles.add(file);
                }
            }
            result.setAttachFiles(attachFiles);
        }

        if (result.getAttachFileId() != null) {
            String attachFileId = result.getAttachFileId();
            String[] idArr = attachFileId.split("[|]", -1);
            List<String> attachFileIds = new ArrayList<>();
            if (idArr.length > 0) {
                for (String id : idArr) {
                    //if (!id.isEmpty()) {
                    attachFileIds.add(id);
                    //}
                }
            }
            result.setAttachFileIds(attachFileIds);
        }

        return result;
    }

    @Override
    public int updateNoticeReadCount(HashMap<String, String> paramMap) {
        return this.noticeMapper.updateNoticeReadCount(paramMap);
    }

    @Override
    public NoticeVO getNotice(Map<String, String> paramMap) {
        return this.noticeMapper.getNotice(paramMap);
    }

    /**
     * 첨부 파일 다운로드
     * @param request 리퀘스트
     * @param response 리스폰스
     * @param paramMap 파라미터
     */
    @Override
    public void attatchedFileDownload(HttpServletRequest request, HttpServletResponse response, Map<String, Object> paramMap){
        File uploadFileHome = new File(ATTACH_FILE_PATH);
        String fileName     = paramMap.get("fileName").toString();
        String fileId       = paramMap.get("fileId").toString();
        String filteredName = "";

        if (fileName != null && !"".equals(fileName)) {
            filteredName = fileName.replaceAll("(\r\n|\r|\n|\n\r|&|/)", "");
        }

        File downloadFile = new File(uploadFileHome, fileId);
        int fileSize = (int)downloadFile.length();

        if (fileSize > 0) {
            FileInputStream inStream = null;
            OutputStream outStream = null;
            try {
                if (downloadFile != null) inStream = new FileInputStream(downloadFile);
                // obtains ServletContext
                ServletContext context = request.getSession().getServletContext();

                // gets MIME type of the file
                String mimeType = context.getMimeType(downloadFile.getAbsolutePath());
                String filteredMimeType = "application/octet-stream";

                if (mimeType != null) filteredMimeType = mimeType.replaceAll("(\r\n|\r|\n|\n\r)", "");

                response.setContentType(filteredMimeType);
                response.setContentLength(fileSize);

                String userAgent = request.getHeader("User-Agent");
                String header = "attachment; filename="
                        + new String(filteredName.getBytes("euc-kr"), "latin1") + ";";

                if (userAgent != null) {
                    if (userAgent.indexOf("MSIE 5.5") > -1) {// MS IE 5.5 이하
                        header =  "filename=" + URLEncoder.encode(filteredName, "UTF-8") + ";";
                    }
                    else if (userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("Trident") > -1) { // MS IE (보통은 6.x 이상 가정)
                        header = "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ";";
                    }
                }

                response.setHeader("Content-Disposition", header);
                outStream = response.getOutputStream();

                byte[] buffer = new byte[4096];
                int bytesRead = -1;

                if (inStream != null && inStream.available() > 0) {
                    while ((bytesRead = inStream.read(buffer)) != -1) {
                        if (outStream != null) {
                            outStream.write(buffer, 0, bytesRead);
                        }
                    }
                    if (paramMap.get("boardNo") != null) {
                        HashMap<String, String> updateMap = new HashMap<>();
                        updateMap.put("boardNo", (String)paramMap.get("boardNo"));
                        updateMap.put("boardId", "2");
                        this.updateNoticeReadCount(updateMap);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException();
            } finally {
                try {
                    if (inStream != null) {
                        inStream.close();
                    }

                    if (outStream != null) {
                        outStream.close();
                    }
                } catch (IOException e) {
                    log.error("IOException : /notice/getFile.do, Enabled Close InputStream or OutputStream");
                    throw new RuntimeException();
                }
            }
        }
        else {
            log.error("파일이 존재하지 않습니다. fileId : {}, fileName : {}", fileId, fileName);
        }
    }

    @Override
    public int insertBoard(List<MultipartFile> attachFile, String userId, Map<String, String> paramMap) {
        boolean isWriteSuccess = true;
        String fileNames = "";
        String fileIds = "";
        for (MultipartFile file : attachFile) {
            Map<String, Object> map = uploadFiles(file, false);
            fileNames += map.get("fileName");
            fileIds   += map.get("fileId");
            isWriteSuccess = (Boolean) map.get("isWriteSuccess");
            if (!isWriteSuccess) {
                break;
            }
            fileNames += "|";
            fileIds += "|";
        }

        if (isWriteSuccess) {
            fileIds = fileIds.substring(0, fileIds.length() - 1);
            fileNames = fileNames.substring(0, fileNames.length() - 1);
            Map<String, Object> resultMap = getInsertUpdateBoardParam(fileIds, fileNames, paramMap);
            resultMap.put("categoryCd", "N");
            resultMap.put("webUserId", userId);
            log.error("{}", resultMap);
            return this.noticeMapper.insertNotice(resultMap);
        }
        return 0;
    }

    @Override
    public int updateBoard(List<MultipartFile> attachFile, Map<String, String> paramMap, List<String> attachFileName) {
        NoticeVO vo             = this.noticeMapper.getNotice(paramMap);
        String[] orginAttatch   = new String[]{};
        String[] orginAttatId   = new String[]{};
        if (vo.getAttachFile() != null) {
            orginAttatch = vo.getAttachFile().split("\\|");
        }
        if (vo.getAttachFileId() != null) {
            orginAttatId = vo.getAttachFileId().split("\\|");
        }

        boolean isWriteSuccess = true;

        String fileNames = "";
        String fileIds = "";

        for (int ii = 0; ii < attachFile.size(); ii++) {
            MultipartFile file = attachFile.get(ii);
            String fileName = file.getOriginalFilename();
            if ( fileName == null || "".equals(fileName)) {
                if (orginAttatch.length > ii && attachFileName.size() > ii) {
                    if (!orginAttatch[ii].isEmpty() && !attachFileName.get(ii).isEmpty()) {
                        if (orginAttatch[ii].equals(attachFileName.get(ii))) {
                            fileIds   += orginAttatId[ii];
                            fileNames += attachFileName.get(ii);
                        }
                    }
                }
            }
            else {
                Map<String, Object> filesMap = uploadFiles(file, false);
                isWriteSuccess = (Boolean) filesMap.get("isWriteSuccess");
                if (!isWriteSuccess) {
                    break;
                }
                fileIds   += (String) filesMap.get("fileId");
                fileNames += (String) filesMap.get("fileName");
            }
            fileNames += "|";
            fileIds   += "|";
        }

        if (isWriteSuccess) {
            fileIds = fileIds.substring(0, fileIds.length() - 1);
            fileNames = fileNames.substring(0, fileNames.length() - 1);
            Map<String, Object> resultMap = getInsertUpdateBoardParam(fileIds, fileNames, paramMap);
            resultMap.put("boardNo", paramMap.get("boardNo"));
            return this.noticeMapper.updateNotice(resultMap);
        }
        return 0;
    }

    @Override
    public void deleteNotice(Map<String, Object> paramMap) {
        this.noticeMapper.deleteNotice(paramMap);
    }

    @Override
    public List<NoticeVO> getNoticeListData() {
        return this.noticeMapper.getNoticeListData();
    }

    public Map<String, Object> uploadFiles(MultipartFile file, Boolean isImage){
        Map<String, Object> resultMap = new HashMap<>();
        String fileName = file.getOriginalFilename();
        String contentType = file.getContentType();
        String fileUuid = "";
        Boolean isWriteSuccess = true;
        File uploadFileHome = null;
        if (!"".equals(fileName) && fileName != null) {
            String extension = fileName.substring(fileName.lastIndexOf(".")+1);
            if (isImage) {
                if (contentType != null && !contentType.contains("image")) {
                    log.error("Check the file types : {}", contentType);
                    isWriteSuccess = false;
                    fileUuid = "";
                    fileName = "";
                } else {
                    uploadFileHome = new File(IMAGE_FILE_PATH);
                }
            }
            else {
                boolean isContainType = false;
                for (String ext : fileExtArray) {
                    if (extension != null && ext.equals(extension)) {
                        isContainType = true;
                        break;
                    }
                }

                if (contentType != null && contentType.contains("image")) {
                    isContainType = true;
                }

                if (!isContainType) {
                    log.error("Check the file types : {}", contentType);
                    isWriteSuccess = false;
                    fileUuid = "";
                    fileName = "";
                } else {
                    uploadFileHome = new File(ATTACH_FILE_PATH);
                }
            }

            if (isWriteSuccess) {
                uploadFileHome.setExecutable(false, true);
                uploadFileHome.setReadable(true);
                uploadFileHome.setWritable(false, true);

                if (!uploadFileHome.exists()) {
                    uploadFileHome.mkdir();
                }

                fileUuid = UUID.randomUUID().toString().replace("-", "") + "." + extension;
                File uploadImgFile = new File(uploadFileHome, fileUuid);
                try {
                    if (file.getSize() <= 200000000) {
                        file.transferTo(uploadImgFile);
                    }
                    else {
                        log.error("Image is Too Large Size(Limit Size 200000000)");
                        isWriteSuccess = false;
                    }
                } catch (IllegalStateException | IOException e) {
                    log.error("Can not transfer To uploadImgFile...");
                    isWriteSuccess = false;
                }
            }
        }
        resultMap.put("fileName", fileName);
        resultMap.put("fileId", fileUuid);
        resultMap.put("isWriteSuccess", isWriteSuccess);
        return resultMap;
    }

    public Map<String, Object> getInsertUpdateBoardParam(String fileIds, String fileNames,
                                                         Map<String, String> paramMap) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("bSubject", paramMap.get("subject"));
        resultMap.put("bContent", paramMap.get("content"));
        resultMap.put("attachFile", fileNames);
        resultMap.put("ATTACHFILEID", fileIds);
        resultMap.put("bNotice", (paramMap.get("notice") == null) ? "N" : "Y");
        resultMap.put("boardId", paramMap.get("boardId"));
        return resultMap;
    }

}
