package net.northking.cctp.attachment.api.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dd.plist.NSArray;
import com.dd.plist.NSDictionary;
import com.dd.plist.NSString;
import com.dd.plist.PropertyListParser;
import net.dongliu.apk.parser.ApkFile;
import net.dongliu.apk.parser.bean.ApkMeta;
import net.northking.cctp.attachment.constant.ScriptConstant;
import net.northking.cctp.attachment.dto.FileMessage;
import net.northking.cctp.attachment.enums.FileError;
import net.northking.cctp.attachment.enums.PlatformType;
import net.northking.cctp.attachment.util.NorthKingIpaPngFix;
import net.northking.cctp.attachment.util.XMLUtils;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.*;
import java.util.zip.ZipInputStream;


@Service
public class FileServiceImpl implements FileService {
    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);


    @Override
    public FileMessage uploadAndroidLargeApp(MultipartFile multipartFile,String urlPath) {
        FileMessage fileMessage = new FileMessage();
        // 文件大小
        long size = 0;
        Map<String, Object> result = new HashMap<>();
        // 解析应用
        ApkFile apkFile = null;
        File file=null;
        try {
            String originalFileName = multipartFile.getOriginalFilename();
            String fileName[] = originalFileName.split("\\.");
            file = File.createTempFile(fileName[0],fileName[1]);
            multipartFile.transferTo(file);
            size = file.length();
            if (size == 0) {
                logger.info("APK文件为空");
                throw new PlatformRuntimeException(FileError.APP_IS_NULL);
            }
            try {
                apkFile = new ApkFile(file);
            } catch (IOException e) {
                logger.info("new ApkFile(file)解析APK异常信息：{}",e.getMessage());
                throw new PlatformRuntimeException(FileError.APP_RESOLUTION_ERROR);
            }
            apkFile.setPreferredLocale(Locale.SIMPLIFIED_CHINESE);
            ApkMeta apkMeta;
            try {
                apkMeta = apkFile.getApkMeta();
            } catch (IOException e) {
                logger.info("apkFile.getApkMeta()解析APK异常信息：{}",e.getMessage());
                throw new PlatformRuntimeException(FileError.APP_RESOLUTION_ERROR);
            }
            result.put(ScriptConstant.APP_SIZE, getAppSize(size));
            result.put(ScriptConstant.APP_NAME, apkMeta.getName());
            result.put(ScriptConstant.PACKAGE_NAME, apkMeta.getPackageName());
            result.put(ScriptConstant.BUILD_VERSION, apkMeta.getVersionName());
            result.put(ScriptConstant.PLATFORM, PlatformType.getCode(ScriptConstant.MOB_PLATFORM_ANDROID));
            result.put(ScriptConstant.APP_URL, urlPath);
            try {
                result.put(ScriptConstant.APP_ICON, apkFile.getAllIcons().get(0).getData());
            } catch (IOException e) {
                logger.error("",e);
                throw new PlatformRuntimeException(FileError.APP_ICON_GET_ERROR);
            }
            // 封装activity
            String activity = null;
            try {
                activity = getActtivity(apkFile);
            } catch (Exception e) {
                logger.info("getActtivity(apkFile)解析APK异常信息：{}",e.getMessage());
                throw new PlatformRuntimeException(FileError.APP_RESOLUTION_ERROR);
            }
            result.put(ScriptConstant.ACTIVITY, activity);

            Map<String, Object> manifest = new HashMap<>(8);
            manifest.put(ScriptConstant.MOB_PACKAGE, apkMeta.getPackageName());
            Map<String, Object> application = new HashMap<>(8);
            List<Map<String, String>> launcherActivities = new ArrayList<>();
            Map<String, String> params = new HashMap<>(8);
            params.put(ScriptConstant.NAME, activity);
            launcherActivities.add(params);
            application.put(ScriptConstant.LAUNCHER_ACTIVE, launcherActivities);
            manifest.put(ScriptConstant.APPLICATION, application);

            fileMessage.setFileSaveUrl(urlPath);
            fileMessage.setFileUrl(urlPath);
            fileMessage.setMessage(result);
            fileMessage.setManifest(manifest);
        } catch (PlatformRuntimeException e) {
            throw e;
        } catch (IOException e){
            logger.error("文件转换失败，e,{}",e);
        }finally {
            try {
                if (null != apkFile) {
                    apkFile.close();
                }
                if(file!=null){
                    boolean delete = file.delete();
                    if (!delete) {
                        logger.error("删除文件失败");
                    }
                }
            } catch (IOException e) {
                logger.error("",e);
            }
        }
        return fileMessage;
    }

    @Override
    public FileMessage uploadIosLargeApp(MultipartFile multipartFile,String urlPath) {
        FileMessage fileMessage = new FileMessage();
        Map<String, Object> map = new HashMap<>();
        long size = 0;
        File file =null;
        //解析应用
        try {
            String originalFileName = multipartFile.getOriginalFilename();
            String fileName[] = originalFileName.split("\\.");
            file = File.createTempFile(fileName[0],fileName[1]);
            multipartFile.transferTo(file);
            size = file.length();
            if (size == 0) {
                logger.info("IPA文件为空");
                throw new PlatformRuntimeException(FileError.APP_IS_NULL);
            }
            map = getVersionInfo(file);
        } catch (PlatformRuntimeException e) {
            throw e;
        } catch (Exception e) {
            logger.error("",e);
            throw new PlatformRuntimeException(FileError.IPA_UPLOAD_FAIL);
        }
        if (map.get(ScriptConstant.STATUS_ERR) != null) {
            throw new PlatformRuntimeException(FileError.IPA_UPLOAD_FAIL);
        }
        Map<String, Object> manifest = new HashMap<>(8);
        manifest.put(ScriptConstant.MOB_PACKAGE, map.get(ScriptConstant.PACKAGE_NAME));
        manifest.put(ScriptConstant.VERSION_CODE, map.get(ScriptConstant.VERSION_CODE));
        manifest.put(ScriptConstant.VERSION_NAME, map.get(ScriptConstant.BUILD_VERSION));
        fileMessage.setFileSaveUrl(urlPath);
        fileMessage.setFileUrl(urlPath);
        map.put(ScriptConstant.APP_URL, urlPath);
        map.put(ScriptConstant.APP_SIZE, getAppSize(size));
        fileMessage.setMessage(map);
        fileMessage.setManifest(manifest);
        try {
            boolean delete = file.delete();
            if (!delete) {
                logger.error("删除文件失败");
            }
        }catch (Exception e){
            logger.error("",e);
        }
        return fileMessage;
    }

    @Override
    public FileMessage uploadHarmonyOSLargeApp(MultipartFile multipartFile,String urlPath) {
        FileMessage fileMessage = new FileMessage();
        long size = 0;
        File file=null;
        InputStream inputStream = null;
        ZipInputStream zin = null;
        java.util.zip.ZipFile zf = null;
        FileInputStream fileInputStream = null;
        File apkFile = null;
        //解析应用
        try {
            String originalFileName = multipartFile.getOriginalFilename();
            String fileNames[] = originalFileName.split("\\.");
            file = File.createTempFile(fileNames[0],fileNames[1]);
            size = file.length();
            if (size == 0) {
                logger.info("hap文件为空");
                throw new PlatformRuntimeException(FileError.APP_IS_NULL);
            }
            //apk文件流
            InputStream apkIn = null;
            //apk文件名
            String apkFileName = null;
            //hapConfig
            String hapConfig = "";

            //设置编码
            Charset charset = Charset.forName(ScriptConstant.CHARSET_FOR_NAME);

            try {
                // hap包放进输入流
                fileInputStream = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                logger.error("",e);
            }
            inputStream = new BufferedInputStream(fileInputStream);
            // 压缩包输入流
            zin = new ZipInputStream(inputStream, charset);
            // 压缩包文件
            zf = new java.util.zip.ZipFile(file);

            //获取config以及apk文件
            java.util.zip.ZipEntry ze;
            while ((ze = zin.getNextEntry()) != null) {
                //遍历的当前文件名
                String fileName = ze.getName();
                //当遍历到config.json
                if (ScriptConstant.CONFIG_JSON_NAME.equals(fileName)) {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(zf.getInputStream(ze)));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        hapConfig += line;
                    }
                    if (reader != null) {
                        reader.close();
                    }
                }
                //当遍历到apk文件
                if (ScriptConstant.FILE_ENDWITH_APK.equals(fileName.substring(fileName.length() - 4))) {
                    apkIn = zf.getInputStream(ze);
                    apkFileName = fileName;
                }
            }
            // 解析
            // 将config.json转成map
            JSONObject apkConfigJsonObject = JSON.parseObject(hapConfig);

            // 将apk流转化为文件
            apkFile = new File(apkFileName);
            FileUtils.copyInputStreamToFile(apkIn, apkFile);
            //解析apk得到信息
            fileMessage = resolveApk(apkFile);
            Map<String, Object> message = fileMessage.getMessage();
            message.put(ScriptConstant.APP_SIZE, getAppSize(size));
            message.put(ScriptConstant.PLATFORM, PlatformType.getCode(ScriptConstant.PLATFORM_HARMONY));
            message.put(ScriptConstant.APP_URL, urlPath);
            fileMessage.setFileSaveUrl(urlPath);
            fileMessage.setFileUrl(urlPath);
            fileMessage.setMessage(message);

        } catch (PlatformRuntimeException e) {
            throw e;
        } catch (Exception e) {
            logger.error("",e);
            throw new PlatformRuntimeException(FileError.HAP_ERR);
        }finally {
            if (zf != null) {
                try {
                    zf.close();
                } catch (IOException e) {
                    logger.error("",e);
                }
            }
            if (zin != null) {
                try {
                    zin.close();
                } catch (IOException e) {
                    logger.error("",e);
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    logger.error("",e);
                }
            }
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    logger.error("",e);
                }
            }
            if (apkFile != null && apkFile.exists()) {
                boolean delete = apkFile.delete();
                if (!delete) {
                    logger.error("删除文件失败");
                }
            }
            if (file != null) {
                boolean delete = file.delete();
                if (!delete) {
                    logger.error("删除文件失败");
                }
            }
        }
        return fileMessage;
    }

    private String getActtivity(ApkFile apkFile) throws Exception {
        Document doc = XMLUtils.getDocument(apkFile.getManifestXml());
        return XMLUtils.getValue(doc, ScriptConstant.X_PATH);
    }

    public static String getAppSize(long size) {
        DecimalFormat df = new DecimalFormat(ScriptConstant.DECIMAL_FORMAT);
        if (size / (long) (1024 * 1024 * 1024) >= 1) {//GB

            return df.format((double) size / (double) (1024 * 1024 * 1024)) + ScriptConstant.APP_SIZE_GB;

        } else if (size / (long) (1024 * 1024) >= 1 && size / (long) (1024 * 1024 * 1024) < 1) {//MB

            return df.format((double) size / (double) (1024 * 1024)) + ScriptConstant.APP_SIZE_MB;

        } else if (size / (long) (1024) >= 1 && size / (long) (1024 * 1024) < 1) {//KB

            return df.format((double) size / (double) (1024)) + ScriptConstant.APP_SIZE_KB;

        } else if (size / (long) (1024) < 1) {

            return size + ScriptConstant.APP_SIZE_B;

        }
        return ScriptConstant.APP_SIZE_DEFAULT;
    }

    public  Map<String, Object> getVersionInfo(File ipaFile) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        InputStream infoIs = null;
        ZipFile zipFile = new ZipFile(ipaFile, "utf-8");
        Enumeration<ZipEntry> entries = zipFile.getEntries();
        NSDictionary rootDict = null;
        String icon = null;
        while (entries.hasMoreElements()) {
            ZipEntry zipEntry = entries.nextElement();
            if (!zipEntry.isDirectory()) {
                String name = zipEntry.getName();
                logger.info(name);
                if (null != name && name.toLowerCase().contains(".app/info.plist")&&name.split("/").length==3) {
                    ByteArrayOutputStream _copy = new ByteArrayOutputStream();
                    int chunk = 0;
                    byte[] data = new byte[1024];
                    infoIs = zipFile.getInputStream(zipEntry);
                    while (-1 != (chunk = infoIs.read(data))) {
                        _copy.write(data, 0, chunk);
                    }
                    infoIs = new ByteArrayInputStream(_copy.toByteArray());
                    rootDict = (NSDictionary) PropertyListParser.parse(infoIs);

                    //我们可以根据info.plist结构获取任意我们需要的东西
                    //比如下面我获取图标名称，图标的目录结构请下面图片
                    //获取图标名称
                    NSDictionary iconDict = (NSDictionary) rootDict.get("CFBundleIcons");
                    while (null != iconDict) {
                        if (iconDict.containsKey("CFBundlePrimaryIcon")) {
                            NSDictionary CFBundlePrimaryIcon = (NSDictionary) iconDict.get("CFBundlePrimaryIcon");
                            if (CFBundlePrimaryIcon.containsKey("CFBundleIconFiles")) {
                                NSArray CFBundleIconFiles = (NSArray) CFBundlePrimaryIcon.get("CFBundleIconFiles");
                                icon = CFBundleIconFiles.getArray()[0].toString();
                                if (icon.contains(".png")) {
                                    icon = icon.replace(".png", "");
                                }
                                break;
                            }
                        }
                    }
                    break;
                }
            }
        }
        String data = null;
        Enumeration<ZipEntry> entries2 = zipFile.getEntries();
        while (entries2.hasMoreElements()) {
            ZipEntry zipEntry = entries2.nextElement();
            if (!zipEntry.isDirectory()) {
                String name = zipEntry.getName();
                if (!StringUtils.isEmpty(icon) && name.contains(icon.trim())) {
                    infoIs = zipFile.getInputStream(zipEntry);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    int i = 0;
                    byte[] bytes = new byte[1024];
                    while ((i = infoIs.read(bytes))!=-1){
                        byteArrayOutputStream.write(bytes,0,i);
                    }
                    byte[] byteArray = byteArrayOutputStream.toByteArray();
                    NorthKingIpaPngFix fix = new NorthKingIpaPngFix(byteArray);
                    byte[] convert = fix.convert("png");
                    byteArrayOutputStream.flush();
                    byteArrayOutputStream.close();
                    logger.info("读取的文件名字：{}",name);
                    logger.info("读取的流是否为空：{}",byteArray == null);
                    logger.info("转换后的流是否为空：{}",convert==null);
                    byte[] baseByte = Base64.getEncoder().encode(convert);
                    String base64 = new String(baseByte, StandardCharsets.UTF_8);
                    data = base64;
                    break;
                }
            }
        }
        // 应用包名
        NSString parameters = (NSString) rootDict.get("CFBundleIdentifier");
        map.put("packageName", parameters.toString());

        // 应用名称
        parameters = (NSString) rootDict.objectForKey("CFBundleName");
        map.put("appName", parameters.toString());


        // 应用版本名
        parameters = (NSString) rootDict.objectForKey("CFBundleShortVersionString");
        map.put("buildVersion", parameters.toString());
        map.put("platform", PlatformType.getCode("ios"));



        parameters = (NSString) rootDict.objectForKey("CFBundleInfoDictionaryVersion");
        if (parameters!=null){
            float f=Float.valueOf(parameters.toString());
            int i=(int)f;
            map.put("versionCode",i);
        }else{
            map.put("versionCode",6);
        }
        if (data!=null&&data.length()>0){
            map.put("appIcon", data);
        }
        return map;
    }

    public FileMessage resolveApk(File file) {
        FileMessage fileMessage = new FileMessage();
        Map<String, Object> result = new HashMap<>();
        //解析应用
        ApkFile apkFile = null;
        try {
            try {
                apkFile = new ApkFile(file);
            } catch (IOException e) {
                logger.error("",e);
                throw new PlatformRuntimeException(FileError.APK_READ_ERR);
            }
            apkFile.setPreferredLocale(Locale.SIMPLIFIED_CHINESE);
            ApkMeta apkMeta;
            try {
                apkMeta = apkFile.getApkMeta();
            } catch (IOException e) {
                logger.error("",e);
                throw new PlatformRuntimeException(FileError.APP_RESOLUTION_ERROR);
            }
            result.put(ScriptConstant.APP_NAME, apkMeta.getName());
            result.put(ScriptConstant.PACKAGE_NAME, apkMeta.getPackageName());
            result.put(ScriptConstant.BUILD_VERSION, apkMeta.getVersionName());
            result.put(ScriptConstant.PLATFORM, PlatformType.getCode(ScriptConstant.MOB_PLATFORM_ANDROID));
            try {
                result.put(ScriptConstant.APP_ICON, apkFile.getAllIcons().get(0).getData());
            } catch (IOException e) {
                logger.error("",e);
                throw new PlatformRuntimeException(FileError.APP_ICON_GET_ERROR);
            }
            //封装activity
            String activity = null;
            try {
                activity = getActtivity(apkFile);
            } catch (Exception e) {
                logger.error("",e);
                throw new PlatformRuntimeException(FileError.APP_RESOLUTION_ERROR);
            }
            result.put(ScriptConstant.ACTIVITY, activity);
            Map<String, Object> manifest = new HashMap<>(8);
            manifest.put(ScriptConstant.MOB_PACKAGE, apkMeta.getPackageName());
            Map<String, Object> application = new HashMap<>(8);
            List<Map<String, String>> launcherActivities = new ArrayList<>();
            Map<String, String> params = new HashMap<>(8);
            params.put(ScriptConstant.NAME, activity);
            launcherActivities.add(params);
            application.put(ScriptConstant.LAUNCHER_ACTIVE, launcherActivities);
            manifest.put(ScriptConstant.APPLICATION, application);


            fileMessage.setMessage(result);
            fileMessage.setManifest(manifest);
        } catch (Exception e) {
            logger.error("",e);
            throw new PlatformRuntimeException(FileError.APK_UPLOAD_FAIL);
        } finally {
            try {
                if (null != apkFile) {
                    apkFile.close();
                }
            } catch (IOException e) {
                logger.error("",e);
            }

        }
        return fileMessage;
    }
}
