package com.dbnt.kcgfilemanager.controller;

import com.dbnt.kcgfilemanager.model.FileInfo;
import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.service.BoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.security.Principal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@RestController
@RequiredArgsConstructor
@RequestMapping("/file")
public class FileController {

  private final BoardService boardService;

  @Value("${spring.servlet.multipart.location}")
  private String locationPath;

  @GetMapping("/fileDownload")
  public void fileDownload(Principal principal, Integer contentSeq, Integer fileSeq, HttpServletRequest request, HttpServletResponse response){
    FileInfo downloadFile = boardService.selectDownloadFileInfo(contentSeq, fileSeq, ((UserInfo)((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getUserId());

    BufferedInputStream in;
    BufferedOutputStream out;
    try {
      File file = new File(downloadFile.getSavePath(), downloadFile.getConversionName());

      setDisposition(downloadFile.getFullName(), request, response);
      in = new BufferedInputStream(new FileInputStream(file));
      out = new BufferedOutputStream(response.getOutputStream());
      FileCopyUtils.copy(in, out);
      out.flush();
      if(out!=null) out.close();
      if(in!=null )in.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @GetMapping("/fileDownloadToZip")
  public void fileDownloadToZip(Principal principal, Integer contentSeq, @RequestParam(value = "fileSeq") List<Integer> fileSeqList, HttpServletResponse response){
    List<FileInfo> targetList = boardService.selectDownloadFileInfoList(contentSeq, fileSeqList, ((UserInfo)((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getUserId());

    BufferedInputStream in;
    BufferedOutputStream out;

    String tempZipPath = locationPath+File.separator+"tempZip"+File.separator;
    File tempFolder = new File(tempZipPath);
    if (!tempFolder.exists() || tempFolder.isFile()) {
      tempFolder.mkdirs();
    }
    String downloadFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));

    tempZipPath += downloadFileName+".zip";

    try{
      FileOutputStream fout = new FileOutputStream(tempZipPath);
      ZipOutputStream zout = new ZipOutputStream(fout);
      for(FileInfo downLoadFile: targetList){
        ZipEntry zipEntry = new ZipEntry(downLoadFile.getFullName());
        zout.putNextEntry(zipEntry);
        File savedFile = new File(downLoadFile.getSavePath(), downLoadFile.getConversionName());
        FileInputStream fin = new FileInputStream(savedFile);
        byte[] buffer = new byte[1024];
        int length;
        while((length = fin.read(buffer)) > 0){
          zout.write(buffer, 0, length);
        }

        zout.closeEntry();
        fin.close();
      }
      zout.close();

      response.setContentType("application/zip");
      response.addHeader("Content-Disposition", "attachment; filename="+downloadFileName+".zip");

      FileInputStream fis=new FileInputStream(tempZipPath);
      ServletOutputStream so=response.getOutputStream();
      in=new BufferedInputStream(fis);
      out=new BufferedOutputStream(so);

      byte[] data=new byte[2048];
      int input;
      while((input=in.read(data))!=-1){
        out.write(data,0,input);
        out.flush();
      }

      if(out!=null) out.close();
      if(fis!=null) fis.close();
      if(in!=null) in.close();
      if(so!=null) so.close();
    }catch (IOException e){
      e.printStackTrace();
    } finally {
      File tempZip = new File(tempZipPath);
      tempZip.delete();
    }
  }

  private void setDisposition(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
    String browser = getBrowser(request);

    String dispositionPrefix = "attachment; filename=";
    String encodedFilename = null;

    if (browser.equals("MSIE")) {
      encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
    } else if (browser.equals("Trident")) { // IE11 문자열 깨짐 방지
      encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
    } else if (browser.equals("Firefox")) {
      encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
    } else if (browser.equals("Opera")) {
      encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
    } else if (browser.equals("Chrome")) {
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < filename.length(); i++) {
        char c = filename.charAt(i);
        if (c > '~') {
          sb.append(URLEncoder.encode("" + c, "UTF-8"));
        } else {
          sb.append(c);
        }
      }
      encodedFilename = sb.toString();
    } else {
      throw new IOException("Not supported browser");
    }

    response.setHeader("Content-Disposition", dispositionPrefix + encodedFilename);

    if ("Opera".equals(browser)) {
      response.setContentType("application/octet-stream;charset=UTF-8");
    }
  }

  private String getBrowser(HttpServletRequest request) {
    String header = request.getHeader("User-Agent");
    if (header.indexOf("MSIE") > -1) {
      return "MSIE";
    } else if (header.indexOf("Trident") > -1) { // IE11 문자열 깨짐 방지
      return "Trident";
    } else if (header.indexOf("Chrome") > -1) {
      return "Chrome";
    } else if (header.indexOf("Opera") > -1) {
      return "Opera";
    }
    return "Firefox";
  }
}
