package com.its.web.service.notice;

import com.its.web.dto.message.ResultDto;
import com.its.web.dto.notice.AttachFileDto;
import com.its.web.dto.notice.NoticeDto;
import com.its.web.mapper.its.notice.NoticeMapper;
import com.its.web.pagination.Pagination;
import com.its.web.util.CommonUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;

@Slf4j
@RequiredArgsConstructor
@Service
public class NoticeService {
    private final NoticeMapper mapper;

    @Value("${board-location}")
    String boardLocation;

    public Pagination findAllList(String page, String searchType, String searchText, boolean isHide) {
        Pagination pagination = new Pagination(page, this.mapper.getNoticeTotalCount(), searchType, searchText);
        pagination.setHide(isHide);
        List<NoticeDto> list = this.mapper.findAllNotice(pagination);
        pagination.setList(list);
        return pagination;
    }

    public NoticeDto findNotice(String boardNo) {
        try {
            return this.mapper.findNoticeById(Long.parseLong(boardNo));
        }
        catch (NumberFormatException e){
            log.error("Cause NumberFormatException, Please Check The Board No -> {}", boardNo);
            return null;
        }
    }

    public List<NoticeDto> findMainNotice(int num, boolean isHide) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("num", num);
        paramMap.put("isHide", isHide);
        return this.mapper.findMainNotice(paramMap);
    }

    public AttachFileDto getAttachFile(String boardNo, String fileId) {
        AttachFileDto dto = new AttachFileDto();
        dto.setBoardNo(boardNo);
        String message = null;
        if (boardNo != null && fileId != null) {
            Map<String, String> paramMap = new HashMap<>();

            paramMap.put("boardNo", boardNo);
            paramMap.put("fileId", fileId);
            int result = this.mapper.getAttachFileCount(paramMap);
            if (result > 0) {
                File file = new File(boardLocation, fileId);
                int fileSize = (int) file.length();

                if (fileSize > 0) {
                    try {
                        byte[] byteArray = FileUtils.readFileToByteArray(file);
                        if (byteArray.length > 0) {
                            int i = 0;
                            dto.setAttachFile(new byte[byteArray.length]);
                            for (byte b : byteArray) {
                                dto.getAttachFile()[i++] = b;
                            }
                            message = "파일 전송 완료.";
                        }
                        else {
                            message = "파일을 찾을 수 없습니다.";
                        }
                    }
                    catch (FileNotFoundException e) {
                        log.error("File Not FoundException");
                        message = "파일을 찾을 수 없습니다.";
                    } catch (IOException e) {
                        log.error("IOException");
                        message = "파일을 읽는 중 오류가 발생하였습니다.";
                    }
                }
            }
            else {
                message = "파일을 찾일 수 없습니다.";
            }
        }
        dto.setMessage(message);
        return dto;
    }

    public ResultDto deleteBoard(String boardNo) {
        NoticeDto deleteDto = this.findNotice(boardNo);
        String fileIds   = deleteDto.getAttachFileId();
        if (fileIds != null){
            String[] idArray = fileIds.split("\\|");
            if (idArray.length > 0) {
                for(String id : idArray) {
                    File file = new File(boardLocation, id);
                    if (file.exists()) {
                        boolean isDelete = file.delete();
                        if (!isDelete) {
                            log.error("게시물 파일이 삭제되지 않았습니다. {}", id);
                        }
                    }
                }
            }
        }

        int affectedRows = this.mapper.deleteBoard(boardNo);
        String message = "선택하신 게시물이 삭제되지 않았습니다.";
        String success = "F";
        if (affectedRows > 0) {
            message = "선택하신 게시물이 삭제되었습니다.";
            success = "S";
        }
        return CommonUtil.getResultDto(success, message);
    }

    public ResultDto writeNotice(NoticeDto.NoticeDtoWriteReq param, List<MultipartFile> attachFile, String webUserId) {
        Map<String, Object> paramMap = new HashMap<>();
        String fileNames = "";
        String fileIds = "";
        String message = "";
        String success = "S";
        paramMap.put("bContent", param.getBcontent());
        paramMap.put("bSubject", param.getBsubject());
        paramMap.put("webUserId", webUserId);
        paramMap.put("hmpgDsplEn", param.getHmpgDsplEn());

        if (attachFile != null && attachFile.size() > 0){

            for (MultipartFile file : attachFile) {
                String fileName  = file.getOriginalFilename();

                if (fileName != null) {
                    String ext = fileName.substring(fileName.lastIndexOf("."));
                    String fileId   = UUID.randomUUID().toString().replaceAll("-", "") + ext;

                    boolean isSuccess = uploadAttachFile(file, fileId);
                    fileNames = fileName + "|";
                    fileIds = fileId + "|";
                    if (!isSuccess) {
                        message = "파일 생성 중 오류가 발생하였습니다.";
                        success = "F";
                    }
                }
            }
        }

        if (success.equals("S")) {
            if (attachFile != null) {
                int fileSize = attachFile.size();

                if (attachFile != null && fileSize == 1) {
                    fileNames = fileNames + "|";
                    fileIds = fileIds + "|";
                }
                else if (fileSize == 3) {
                    fileNames = fileNames.substring(fileNames.length() - 1);
                    fileIds = fileIds.substring(fileIds.length() - 1);
                }
            }
            else {
                fileNames = "||";
                fileIds = "||";
            }

            paramMap.put("attachFile", fileNames);
            paramMap.put("attachFileId", fileIds);
            int affectedRows = this.mapper.insertNotice(paramMap);
            message = "작성하신 게시물이 저장되었습니다.";
            if (affectedRows <= 0) {
                message = "작성하신 게시물이 저장되지 않았습니다.";
                success = "F";
            }
        }

        return CommonUtil.getResultDto(success, message);
    }

    public ResultDto modifyNotice(NoticeDto.NoticeDtoUpdateReq param, List<MultipartFile> attachFile) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bSubject", param.getBSubject());
        paramMap.put("bContent", param.getBContent());
        paramMap.put("boardNo", param.getBoardNo());
        paramMap.put("hmpgDsplEn", param.getHmpgDsplEn());
        String attachFileNames = param.getAttachFileNames();
        Map<Integer, String> sameIndex = new HashMap<>();
        String fileId = "||";
        String success = "S";
        String message = "작성하신 게시물이 수정되었습니다.";

        NoticeDto dto = this.findNotice(param.getBoardNo());
        if (dto != null) {
            if (!attachFileNames.equals(dto.getAttachFile())) {
                String originAttachFile = dto.getAttachFile();
                String originFileIds = dto.getAttachFileId();
                if (originAttachFile != null && originFileIds != null) {
                    String[] fileNameArr = originAttachFile.split("\\|");
                    String[] fileIdArr = originFileIds.split("\\|");
                    String[] nameArr = attachFileNames.split("\\|");
                    // 기존 파일과 같은 이름이 있는지 찾고 있으면 유지 해줘야한다
                    if (fileNameArr.length > 0) {
                        for (int ii = 0; ii < fileNameArr.length; ii++) {
                            String originName = fileNameArr[ii];
                            if (nameArr.length >= (ii +1) && originName.equals(nameArr[ii])) {
                                sameIndex.put(ii, fileIdArr[ii]);
                            }
                            else {
                                if (fileIdArr.length > ii && fileIdArr[ii] != null) {
                                    File file = new File(boardLocation, fileIdArr[ii]);
                                    if (file.exists()) {
                                        boolean isDelete = file.delete();
                                        if (!isDelete) {
                                            log.error("기존 파일 삭제가 진행되지 않았습니다. {}", fileIdArr[ii]);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else {
                fileId = dto.getAttachFileId();
            }
        }

        if (attachFile != null && attachFile.size() > 0) {
            int cnt = 0;
            for (MultipartFile file : attachFile) {
                String orgName = file.getOriginalFilename();
                if (orgName != null && orgName.lastIndexOf(".") > 0) {
                    String ext = orgName.substring(orgName.lastIndexOf("."));
                    String attachFileId = UUID.randomUUID().toString().replaceAll("-", "") + ext;
                    if (sameIndex.size() == 0) {
                        sameIndex.put(cnt++, attachFileId);
                    }
                    else {
                        for (int ii = 0; ii < 3; ii++) {
                            if (sameIndex.get(ii) == null) {
                                sameIndex.put(ii, attachFileId);
                                break;
                            }
                        }
                    }
                    boolean isSuccess = uploadAttachFile(file, attachFileId);
                    if (!isSuccess) {
                        message = "파일 생성 중 오류가 발생하였습니다.";
                        success = "F";
                        return CommonUtil.getResultDto(success, message);
                    }
                }
            }

            fileId = "";
            for (int ii = 0; ii < 3; ii++) {
                if (sameIndex.get(ii) != null) {
                    fileId += sameIndex.get(ii);
                }
                if (ii != 2) {
                    fileId += "|";
                }
            }
        }

        paramMap.put("attachFile", attachFileNames);
        paramMap.put("attachFileId", fileId);
        int affectedRows = this.mapper.updateNotice(paramMap);
        if (affectedRows <= 0) {
            message ="작성하신 게시물이 수정되지 않았습니다.";
            success = "F";
        }

        return CommonUtil.getResultDto(success, message);
    }

    public boolean uploadAttachFile(MultipartFile attachFile, String fileId) {
        File file = new File(boardLocation);
        boolean isMkFile = true;
        if (!file.exists()) {
            file.setExecutable(false, true); /*파일 실행 권한 설정 (파라미터 - 일반, 소유자)*/
            file.setReadable(true); /*파일 읽기 권한 설정*/
            file.setWritable(false, true);
            isMkFile = file.mkdirs();
        }
        if (isMkFile) {
            File transFile = new File(boardLocation + "/" + fileId);

            try {
                attachFile.transferTo(transFile);
            }
            catch (IOException e) {
                log.error("IOException - 첨부 파일 파일 생성 중 오류가 발생하였습니다.");
                isMkFile = false;
            }
        }
        else {
            log.error("디렉토리 생성 중 오류가 발생하였습니다.");
        }

        return isMkFile;
    }

    public int updateNoticeReadCount(String boardNo) {
        return this.mapper.updateNoticeReadCount(boardNo);
    }

    public String imageUpload(HttpServletRequest req){
        String uploadImageInfo = "";
        String fileName = req.getHeader("file-name");
        try {
            fileName = URLDecoder.decode(fileName, "UTF-8");
        }
        catch (UnsupportedEncodingException exception) {
            log.error("Can not Decoding UTF-8");
        }
        String filePath = boardLocation + "/upload";
        File file = new File(filePath);
        boolean isMkdirs = true;
        if (!file.exists()) {
            file.setExecutable(false, true); /*파일 실행 권한 설정 (파라미터 - 일반, 소유자)*/
            file.setReadable(true); /*파일 읽기 권한 설정*/
            file.setWritable(false, true);
            isMkdirs = file.mkdirs();
        }

        if (isMkdirs) {
            String realFileNm;
            realFileNm = UUID.randomUUID().toString().replace("-", "") + fileName.substring(fileName.lastIndexOf("."));
            String rlFileNm = filePath + File.separator + realFileNm;

            OutputStream os = null;
            try (
                    InputStream is = req.getInputStream();
            ) {
                os = new FileOutputStream(rlFileNm);
                int numRead;
                byte[] byteArray = new byte[Integer.parseInt(req.getHeader("file-size"))];
                if (is != null) {
                    while ((numRead = is.read(byteArray, 0, byteArray.length)) != -1) {
                        os.write(byteArray, 0, numRead);
                    }
                }
                os.flush();
            } catch (IOException e) {
                log.error("IO Exception - noticeImageUpload");
            } catch (Exception e1) {
                log.error("Exception - noticeImageUpload");
            }
            finally {
                if(os != null) {
                    try{
                        os.close();
                    }
                    catch (IOException e) {
                        log.error("Cause IOException...");
                    }
                }
            }
            uploadImageInfo += "&bNewLine=true";
            uploadImageInfo += "&sFileName=" + fileName;
            uploadImageInfo += "&sFileURL=" + "/api/notice/upload/" + realFileNm;
        }

        return uploadImageInfo;
    }

    public byte[] getImage(String imageName) {
        byte[] image = null;
        File file = new File(boardLocation + "/upload", imageName);
        int fileSize = (int) file.length();
        if (fileSize > 0) {
            try {
                byte[] byteArray = FileUtils.readFileToByteArray(file);
                if (byteArray.length > 0) {
                    int ii = 0;
                    image = new byte[byteArray.length];
                    for (byte b : byteArray) {
                        image[ii++] = b;
                    }
                }
            } catch (FileNotFoundException e) {
                log.error("Not Found File - {}", boardLocation + "/upload/" + imageName);
            } catch (IOException e) {
                log.error("Can Not Send Image Cause IOException - {}", boardLocation + "/upload/" + imageName);
            }
        }
        return image;
    }
}



