package com.yizhi.application.course.util;

import com.alibaba.fastjson.JSON;

import com.yizhi.core.application.exception.FileReadException;
import com.yizhi.course.application.vo.ScormVO;
import com.yizhi.util.application.zip.ZipUtil;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @Author: shengchenglong
 * @Date: 2018/1/22 14:41
 */
@Component
public class ScormXMLImporter {

    @Value("${ACTIVE}")
    public String active;

    @PostConstruct
    public void init(){
        try {
            String os = System.getProperty("os.name");
            boolean isWindows = os.toLowerCase().startsWith("windows");
            LOG.info("操作系统信息="+os);
            if(!isWindows){
                LOG.info("执行结果={}",CommandUtils.exec("yum", "-y" ,"install", "unzip"));
            }
        } catch (IOException e) {
            LOG.error("准备安装unzip失败");
        }
    }

    private static final Logger LOG = LoggerFactory.getLogger(ScormXMLImporter.class);


   /* @Value("${ACTIVE}")
    private String active;*/

    /**
     * 获取路径下的所有文件/文件夹
     *
     * @param directoryPath  需要遍历的文件夹路径
     * @param isAddDirectory 是否将子文件夹的路径也添加到list集合中
     * @return
     */
    public static List<String> getAllFile(String directoryPath, boolean isAddDirectory) {
        List<String> list = new ArrayList<String>();
        File baseFile = new File(directoryPath);
        if (baseFile.isFile() || !baseFile.exists()) {
            return list;
        }
        File[] files = baseFile.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                if (isAddDirectory) {
                    list.add(file.getAbsolutePath());
                }
                list.addAll(getAllFile(file.getAbsolutePath(), isAddDirectory));
            } else {
                list.add(file.getAbsolutePath());
            }
        }
        return list;
    }

    /**
     * 解析 Scorm xml 文件
     * @param urlStr 文件oss上的路径（前端上传完）
     * @param savePath 本地保存路径
     * @param fileName 上传的文件名
     * @param materialId 素材id
     * @return
     * @throws Exception
     */
    public ScormVO scormParse(String urlStr, String savePath, String fileName, Long materialId) throws Exception {

        // 获取oss的文件名
        fileName = Paths.get(new URL(urlStr).getPath()).getFileName().toString();
        /*System.out.println("URL 为：" + url.toString());
        System.out.println("协议为：" + url.getProtocol());
        System.out.println("验证信息：" + url.getAuthority());
        System.out.println("文件名及请求参数：" + url.getFile());
        System.out.println("主机名：" + url.getHost());
        System.out.println("路径：" + url.getPath());
        System.out.println("端口：" + url.getPort());
        System.out.println("默认端口：" + url.getDefaultPort());
        System.out.println("请求参数：" + url.getQuery());
        System.out.println("定位位置：" + url.getRef());*/

        savePath =  savePath + File.separator + materialId;
        LOG.info("文件保存路径 = {}，fileName={}",savePath, fileName);

        // 把前端oss上传的文件下载到本地目录（返回下载到本地的file对象）
        File fileInfo = read(urlStr, savePath, fileName);
        LOG.info("oss文件下载到本地的路径={}",fileInfo.getAbsolutePath());

        // scorm 文件目录(解压后都放在这里面)
        File scormBaseDir = new File(savePath);
        LOG.info("scorm文件保存的根目录={}",scormBaseDir.getAbsolutePath());

        // 若 scorm 文件夹不存在
        if (!scormBaseDir.exists()) {
            if (!scormBaseDir.mkdirs()) {
                throw new Exception("scorm 文件夹不存在，且创建失败！");
            }
        }

        // 没有后缀的文件名
        String fileNameWithoutSuffix = fileInfo.getName().substring(0, fileInfo.getName().lastIndexOf("."));
        LOG.info("oss文件下载到本地的路径的文件名，去掉后缀的文件名{}",fileNameWithoutSuffix);
        //String randomPath = UUID.randomUUID().toString().replace("-", "")+((int)(Math.random()*100));

        // 当前 scorm 课件解压后的根目录
        File scormDir = new File(scormBaseDir.getAbsolutePath() + File.separator + fileNameWithoutSuffix);
        LOG.info("scorm文件将要解压到{}目录",scormDir.getAbsolutePath());

        String os = System.getProperty("os.name");
        boolean isWindows = os.toLowerCase().startsWith("windows");
        if(isWindows){
            ZipUtil.unZip(fileInfo.getAbsolutePath(), scormDir.getAbsolutePath());
        }else{
            //CompressionUtils.unzipJDK8(fileInfo.getAbsolutePath(), scormDir.getAbsolutePath());
            //String resut = CommandUtils.exec("mkdir","-p",scormDir.getAbsolutePath());
            CompressionUtils.createDirectory(scormDir.getAbsolutePath(),"");
            //LOG.info("执行结果="+resut);
            String resut = CommandUtils.exec("unzip","-d",scormDir.getAbsolutePath(),fileInfo.getAbsolutePath());
            LOG.info("执行结果="+resut);
            //CommandUtils.exec("unzip","-o",fileInfo.getAbsolutePath(),"-d",scormDir.getAbsolutePath());
        }
        LOG.info("把本地的scorm文件{}解压到{}目录",fileInfo.getAbsolutePath(),scormDir.getAbsolutePath());

        File scormFile = new File(scormDir.getAbsolutePath() + "/imsmanifest.xml");
        LOG.info("scorm描述文件的路径={}",scormFile.getAbsolutePath());
        File oldScorm = scormDir;
        //评出在oss上的路径
        String path =  File.separator + materialId + File.separator + fileNameWithoutSuffix;
        LOG.info("定义文件上传到oss的path路径 = {}",path);
        if (!scormFile.exists()) {
            LOG.info("不存在scorm描述文件的路径={}，准备创建",scormFile.getAbsolutePath());
            if(!scormDir.getAbsolutePath().endsWith(fileNameWithoutSuffix)){
                scormDir = new File(scormDir.getAbsolutePath() + File.separator + fileNameWithoutSuffix );
            }
            if(!path.endsWith(fileNameWithoutSuffix)){
                path = path + File.separator + fileNameWithoutSuffix;
            }
            //scormFile.createNewFile();
        }

        if (scormDir.exists()) {

            // 旧的文件遍历上传
            /*File[] scormItems = scormDir.listFiles();
            if (scormItems.length > 0) {
                List<String> fileList = getAllFile(oldScorm.getAbsolutePath(), Boolean.FALSE);
                LOG.info("打印将要上传的本地地址");
                fileList.stream().forEach(System.out::println);
                List<String> uploadFileList = new ArrayList<>();
                for (String fileStr : fileList) {
                    if (uploadFileList.size() > 10) {
                        ScormThread scormThread = new ScormThread(fileList);
                        scormThread.start();
                        uploadFileList.clear();
                    } else {
                        uploadFileList.add(fileStr);
                    }
                }
                if (uploadFileList.size() > 0) {
                    ScormThread scormThread = new ScormThread(fileList);
                    scormThread.start();
                    uploadFileList.clear();
                }

                for (File item : scormItems) {
                    if (item.getName().equalsIgnoreCase("imsmanifest.xml")) {
                        return analysisScorm(item, path);
                    }
                }
            }*/
            List<String> filesData = PathUtils.getPathsByDirectory(scormDir.getAbsolutePath());
            List<BatchUploadUtils> batchUploadUtils = null;
            if(!CollectionUtils.isEmpty(filesData)){
                String activeTmp = Objects.isNull(active)?"":active.toLowerCase();
                String imsmanifestPath = null;
                LOG.info("打印将要上传的本地地址");
                filesData.stream().forEach(System.out::println);
                batchUploadUtils = new ArrayList<>();
                for(String fileStr : filesData){
                    //String key = "scorm" + fileStr.split("scorm")[1].replaceAll("\\\\", "/");
                    String key = fileStr.substring(fileStr.indexOf("scorm"));

                    BatchUploadUtils batchUpload = new BatchUploadUtils(activeTmp,fileStr,key);
                    batchUploadUtils.add(batchUpload);
                    if (fileStr.toLowerCase().endsWith("imsmanifest.xml")) {
                        imsmanifestPath = fileStr;
                    }
                }
                List<CompletableFuture<String>> futures =batchUploadUtils.stream().map(batchObj-> CompletableFuture.supplyAsync(
                        () -> batchObj.upload(true)))
                        .collect(Collectors.toList());
                List<String> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
                LOG.info("打印OSS上传完成的网络地址");
                results.stream().forEach(System.out::println);// 打印上传完的oss地址
                if(!StringUtils.isEmpty(imsmanifestPath)){
                    ScormVO scormRet = analysisScorm(new File(imsmanifestPath), path);
                    LOG.info("解析完scorm课程，最终分析结果={}", JSON.toJSONString(scormRet));
                    try {
                        Files.delete(Paths.get(scormDir.getAbsolutePath()));
                    }catch (IOException e){
                        LOG.info("删除解压的文件夹失败{}",e);
                    }

                    return scormRet;
                }
            }

        } else {
            LOG.info("scorm文件将要解压到{}目录不存在",scormDir.getAbsolutePath());
            LOG.error("{} 课件包为空！", fileInfo.getName());
            throw new Exception(fileInfo.getName() + "课件包为空！");
        }
        return null;
    }

    /**
     *
     * @param scormDirName
     * @return
     * @throws Exception
     */

    /**
     * 解析 scorm 课件包中的 xml
     *
     * @param xml 课件包解压后的 xml 文件
     * @return
     * @throws Exception
     */
    private ScormVO analysisScorm(File xml, String name) throws Exception {
        ScormVO scorm = new ScormVO();

        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(xml);

        //获取根节点对象
        Element rootElement = document.getRootElement();

        Element organizationsEle = getElement(rootElement, "organizations");
        Element organizationEle = getElement(organizationsEle, "organization");

        String organizationsEleDefault = organizationsEle.attributeValue("default");
        String organizationEleIdentifier = organizationEle.attributeValue("identifier");
        if (organizationsEleDefault == null || organizationEleIdentifier == null || !(organizationsEleDefault.equals(organizationEleIdentifier))) {
            throw new Exception("文档默认使用的目录的 Id 和 一级目录唯一标识 Id 有一为空 或 不相同！");
        }

        Element organizationTitleEle = getElement(organizationEle, "title");
        Element itemEle = getElement(organizationEle, "item");
        Element itemTitleEle = getElement(itemEle, "title");

        Element resourcesEle = getElement(rootElement, "resources");
        Element resourceEle = getElement(resourcesEle, "resource");
//        List<Element> fileEles = resourceEle.elements("file");
//
//        if (CollectionUtils.isEmpty(fileEles)) {
//            throw new Exception("file节点为空！");
//        }

        scorm.setOrganizationTitle(organizationTitleEle.getText());
        scorm.setOrganizationIdentifier(organizationEleIdentifier);
        scorm.setOrganizationItemTitle(itemTitleEle.getText());
        scorm.setOrganizationItemIdentifier(itemEle.attributeValue("identifier"));
        scorm.setOrganizationItemIdentifierref(itemEle.attributeValue("identifierref"));
        scorm.setResourceIdentifier(resourceEle.attributeValue("identifier"));
        // 保存成绝对路径
        scorm.setResourceFiles(xml.getParent() + File.separator + resourceEle.attributeValue("href"));
        String path = "";
        // scorm 播放url 课程去掉域名
        /*if ("dev".equals(active)) {
            path = UtilConstants.SCORM_URL_PATH_DEV;
        } else if ("sit".equals(active)) {
            path = UtilConstants.SCORM_URL_PATH_SIT;
        } else if ("uat".equals(active)) {
            path = UtilConstants.SCORM_URL_PATH_UAT;
        } else {
            path = UtilConstants.SCORM_URL_PATH_PROD;
        }*/
        path = "/scorm";
        scorm.setResourceHref(path + File.separator + name + File.separator + resourceEle.attributeValue("href"));
//        scorm.setResourceFiles(getResourcesFileHref(fileEles));

        return scorm;
    }

    /**
     * 获取子节点
     *
     * @param parent
     * @param child
     * @return
     * @throws Exception
     */
    private Element getElement(Element parent, String child) throws Exception {
        Element e = parent.element(child);
        if (null == e) {
            throw new Exception(child + "节点为空！");
        }
        return e;
    }

    /**
     * 获取 files 路径字符串，由逗号分隔
     *
     * @param fileEles
     * @return
     */
    private String getResourcesFileHref(List<Element> fileEles) {
        StringBuilder sb = new StringBuilder();
        for (Element e : fileEles) {
            sb.append(",");
            sb.append(e.attributeValue("href"));
        }
        return sb.substring(1);
    }

    /**
     * @param urlStr 前端上传文件到oss的url
     * @param savePath 本地保存的地址
     * @param fileName 上传的资源文件名称
     * @return fileInfo 的 properties 中含有一个 absolutePath
     */
    public File read(String urlStr, String savePath, String fileName) {

        File file = null;
//        FileInfo fileInfo = new FileInfo();
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //防止屏蔽程序抓取而返回403错误
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            conn.setRequestProperty("Charsert", "UTF-8");
            conn.setRequestProperty("Accept-Charset", "UTF-8");
            //得到输入流
            InputStream inputStream = conn.getInputStream();
            //获取自己数组
            byte[] buffer = new byte[1024];
            int len = 0;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            while ((len = inputStream.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.close();

            byte[] getData = bos.toByteArray();
            //文件保存位置
            File saveDir = new File(savePath);
            if (!saveDir.exists()) {
                saveDir.mkdirs();
            }
            file = new File(saveDir + File.separator + fileName);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(getData);
            if (fos != null) {
                fos.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }

//            fileInfo=new FileInfo();
//            fileInfo.setContent(new FileInputStream(file));
//            // 判断文件类型
//            fileInfo.setFileName(file.getName());
//            String fileType = file.getName().substring(fileName.lastIndexOf(".") + 1, fileName.length());
//            fileInfo.setFileType(fileType);
//
//            String[] properties = new String[1];
//            properties[0] = file.getAbsolutePath();
//            fileInfo.setProperties(properties);
        } catch (Exception e) {
            LOG.error("文件解析失败", e);
            throw new FileReadException();
        }
        return file;
    }
}
