package ai.guiji.duix.test.service;

import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import android.util.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import ai.guiji.duix.sdk.client.util.MD5Util;
import ai.guiji.duix.test.net.SyncDownloadFile;
import ai.guiji.duix.test.util.StringUtils;
import ai.guiji.duix.test.util.ZipUtil;


public class StorageService {

    public interface Callback {

        void onDownloadProgress(int progress);

        void onUnzipProgress(int progress);

        void onComplete(String path);

        void onError(String msg);
    }

    /**
     * 下载zip文件并解压
     *
     * @param context
     * @param url
     * @param targetPath
     * @param uuid
     * @param callback
     */
    public static void downloadAndUnzip(Context context, String url, String targetPath, String uuid, Callback callback, boolean deleteZip) {
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            File cacheDir = context.getExternalCacheDir();
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            File zipFile = new File(cacheDir, MD5Util.string2MD5(url));
            boolean result = true;
            if (!zipFile.exists()) {
                Log.d("123", "zip not found, try download.");
                result = new SyncDownloadFile(url, zipFile.getAbsolutePath(), callback::onDownloadProgress).download();
                Log.d("123", "download file success.");
            } else {
                Log.d("123", "found cache zip file.");
            }
            if (result) {
                Log.e("123", "try unzip file.");
                File targetDirFile = new File(targetPath);
                if (targetDirFile.exists()) {
                    Log.e("123", "delete old files.");
                    deleteContents(targetDirFile);
                }
                // 拿到目标路径的父级
                File targetParentDir = new File(targetPath).getParentFile();
                if (!targetParentDir.exists()) {
                    targetParentDir.mkdirs();
                }
                result = ZipUtil.unzip(zipFile.getAbsolutePath(), targetParentDir.getAbsolutePath(), callback::onUnzipProgress);
                if (result) {
                    Log.d("123", "unzip file complete.");
                    // 这里时候targetDirFile应该是存在的
                    if (targetDirFile.exists()) {
                        File uuidFile = new File(targetDirFile, "uuid");
                        try {
                            OutputStream out = new FileOutputStream(uuidFile);
                            byte[] uuidBytes = uuid.getBytes();
                            out.write(uuidBytes, 0, uuidBytes.length);
                            out.flush();
                            out.close();
                            if (deleteZip && zipFile.exists()){
                                zipFile.delete();
                            }
                            callback.onComplete(targetPath);
                        } catch (Exception e) {
                            callback.onError("touch uuid file error!");
                        }
                    } else {
                        callback.onError("unzip dir not found!");
                    }
                } else {
                    if (zipFile.exists()){
                        zipFile.delete();
                    }
                    callback.onError("unzip file error!");
                }
            } else {
                callback.onError("zip file download error");
            }
        });
    }

    /**
     * 从assets拷贝文件到sdcard
     *
     * @param context
     * @param sourcePath 在assets目录的路径
     * @param targetPath 在sd卡的路径
     */
    public static void unpack(Context context, String sourcePath, final String targetPath, final Callback callback) {
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            try {
                final String outputPath = sync(context, sourcePath, targetPath);
                callback.onComplete(outputPath);
            } catch (final IOException e) {
                callback.onError("拷贝文件异常: " + e);
            }
        });
    }

    public static boolean deleteContents(File dir) {
        File[] files = dir.listFiles();
        boolean success = true;
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    success &= deleteContents(file);
                }
                if (!file.delete()) {
                    success = false;
                }
            }
        }
        return success;
    }

    private static String sync(Context context, String sourcePath, String targetPath) throws IOException {

        AssetManager assetManager = context.getAssets();

        File externalFilesDir = context.getExternalFilesDir(null);
        if (externalFilesDir == null) {
            throw new IOException("cannot get external files dir, "
                    + "external storage state is " + Environment.getExternalStorageState());
        }

        File targetDir = new File(externalFilesDir, targetPath);
        String resultPath = new File(targetDir, sourcePath).getAbsolutePath();
        String sourceUUID = readLine(assetManager.open(sourcePath + "/uuid"));
        try {
            String targetUUID = readLine(new FileInputStream(new File(targetDir, sourcePath + "/uuid")));
            if (targetUUID.equals(sourceUUID)) return resultPath;
        } catch (FileNotFoundException e) {
            // ignore
        }
        deleteContents(targetDir);

        copyAssets(assetManager, sourcePath, targetDir);

        // Copy uuid
        copyFile(assetManager, sourcePath + "/uuid", targetDir);

        return resultPath;
    }

    private static String readLine(InputStream is) throws IOException {
        return new BufferedReader(new InputStreamReader(is)).readLine();
    }

    private static void copyAssets(AssetManager assetManager, String path, File outPath) throws IOException {
        String[] assets = assetManager.list(path);
        if (assets == null) {
            return;
        }
        if (assets.length == 0) {
            if (!path.endsWith("uuid"))
                copyFile(assetManager, path, outPath);
        } else {
            File dir = new File(outPath, path);
            if (!dir.exists()) {
                Log.d("123", "Making directory " + dir.getAbsolutePath());
                if (!dir.mkdirs()) {
                    Log.d("123", "Failed to create directory " + dir.getAbsolutePath());
                }
            }
            for (String asset : assets) {
                copyAssets(assetManager, path + "/" + asset, outPath);
            }
        }
    }

    private static void copyFile(AssetManager assetManager, String fileName, File outPath) throws IOException {
        InputStream in;

        Log.d("123", "Copy " + fileName + " to " + outPath);
        in = assetManager.open(fileName);
        OutputStream out = new FileOutputStream(outPath + "/" + fileName);

        byte[] buffer = new byte[4000];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        out.close();
    }

}
