package com.younike.web.controller.common;

import com.younike.common.config.CommonConfig;
import com.younike.common.constant.Constants;
import com.younike.common.core.domain.AjaxResult;
import com.younike.common.core.domain.VideoUploadResult;
import com.younike.common.utils.DatePattern;
import com.younike.common.utils.DateUtils;
import com.younike.common.utils.StringUtils;
import com.younike.common.utils.file.FileUploadUtils;
import com.younike.common.utils.file.FileUtils;
import com.younike.common.utils.AliyunOSSUtil;
import com.younike.common.utils.uuid.StringComparator;
import com.younike.framework.config.ServerConfig;
import com.younike.system.domain.FileUrlList;
import com.younike.system.service.IStorageService;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 通用请求处理
 *
 */
@RestController
public class CommonController
{
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);

    @Autowired
    private ServerConfig serverConfig;
    @Value("${oss.filepath}")
    private String filePath;
    @Value("${oss.filehost}")
    private String filehost;

    private String newFileHost = "https://cdn.uxianxin.com/";

    @Resource
    private AliyunOSSUtil aliyunOSSUtil;

    @Resource
    private IStorageService storageService;

    /**
     * 通用下载请求
     *
     * @param fileName 文件名称
     * @param delete 是否删除
     */
    @GetMapping("common/download")
    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
    {
        try
        {
            if (!FileUtils.checkAllowDownload(fileName))
            {
                throw new Exception(StringUtils.format("文件名称({})非法，不允许下载。 ", fileName));
            }
            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
            String filePath = CommonConfig.getDownloadPath() + fileName;

            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, realFileName);
            FileUtils.writeBytes(filePath, response.getOutputStream());
            if (delete)
            {
                FileUtils.deleteFile(filePath);
            }
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }

    /**
     * 通用上传请求
     */
    @PostMapping("/common/upload")
    @PermitAll
    public AjaxResult uploadFile(MultipartFile file) throws Exception
    {
        try
        {
            // 上传文件路径
            String filePath = CommonConfig.getUploadPath();
            // 上传并返回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);
            String url = serverConfig.getUrl() + fileName;
            AjaxResult ajax = AjaxResult.success();
            ajax.put("fileName", fileName);
            ajax.put("url", url);
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    @GetMapping("/member/getAsign")
    @PermitAll
    @ApiOperation("会员端获取文件服务器host")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "请求成功"),
            @ApiResponse(responseCode = "150", description = "请求参数不正确"),
            @ApiResponse(responseCode = "404", description = "请求路径没有或页面跳转路径不对")
    })
    public AjaxResult getMemberFileAsign(){

        Map<String, String> signature;

        try{
            signature = aliyunOSSUtil.createSignature();
        }catch (Exception e){
            return AjaxResult.error();
        }
        return AjaxResult.success(signature);

    }

    @GetMapping("/getAsign")
    @PermitAll
    @ApiOperation("服务人员端获取文件服务器host")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "请求成功"),
            @ApiResponse(responseCode = "150", description = "请求参数不正确"),
            @ApiResponse(responseCode = "404", description = "请求路径没有或页面跳转路径不对")
    })
    public AjaxResult getServerFileAsign(){

        Map<String, String> signature;

        try{
            signature = aliyunOSSUtil.createSignature();
        }catch (Exception e){
            return AjaxResult.error();
        }
        return AjaxResult.success(signature);
    }

    /**
     * 单个视频上传
     */
    @PostMapping("/common/uploadVideo")
    @PermitAll
    public AjaxResult  videoUpload(@RequestParam("file") MultipartFile file) {
        // 保存文件
        try
        {
            VideoUploadResult video = storageService.storeVideo(file);
            return AjaxResult.success(video);
        } catch (Exception e) {
            log.error("下载文件失败", e);
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 本地资源通用下载
     */
    @GetMapping("/common/download/resource")
    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
            throws Exception
    {
        try
        {
            if (!FileUtils.checkAllowDownload(resource))
            {
                throw new Exception(StringUtils.format("资源文件({})非法，不允许下载。 ", resource));
            }
            // 本地资源路径
            String localPath = CommonConfig.getProfile();
            // 数据库资源地址
            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
            // 下载名称
            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, downloadName);
            FileUtils.writeBytes(downloadPath, response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }

    /**
     * 通用上传图片到oss
     */
    @PostMapping("/common/uploadImage")
    @PermitAll
    public AjaxResult uploadImage(@RequestParam("file") MultipartFile file)throws Exception
    {
//        AliyunOSSUtil aliyunOSSUtil = new AliyunOSSUtil();
        try
        {
            log.info("文件上传");
            String fileName = file.getOriginalFilename();
            String uploadUrl = "";
            if (file != null) {
                if (!"".equals(fileName.trim())) {
                    File newFile = new File(fileName);
                    FileOutputStream os = new FileOutputStream(newFile);
                    os.write(file.getBytes());
                    os.close();
                    file.transferTo(newFile);

                    // 根据不同文件文件类型-不同日期生成不同文件夹
                    Date date = new Date();
                    String datePath = DateUtils.formatDateByStyle(date, DatePattern.CN_DATE_BASIC_STYLE4.getDatePattern());

                    String timeStamp = String.valueOf(System.currentTimeMillis());
                    fileName = timeStamp + fileName.substring(fileName.lastIndexOf("."), fileName.length());

                    String path;
                    if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) {
                        // images
                        path = filePath + "images/" + datePath + "/" + fileName;
                    } else if (fileName.endsWith(".mp4")) {
                        // video
                        path = filePath + "video/" + datePath + "/" + fileName;
                    } else {
                        path = filePath + "other/" + datePath + "/" + fileName;
                    }
                    // 上传到OSS
                    uploadUrl = aliyunOSSUtil.uploadFiles(newFile, path);
                    newFile.delete();
                    if (uploadUrl != null) {
//                        return uploadUrl;
                    }
                }
            }

            AjaxResult ajax = AjaxResult.success();
            ajax.put("fileName", fileName);
            ajax.put("url", filehost+uploadUrl);
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }



    /**
     * 单个文件上传
     */
    @PostMapping("/common/uploadFiles")
    @PermitAll
    public AjaxResult uploadFiles(@RequestParam("file") MultipartFile file)throws Exception
    {
        try
        {
            log.info("文件上传");
            String fileName = file.getOriginalFilename();
            String uploadUrl = "";
            if (file != null) {
                if (!"".equals(fileName.trim())) {
                    File newFile = new File(fileName);
                    FileOutputStream os = new FileOutputStream(newFile);
                    os.write(file.getBytes());
                    os.close();
                    file.transferTo(newFile);

                    // 根据不同文件文件类型-不同日期生成不同文件夹
                    Date date = new Date();
                    String datePath = DateUtils.formatDateByStyle(date, DatePattern.CN_DATE_BASIC_STYLE4.getDatePattern());

                    String timeStamp = String.valueOf(System.currentTimeMillis());
                    fileName = timeStamp + fileName.substring(fileName.lastIndexOf("."), fileName.length());

                    String path;
                    if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) {
                        // images
                        path = filePath + "images/" + datePath + "/" + fileName;
                    } else if (fileName.endsWith(".mp4")) {
                        // video
                        path = filePath + "video/" + datePath + "/" + fileName;
                    } else {
                        path = filePath + "other/" + datePath + "/" + fileName;
                    }
                    // 上传到OSS
                    uploadUrl = aliyunOSSUtil.uploadFiles(newFile, path);
                    newFile.delete();
                    if (uploadUrl != null) {
//                        return uploadUrl;
                    }
                }
            }

            AjaxResult ajax = AjaxResult.success();
            ajax.put("fileName", fileName);
            ajax.put("url", newFileHost+uploadUrl);
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 文件批量上传文件上传
     */
    @PostMapping("/common/uploadBatchFiles")
    @PermitAll
    public AjaxResult uploadBatchFiles(@RequestParam("files") MultipartFile[] files)throws Exception
    {
        List<FileUrlList> fileUrlList = new ArrayList<>();
        for(MultipartFile file:files){
            FileUrlList fileUrlList1 = new FileUrlList();
            try
            {
                log.info("文件上传");
                String fileName = file.getOriginalFilename();
                String uploadUrl = "";
                if (file != null) {
                    if (!"".equals(fileName.trim())) {
                        File newFile = new File(fileName);
                        FileOutputStream os = new FileOutputStream(newFile);
                        os.write(file.getBytes());
                        os.close();
                        file.transferTo(newFile);

                        // 根据不同文件文件类型-不同日期生成不同文件夹
                        Date date = new Date();
                        String datePath = DateUtils.formatDateByStyle(date, DatePattern.CN_DATE_BASIC_STYLE4.getDatePattern());

                        String timeStamp = String.valueOf(System.currentTimeMillis());
                        fileName = timeStamp + fileName.substring(fileName.lastIndexOf("."), fileName.length());

                        String path;
                        if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) {
                            // images
                            path = filePath + "images/" + datePath + "/" + fileName;
                        } else if (fileName.endsWith(".mp4")) {
                            // video
                            path = filePath + "video/" + datePath + "/" + fileName;
                        } else {
                            path = filePath + "other/" + datePath + "/" + fileName;
                        }
                        // 上传到OSS
                        uploadUrl = aliyunOSSUtil.uploadFiles(newFile, path);
                        newFile.delete();
                        if (uploadUrl != null) {
//                        return uploadUrl;
                        }
                    }
                }
                fileUrlList1.setFileUrl(newFileHost+uploadUrl);
                fileUrlList1.setFileName(fileName);
                fileUrlList.add(fileUrlList1);

            }
            catch (Exception e)
            {
                return AjaxResult.error(e.getMessage());
            }
        }
        AjaxResult ajax = AjaxResult.success();
        ajax.put("fileUrlList",fileUrlList);
        return ajax;
    }

    @PostMapping("/common/uploadFileSlice")
    @PermitAll
    public AjaxResult uploadFileSlice(@RequestParam("data")MultipartFile data,@RequestParam("name")String name
            ,@RequestParam("md5")String md5,@RequestParam("current")int current,@RequestParam("total")int total,HttpServletRequest request
    ) throws IOException {
        AjaxResult ajaxResult = new AjaxResult();
        String basePath =  request.getSession().getServletContext().getRealPath("/") + "temp" + File.separatorChar + md5;
        File file= new File(basePath);
        if(!file.exists()) {
            file.mkdirs();
        }
        // 总分片数
        int combineFlag = 0;
        // 判断文件是否已经存在相关切片，如果存在跳过已有的切片，开始新的切片上传
        // 当前文件的路径
        String fileNamePath = basePath + File.separatorChar+ current;
        OutputStream out = new FileOutputStream(fileNamePath);
        // 获取文件的相关数据，然后写入到文件中
        byte[] bytes = data.getBytes();
        out.write(bytes);
        out.flush();
        out.close();
        System.out.println("文件目录为："+ basePath);
        File[] files = file.listFiles();
        if(files.length >= total) {
            String fileUrl = null;
            try {
                // 合并文件
                fileUrl = combineFiles(name, basePath);
                if(StringUtils.isNotBlank(fileUrl)) {
                    ajaxResult.put("filePath", fileUrl);
                    System.out.println(fileUrl);
                    combineFlag = 1;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(combineFlag == 0) {
                    // 合并失败
                    ajaxResult.put("errMessage", "合并文件失败");
                    ajaxResult.put("code", 500);
                } else {
                    ajaxResult.put("code", 200);
                }
                ajaxResult.put("next", current + 1);
            }
        }else{
            ajaxResult.put("code",0);
        }
        return ajaxResult;
    }

    private String combineFiles(String fileName, String basePath) throws Exception {
        int returnFlag = CombineFile(fileName, basePath);
        String fileUrl = null;
        if(returnFlag == 1) {
            // 将文件上传到oss或者是ftp,然后返回一个文件路径
            Date date = new Date();
            String datePath = DateUtils.formatDateByStyle(date, DatePattern.CN_DATE_BASIC_STYLE4.getDatePattern());
            String path = filePath + "video/" + datePath + "/" + fileName;
            fileUrl= aliyunOSSUtil.uploadFileSlice(basePath+"/"+fileName,path);
            fileUrl=newFileHost+fileUrl;
            // 将本地的文件删除
            File deleteFile = new File(basePath);
            delTempChild(deleteFile);
        }
        return fileUrl;
    }

    public static int CombineFile(String tar, String baseFilePath) throws Exception {
        File dirFile = new File(baseFilePath);
        FileInputStream fis;

        // 一次读取2M数据，将读取到的数据保存到byte字节数组中
        byte buffer[] = new byte[1024 * 1024 * 2];
        int len;
        // 判断file是否为目录
        if(dirFile.isDirectory()) {
            String[] fileNames = dirFile.list();
            FileOutputStream fos = new FileOutputStream(baseFilePath + File.separatorChar + tar);
            // 实现目录自定义排序
            Arrays.sort(fileNames, new StringComparator());
            for (int i = 0;i<fileNames.length ;i++){

                fis = new FileInputStream(baseFilePath + File.separatorChar + fileNames[i]);
                len = 0;
                while ((len = fis.read(buffer)) != -1) {
                    // buffer从指定字节数组写入。buffer:数据中的起始偏移量,len:写入的字数。
                    fos.write(buffer, 0, len);
                }
                fos.flush();
                fis.close();
            }
            fos.close();
        }
        return 1;
    }

    /**
     * 删除某个文件或者是某个目录
     * @param file 文件路径或者是文件夹路径
     */
    public static void delTempChild(File file){
        if (file.isDirectory()) {
            // 获取文件夹下所有子文件夹
            String[] children = file.list();
            //递归删除目录中的子目录下
            for (int i=0; i<children.length; i++) {
                delTempChild(new File(file, children[i]));
            }
        }
        // 目录空了，进行删除
        file.delete();
    }
}
