package com.keyware.shandan.system.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.keyware.shandan.bianmu.service.DataLabelsService;
import com.keyware.shandan.common.controller.BaseController;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.FileDownload;
import com.keyware.shandan.common.util.PoiFileReadUtil;
import com.keyware.shandan.frame.properties.CustomProperties;
import com.keyware.shandan.system.constants.FormTypeEnum;
import com.keyware.shandan.system.entity.SysFile;
import com.keyware.shandan.system.entity.SysFileChunk;
import com.keyware.shandan.system.entity.SysFormConfig;
import com.keyware.shandan.system.entity.SysSetting;
import com.keyware.shandan.system.service.SysFileService;
import com.keyware.shandan.system.service.SysFormConfigService;
import com.keyware.shandan.system.service.SysSettingService;
import com.keyware.shandan.system.utils.DictUtil;
import com.keyware.shandan.system.utils.FileChunkUploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * 系统文件表 前端控制器
 * </p>
 *
 * @author GuoXin
 * @since 2021-06-07
 */
@RestController
@RequestMapping("/sys/file")
public class SysFileController extends BaseController<SysFileService, SysFile, String> {
    final String[] imageTypes = {".avif", ".bmp", ".gif", ".jpg", ".jpeg", ".jfif", ".pjpeg", ".pjp", ".png", ".svg", ".webp", ".ico", ".cur"};

    @Autowired
    private SysFileService sysFileService;

    @Autowired
    private SysSettingService settingService;

    @Autowired
    private CustomProperties customProperties;

    @Autowired
    private DataLabelsService labelsService;
    @Autowired
    private SysFormConfigService formConfigService;

    @GetMapping("/layer")
    public ModelAndView fileUploadLayer(ModelAndView mav) {
        mav.setViewName("sys/file/fileUploadLayer");
        mav.addObject("LabelConfigSet", labelsService.formConfigList());

        mav.addObject("uploadFormConfig", formConfigService.listByFormType(FormTypeEnum.fileUploadForm));
        return mav;
    }

    @GetMapping("/layer/dir")
    public ModelAndView fileUploadLayerDir(ModelAndView mav) {
        mav.setViewName("sys/file/dirUploadLayer");
        mav.addObject("LabelConfigSet", labelsService.formConfigList());
        return mav;
    }

    @GetMapping("/edit")
    public ModelAndView fileEditLayer(ModelAndView mav, String id) {
        mav.setViewName("sys/file/fileEditLayer");
        SysFile file = sysFileService.getById(id);
        mav.addObject("file", file);
        mav.addObject("LabelConfigSet", labelsService.formConfigList());
        return mav;
    }

    @GetMapping("/view")
    public ModelAndView fileViewer(ModelAndView mav, @RequestParam String fileId) {
        mav.setViewName("sys/file/fileView");
        SysFile file = sysFileService.getById(fileId);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String inputDate = "";
        if (file.getInputDate() != null) {
            inputDate = sdf.format(file.getInputDate());
        }

        SysSetting bianmuSetting = settingService.getById("BIANMU");
        String url_prefix = bianmuSetting.getSysInternalAddress();
        if (file.getFileSuffix().equalsIgnoreCase(".mp3")
                || file.getFileSuffix().equalsIgnoreCase(".wav")
                || file.getFileSuffix().equalsIgnoreCase(".avi")
                || file.getFileSuffix().equalsIgnoreCase(".mp4")
                || file.getFileSuffix().equalsIgnoreCase(".flv")) {
            url_prefix = bianmuSetting.getSysAddress();
        }
        String fileViewUrl = url_prefix + file.getWebUri();
        if (Arrays.asList(imageTypes).contains(file.getFileSuffix().toLowerCase())) {
            fileViewUrl = bianmuSetting.getSysAddress() + file.getWebUri();
        }
        String downloadUrl = bianmuSetting.getSysAddress() + "/sys/file/download/" + fileId;

        mav.addObject("fileViewUrl", fileViewUrl.replace("\\", "/"));
        mav.addObject("downloadUrl", downloadUrl);
        mav.addObject("fileData", file);
        mav.addObject("file_inputDate", inputDate);
        String fileSize = FileUtil.readableFileSize((long) (file.getFileSize() * 1024 * 1024));
        if (fileSize.endsWith(" B")) {
            fileSize += "yte";
        }
        mav.addObject("readableFileSize", fileSize);
        mav.addObject("LabelConfigSet", labelsService.formConfigList());
        return mav;
    }

    /**
     * 文件上传
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result<SysFile> upload(MultipartFile file, SysFile fileVo) {
        try {
            return Result.of(sysFileService.uploadFiles(file, fileVo));
        } catch (Exception e) {
            e.printStackTrace();
            return Result.of(null, false, "文件上传服务异常");
        }
    }

    /**
     * 根据文件的MD5值判断文件是否已经上传
     * 由于MD5值是仅是基于文件内容得出的值，并不包括文件名称，所以需要在根据文件名称判断，是否需要以新的文件名称重新保存一条记录到数据库
     *
     * @param fileMd5 文件的MD5值，用于判断文件是否已经上传
     * @param name    文件名
     * @return 结果集
     */
    @PostMapping("/upload/check")
    public Result<Object> uploadFileCheck(@RequestBody String fileMd5,
                                          @RequestBody(required = false) String name) {
        Result<Object> result = Result.of(null, true);
        SysFile file = sysFileService.getFileByMd5AndName(fileMd5, name);
        if (file != null) {
            // code=1 文件已存在
            result.setCode(1);
            result.setData(file);
        } else {
            // code = 0 文件不存在
            result.setCode(0);
            // 查询文件上传的缓存
            result.setData(FileChunkUploadUtil.exists(fileMd5));
        }
        return result;
    }

    /**
     * 大文件分片上传
     *
     * @param file      分片文件
     * @param fileChunk 分片数据
     * @return 结果集
     */
    @PostMapping("/upload/chunk")
    public Result<Object> uploadChunk(MultipartFile file, SysFile fileInfo, SysFileChunk fileChunk) throws Exception {
        return Result.of(FileChunkUploadUtil.uploadChunk(file, fileInfo, fileChunk));

    }

    /**
     * 文件分片上传完成，需要自动生成目录结构，并保存目录文件关系
     *
     * @param file 文件信息
     * @return 结果
     */
    @PostMapping("/upload/chunk/complete")
    public Result<Object> mergerChunk(SysFile file) throws Exception {
        FileChunkUploadUtil.mergeChunk(file, sysFile -> sysFileService.save(sysFile));
        return Result.of(sysFileService.autoCreateDirAndUpdateFile(file));
    }

    /**
     * 文件下载
     *
     * @param response
     * @param fileId
     * @return
     */
    @GetMapping("/download/{fileId}")
    public String download(HttpServletResponse response, @PathVariable String fileId) throws IOException {
        SysFile sysFile = sysFileService.getById(fileId);
        Assert.notNull(sysFile, "文件不存在");
        File file = FileUtil.file(customProperties.getFileStorage().getPath() + "/" + sysFile.getPath());
        return FileDownload.download(response, file, sysFile.getFileName());
    }

    /**
     * 下载文件的标签数据
     *
     * @param response
     * @param fileId
     * @return
     * @throws IOException
     */
    @GetMapping("/download/label/{fileId}")
    public String downloadLabel(HttpServletResponse response, @PathVariable String fileId) throws IOException {
        SysFile sysFile = sysFileService.getById(fileId);
        Assert.notNull(sysFile, "文件信息不存在");
        return FileDownload.download(response, generateLabelFile(sysFile), true);
    }

    /**
     * 构建文件和标签文件压缩包
     *
     * @param sysFile 文件数据
     * @return
     * @throws IOException
     */
    private File buildFileAndFileLabels(SysFile sysFile) throws IOException {
        File file = FileUtil.file(customProperties.getFileStorage().getPath() + "/" + sysFile.getPath());
        String parentFilePath = file.getParentFile().getAbsolutePath();
        String zipName = sysFile.getFileName().replace(sysFile.getFileSuffix(), ".zip");
        if (!zipName.endsWith(".zip")) {
            zipName += ".zip";
        }

        return ZipUtil.zip(FileUtil.file(parentFilePath + zipName), false, file, generateLabelFile(sysFile));
    }

    /**
     * 生成置标标签文件
     *
     * @param sysFile 文件数据
     * @return 生成的文件
     * @throws IOException
     */
    private File generateLabelFile(SysFile sysFile) throws IOException {
        String fileName = sysFile.getFileName().replace(sysFile.getFileSuffix(), "_标签数据.txt");
        File file = FileUtil.file(customProperties.getFileStorage().getPath() + "/" + sysFile.getPath());
        File labelFile = FileUtil.file(file.getParentFile().getAbsolutePath() + File.separator + fileName);
        labelFile.createNewFile();
        Set<String> lines = new HashSet<>();
        List<SysFormConfig> formConfig = formConfigService.listByFormType(FormTypeEnum.fileUploadForm);
        formConfig.forEach(conf -> {
            Object filedValue = ReflectUtil.getFieldValue(sysFile, conf.getFieldName());
            String value = String.valueOf(filedValue);
            if(filedValue != null){
                switch (conf.getFieldType()) {
                    case "dict":
                        value = DictUtil.getDictName(conf.getDictType(), value);
                        break;
                    case "radio": // 0-否-;1-是-
                        String partner = conf.getDictType();
                        String[] ss = partner.split(";");
                        for (String s : ss) {
                            String p = value + "-";
                            if (s.startsWith(p)) {
                                value = s.replace(p, "").replace("-", "");
                                break;
                            }
                        }
                        break;
                    case "date":
                        Date date = (Date) filedValue;
                        value = date == null ? "" : DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
                        break;
                    case "input":
                    case "textarea":
                    default:
                        value = filedValue.toString();

                }
            }else{
                value = "";
            }

            lines.add(conf.getShowTitle() + ": " + value);
        });
        sysFile.getLabels().forEach(label -> {
            lines.add(label.getFieldTitle() + ": " + label.getLabel());
        });
        FileUtil.writeLines(lines, labelFile, "UTF-8");
        return labelFile;
    }

}
