package com.dbnt.kcgfilemanager.service;

import com.dbnt.kcgfilemanager.config.LogStatus;
import com.dbnt.kcgfilemanager.mapper.BoardMapper;
import com.dbnt.kcgfilemanager.model.*;
import com.dbnt.kcgfilemanager.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class BoardService {

  private final BoardMapper boardMapper;
  private final BoardRepository boardRepository;

  private final BoardCategoryRepository boardCategoryRepository;
  private final BoardCategoryService boardCategoryService;

  private final BoardLogRepository boardLogRepository;
  private final FileInfoRepository fileInfoRepository;
  private final HashTagRepository hashTagRepository;
  private final HashTagLinkRepository hashTagLinkRepository;
  private final UserInfoRepository userInfoRepository;


  public List<SearchResult> fullSearchBoardContent(List<Integer> categorySeqList, Board board) {
    board.setRowCnt(Integer.MAX_VALUE);
    List<SearchResult> results = new ArrayList<>();

    for(int categorySeq : categorySeqList){
      SearchResult searchResult = new SearchResult();
      BoardCategory category = boardCategoryRepository.findById(categorySeq).orElse(null);
      searchResult.setSearchCategoryName(boardCategoryService.getPageTitle(categorySeq));
      if(category.getDepth() != 4){
        category.setChildCategoryList(boardCategoryService.selectBoardCategoryAll(categorySeq, category.getDepth()+1));
        board.setCategoryList(setBoardCategoryToLastDepth(new ArrayList<>(), category));
      }else{
        board.setCategoryList(new ArrayList<>());
        board.getCategoryList().add(category);
      }

      searchResult.setContentList(selectContentList(board));
      results.add(searchResult);
    }
    return results;
  }

  @Transactional
  public Integer saveContent(Board content){
    Integer contentSeq = boardRepository.save(content).getContentSeq();
    saveBoardLog(content.getContentSeq(), LogStatus.WRITE, null, content.getCreateId());
    saveHashTagLink(contentSeq, content.getHashTagStr());
    saveUploadFiles(content, content.getCreateId());
    return contentSeq;
  }

  @Transactional
  public Integer updateContent(Board updateContent, List<Integer> fileSeqList, UserInfo userInfo) {
    int contentSeq = updateContent.getContentSeq();
    Board savedContent = boardRepository.findById(contentSeq).orElse(null);
    savedContent.setCategorySeq(updateContent.getCategorySeq());
    savedContent.setTitle(updateContent.getTitle());
    savedContent.setStatus(updateContent.getStatus());
    savedContent.setDescription(updateContent.getDescription());
    saveBoardLog(contentSeq, LogStatus.MODIFY, null, userInfo.getUserId());
    deleteHashTagLink(contentSeq);
    saveHashTagLink(contentSeq, updateContent.getHashTagStr());
    if(fileSeqList!=null){
      deleteFileInfo(contentSeq, fileSeqList, userInfo.getUserId());
    }
    if(updateContent.getFileList()!=null){
      saveUploadFiles(updateContent, userInfo.getUserId());
    }
    return contentSeq;
  }

  private void saveBoardLog(Integer contentSeq, LogStatus status, String description, String createId){
    BoardLog lastLog = boardLogRepository.findTopByContentSeqOrderByLogSeqDesc(contentSeq).orElse(null);
    BoardLog log = new BoardLog();
    log.setContentSeq(contentSeq);
    log.setLogSeq(lastLog == null?1:(lastLog.getLogSeq()+1));
    log.setLogStatus(status.name());
    log.setDescription(description);
    log.setCreateId(createId);
    boardLogRepository.save(log);
  }

  private HashTag saveHashTag(String tagName){
    HashTag tag = new HashTag();
    tag.setTagName(tagName);
    return hashTagRepository.save(tag);
  }

  private void saveHashTagLink(Integer contentSeq, String hashTagStr){
    String[] hashTagAry = hashTagStr.trim().replaceAll(",", "").split("#");
    for(String tagName : hashTagAry){
      if(!tagName.equals("")){
        HashTag tag = hashTagRepository.findByTagName(tagName).orElse(null);
        if(tag==null){
          tag = saveHashTag(tagName);
        }
        HashTagLink link = new HashTagLink();
        link.setContentSeq(contentSeq);
        link.setTagSeq(tag.getTagSeq());
        hashTagLinkRepository.save(link);
      }
    }
  }
  private void saveUploadFiles(Board content, String userId){
    FileInfo lastFileInfo = fileInfoRepository.findTopByContentSeqOrderByFileSeqDesc(content.getContentSeq()).orElse(null);
    int fileSeq = lastFileInfo==null?1:(lastFileInfo.getFileSeq()+1);
    for(MultipartFile file : content.getFileList()){
      String saveName = UUID.randomUUID().toString();
      String path = boardCategoryService.makeFilePath(content.getCategorySeq());

      File saveFile = new File(path+"\\"+saveName);
      if(file.getSize()!=0){ // 저장될 파일 확인
        if(!saveFile.exists()){ // 저장될 경로 확인
          if(saveFile.getParentFile().mkdirs()){
            try{
              saveFile.createNewFile();
            }catch (IOException e){
              e.printStackTrace();
            }
          }
        }
        try {
          file.transferTo(saveFile);
        }catch (IllegalStateException | IOException e){
          e.printStackTrace();
        }
      }

      String[] originalFilename = Objects.requireNonNull(file.getOriginalFilename()).split("\\.");
      FileInfo fileInfo = new FileInfo();
      fileInfo.setContentSeq(content.getContentSeq());
      fileInfo.setFileSeq(fileSeq++);
      fileInfo.setOriginalName(originalFilename[0]);
      fileInfo.setExtention(originalFilename[1]);
      fileInfo.setConversionName(saveName);
      fileInfo.setFileSize(calculationFileSize(file.getSize()));
      fileInfo.setSavePath(path);
      fileInfoRepository.save(fileInfo);
      saveBoardLog(content.getContentSeq(), LogStatus.FILE_ADD, fileInfo.getFullName(), userId);
    }
  }

  public List<Board> selectContentList(Board board) {
    return boardMapper.selectContentList(board);
  }
  public Integer selectContentListCnt(Board board) {
    return boardMapper.selectContentListCnt(board);
  }

  @Transactional
  public Board selectContentByContentSeqAndViewCntUp(Integer contentSeq) {
    Board target = selectContent(contentSeq);
    if(!target.getStatus().equals("D")){
      target.setViewCnt(target.getViewCnt()+1);
    }
    return target;
  }
  public Board selectContent(int contentSeq){
    Board content = boardRepository.findById(contentSeq).orElse(null);
    content.setCreateName(userInfoRepository.findByUserId(content.getCreateId()).orElse(null).getUserId());
    return content;
  }
  public Board selectContentModifyInfo(int contentSeq){
    Board content = selectContent(contentSeq);
    content = selectContentForeignAttribute(content);
    StringBuilder hashTagStr = new StringBuilder();
    for(HashTag tag : content.getHashTagList()){
      hashTagStr.append("#").append(tag.getTagName()).append(", ");
    }
    if(!hashTagStr.toString().equals("")) {
      hashTagStr = new StringBuilder(hashTagStr.substring(0, hashTagStr.length() - 2));
      content.setHashTagStr(hashTagStr.toString());
    }
    return content;
  }
  public List<FileInfo> selectDownloadFileInfoList(Integer contentSeq, List<Integer> fileSeqList, String userId) {
    List<FileInfo> fileList = fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq);
    List<FileInfo> targetList = new ArrayList<>();
    for(FileInfo file: fileList){
      if(fileSeqList.contains(file.getFileSeq())){
        targetList.add(file);
        saveBoardLog(contentSeq, LogStatus.FILE_DOWN, file.getFullName(), userId);
      }
    }
    return targetList;
  }
  public FileInfo selectDownloadFileInfo(Integer contestSeq, Integer fileSeq, String userId){
    FileInfo fileInfo = fileInfoRepository.findById(new FileInfo.FileInfoId(contestSeq, fileSeq)).orElse(null);
    saveBoardLog(contestSeq, LogStatus.FILE_DOWN, fileInfo.getFullName(), userId);
    return fileInfo;
  }

  public List<BoardLog> selectContentLog(Integer contentSeq){
    return boardMapper.selectBoardLogFromContentSeq(contentSeq);
  }

  public Board selectContentForeignAttribute(Board target) {
    target.setHashTagList(boardMapper.selectHashTagListFromContentSeq(target.getContentSeq()));
    target.setChildFileList(fileInfoRepository.findByContentSeqOrderByFileSeqAsc(target.getContentSeq()));
    return target;
  }

  @Transactional
  public Integer deleteContent(Board content, UserInfo user) {
    int contentSeq = content.getContentSeq();
    content = boardRepository.findById(contentSeq).orElse(null);
    content.setTitle("삭제된 게시물");
    content.setDescription(null);
    content.setStatus("D");
    saveBoardLog(contentSeq, LogStatus.DELETE, null, user.getUserId());
    deleteHashTagLink(contentSeq);
    deleteFileInfoAll(contentSeq);
    return contentSeq;
  }

  public Board setHashTagSearch(Board board, List<String> tagNameList){
    List<HashTag> tagList = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    for(String tagName: tagNameList){
      HashTag tag = new HashTag();
      tag.setTagName(tagName);
      tagList.add(tag);
      sb.append(tagName).append(",");
    }
    board.setHashTagList(tagList);
    String hashTagStr = sb.toString();
    board.setHashTagStr(hashTagStr.substring(0, hashTagStr.length()-1));
    return board;
  }

  private List<BoardCategory> setBoardCategoryToLastDepth(List<BoardCategory> categoryList, BoardCategory category){
    for(BoardCategory child: category.getChildCategoryList()){
      if(child.getDepth()==4){
        categoryList.add(child);
      }
      setBoardCategoryToLastDepth(categoryList, child);
    }
    return categoryList;
  }

  private void deleteHashTagLink(int contentSeq){
    List<HashTagLink> tagLinkList = hashTagLinkRepository.findByContentSeq(contentSeq);
    hashTagLinkRepository.deleteAll(tagLinkList);
  }
  private void deleteFileInfoAll(int contentSeq){
    List<FileInfo> fileInfoList = fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq);
    for(FileInfo fileInfo: fileInfoList){
      deleteStoredFile(fileInfo.getSavePath(), fileInfo.getConversionName());
    }
    fileInfoRepository.deleteAll(fileInfoList);
  }

  private void deleteFileInfo(Integer contentSeq, List<Integer> fileSeqList, String userId){
    List<FileInfo> fileInfoList = fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq);
    for(FileInfo fileInfo: fileInfoList){
      if(fileSeqList.contains(fileInfo.getFileSeq())){
        deleteStoredFile(fileInfo.getSavePath(), fileInfo.getConversionName());
        saveBoardLog(contentSeq, LogStatus.FILE_REMOVE, fileInfo.getFullName(), userId);
        fileInfoRepository.delete(fileInfo);
      }
    }
  }

  private void deleteStoredFile(String savePath, String conversionName){
    File deleteFile = new File(savePath, conversionName);
    deleteFile.delete();
  }
  private String calculationFileSize(Long fileSize){
    String[] units = {"bytes", "KB", "MB"};
    double unitSelector = Math.floor(Math.log(fileSize)/Math.log(1024));
    return Math.round((fileSize/Math.pow(1024, unitSelector))*100)/100d+" "+units[(int)unitSelector];
  }
}
