package com.hainaos.vc.network;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.hainaos.vc.bean.ActivationInfo;
import com.hainaos.vc.bean.BaseResponse;
import com.hainaos.vc.bean.LocalVideoInfo;
import com.hainaos.vc.bean.PhotoInfo;
import com.hainaos.vc.bean.SpaceInfo;
import com.hainaos.vc.bean.VideoInfo;
import com.hainaos.vc.config.CommonConfig;
import com.hainaos.vc.disklrucache.CacheHelper;
import com.hainaos.vc.gson.GsonUtils;
import com.hainaos.vc.manager.ConnectManager;
import com.hainaos.vc.manager.ConnectMode;
import com.hainaos.vc.network.api.ActivationApi;
import com.hainaos.vc.network.api.FileApi;
import com.hainaos.vc.network.interceptor.RepeatRequestInterceptor;
import com.hainaos.vc.utils.JgyUtils;
import com.hainaos.vc.utils.ToastUtil;
import com.hainaos.vc.utils.Utils;
import com.hainaos.vc.utils.VideoUtils;
import com.tencent.mmkv.MMKV;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.FragmentEvent;

import java.io.File;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.BiFunction;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
import okhttp3.Cache;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class NetInterfaceManager {
    private static final String TAG = "NetInterfaceManager";

    @SuppressLint("StaticFieldLeak")
    private static NetInterfaceManager INSTANCE;
    private Context mContext;
    private ContentResolver crv;
    private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
    private Retrofit mRetrofit;
    private OkHttpClient okHttpClient;
    private CacheHelper mCacheHelper;

    private final ConcurrentHashMap<String, Long> requestIdsMap = new ConcurrentHashMap<>();

    //超时时间
    private static final int timeOut = 5;
    // 缓存文件最大限制大小20M
    private static final long cacheSize = 1024 * 1024 * 64;
    public static final String CUSTOM_REPEAT_REQ_PROTOCOL = "MY_CUSTOM_REPEAT_REQ_PROTOCOL";

    private NetInterfaceManager(Context context) {
        if (context == null) {
            throw new RuntimeException("Context is NULL");
        }
        this.mContext = context;
        this.crv = mContext.getContentResolver();
        this.mCacheHelper = new CacheHelper(mContext);

        if (null == mRetrofit) {
            if (okHttpClient == null) {
                //如果无法生存缓存文件目录，检测权限使用已经加上，检测手机是否把文件读写权限禁止了
                OkHttpClient.Builder builder = new OkHttpClient.Builder();
                builder.connectTimeout(timeOut, TimeUnit.SECONDS); // 设置连接超时时间
                builder.writeTimeout(timeOut, TimeUnit.SECONDS);// 设置写入超时时间
                builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间
                builder.retryOnConnectionFailure(true);// 设置进行连接失败重试
                builder.addInterceptor(new RepeatRequestInterceptor());

                // 设置缓存文件路径
                String cacheDirectory = getCacheDir() + "/OkHttpCache";
                Cache cache = new Cache(new File(cacheDirectory), cacheSize);
                builder.cache(cache);// 设置缓存
                okHttpClient = builder.build();
            }

            mRetrofit = new Retrofit.Builder()
                    .client(okHttpClient)
                    .baseUrl(UrlAddress.ROOT_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                    .build();
        }
    }

    private String getCacheDir() {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            if (mContext.getExternalCacheDir() != null) {
                cachePath = mContext.getExternalCacheDir().getPath();
            } else if (mContext.getExternalFilesDir("cache") != null) {
                cachePath = mContext.getExternalFilesDir("cache").getPath();
            } else {
                cachePath = mContext.getCacheDir().getPath();
            }
        } else {
            cachePath = mContext.getCacheDir().getPath();
        }
        return cachePath;
    }

    public static void init(Context context) {
        if (context == null) {
            throw new RuntimeException("context is NULL");
        }
        if (INSTANCE == null) {
            INSTANCE = new NetInterfaceManager(context);
        }
    }

    public static NetInterfaceManager getInstance() {
        if (INSTANCE == null) {
            throw new IllegalStateException("You must be init NetworkManager first");
        }
        return INSTANCE;
    }

    public OkHttpClient getOkHttpClient() {
        return okHttpClient;
    }

    /*
     *
     * Observable
     *
     * */

    public Observable<BaseResponse<List<VideoInfo>>> getHomeVideoControl() {
        return mRetrofit.create(FileApi.class)
                .getHomeVideo(Utils.getSerial())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<BaseResponse<ArrayList<PhotoInfo>>> getHomePhotoControl() {
        return mRetrofit.create(FileApi.class)
                .getHomePhoto(Utils.getSerial())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<BaseResponse<SpaceInfo>> getHomeSpaceInfoControl() {
        return mRetrofit.create(FileApi.class)
                .getHomeSpaceInfo(Utils.getSerial())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<BaseResponse<ActivationInfo>> getActivationControl() {
        return mRetrofit.create(ActivationApi.class)
                .getActivation(Utils.getSerial())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public interface onVideoPathCallback {
        void setVideoList(ArrayList<LocalVideoInfo> videoList);

        void onComplete();
    }

    public void getHomeVideo(boolean refresh, BehaviorSubject<ActivityEvent> lifecycle, onVideoPathCallback callback) {
        ConnectMode connectMode = ConnectMode.ONE_MINUTE;
        if (refresh) {
            connectMode = ConnectMode.DEFAULT;
        }
        if (ConnectManager.getInstance().isNeedConnect(UrlAddress.HOME_VIDEO, connectMode)) {
            getHomeVideo(lifecycle, callback);
        } else {
            String jsonString = mCacheHelper.getAsString(UrlAddress.HOME_VIDEO);
            //为 "" 是已经请求成功的
            if (jsonString == null) {
                getHomeVideo(lifecycle, callback);
            } else {
                getHomeVideoCache(jsonString, callback);
            }
        }
    }

    public void getHomeVideoCache(String jsonString, onVideoPathCallback callback) {
        Gson gson = new Gson();
        Type type = new TypeToken<List<VideoInfo>>() {
        }.getType();
        try {
            List<VideoInfo> videoInfos = gson.fromJson(jsonString, type);
            ArrayList<LocalVideoInfo> LocalVideoInfos = new ArrayList<>();
            for (VideoInfo videoInfo : videoInfos) {
                LocalVideoInfo localVideoInfo = new LocalVideoInfo();
                localVideoInfo.setFile(videoInfo.getFile());
                localVideoInfo.setFile_name(videoInfo.getFile_name());
                localVideoInfo.setFile_md5(videoInfo.getFile_md5());
                localVideoInfo.setDuration(videoInfo.getDuration());
                if (JgyUtils.getInstance().fileExists(videoInfo.getFile())) {
                    localVideoInfo.setLocalPath(JgyUtils.getInstance().getUrlLocalPath(videoInfo.getFile()));
                }
                LocalVideoInfos.add(localVideoInfo);
            }
            callback.setVideoList(LocalVideoInfos);
        } catch (Exception e) {
            Log.e(TAG, "getHomeVideo: " + e.getMessage());
            callback.setVideoList(null);
        }
        callback.onComplete();
    }

    public void getHomeVideo(BehaviorSubject<ActivityEvent> lifecycle, onVideoPathCallback callback) {
        getHomeVideoControl()
                .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
                .subscribe(getHomeVideoObserver(callback));
    }

    public void getHomeVideoFragment(BehaviorSubject<FragmentEvent> lifecycle, onVideoPathCallback callback) {
        getHomeVideoControl()
                .compose(RxLifecycle.bindUntilEvent(lifecycle, FragmentEvent.DESTROY))
                .subscribe(getHomeVideoObserver(callback));
    }

    public void getHomeVideo(onVideoPathCallback callback) {
        getHomeVideoControl()
                .subscribe(getHomeVideoObserver(callback));
    }

    public void getHomeVideo() {
        getHomeVideoControl()
                .subscribe(getHomeVideoObserver(null));
    }

    private Observer<BaseResponse<List<VideoInfo>>> getHomeVideoObserver(onVideoPathCallback callback) {
        return new Observer<BaseResponse<List<VideoInfo>>>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                Log.e("getHomeVideo", "onSubscribe: ");
            }

            @Override
            public void onNext(@NonNull BaseResponse<List<VideoInfo>> listBaseResponse) {
                Log.e("getHomeVideo", "onNext: " + listBaseResponse);
                int code = listBaseResponse.code;
                if (code == 200) {
                    List<VideoInfo> videoInfos = listBaseResponse.data;
                    ArrayList<LocalVideoInfo> LocalVideoInfos = new ArrayList<>();
                    for (VideoInfo videoInfo : videoInfos) {
                        LocalVideoInfo localVideoInfo = new LocalVideoInfo();
                        localVideoInfo.setFile(videoInfo.getFile());
                        localVideoInfo.setFile_name(videoInfo.getFile_name());
                        localVideoInfo.setFile_md5(videoInfo.getFile_md5());
                        localVideoInfo.setDuration(videoInfo.getDuration());
                        if (JgyUtils.getInstance().fileExists(videoInfo.getFile())) {
                            localVideoInfo.setLocalPath(JgyUtils.getInstance().getUrlLocalPath(videoInfo.getFile()));
                        }
                        LocalVideoInfos.add(localVideoInfo);
                    }
                    callback.setVideoList(LocalVideoInfos);
                    mCacheHelper.put(UrlAddress.HOME_VIDEO, GsonUtils.toJSONString(videoInfos));
                } else {
                    callback.setVideoList(null);
                    mCacheHelper.put(UrlAddress.HOME_VIDEO, "");
                }
            }

            @Override
            public void onError(@NonNull Throwable e) {
                Log.e("getHomeVideo", "onError: " + e.getMessage());
                ToastUtil.show("网络连接失败");
                String jsonString = mCacheHelper.getAsString(UrlAddress.HOME_VIDEO);
                getHomeVideoCache(jsonString, callback);
                onComplete();
            }

            @Override
            public void onComplete() {
                Log.e("getHomeVideo", "onComplete: ");
                if (callback != null) {
                    callback.onComplete();
                }
            }
        };
    }

    public Observable<List<String>> getLocalVideo() {
        return Observable.create(new ObservableOnSubscribe<List<String>>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<List<String>> emitter) throws Throwable {
                long s1 = System.currentTimeMillis();
                List<String> fileList = new ArrayList<>();
                String rootPath = Environment.getExternalStorageDirectory().getPath() + File.separator;
                File file = new File(rootPath);
                if (file.exists()) {
                    LinkedList<File> list = new LinkedList<File>();
                    File[] files = file.listFiles();
                    if (files == null) {
                        emitter.onNext(null);
                    } else {
                        for (File file2 : files) {
                            if (file2.isDirectory()) {
                                list.add(file2);
                            } else {
                                if (VideoUtils.isVideoFormat(file2.getAbsolutePath())) {
                                    fileList.add(file2.getAbsolutePath());
                                }
                            }
                        }
                        File temp_file;
                        while (!list.isEmpty()) {
                            temp_file = list.removeFirst();
                            files = temp_file.listFiles();
                            if (files == null) {
                                continue;
                            }
                            for (File file2 : files) {
                                if (file2.isDirectory()) {
                                    list.add(file2);
                                } else {
                                    if (VideoUtils.isVideoFormat(file2.getAbsolutePath())) {
                                        fileList.add(file2.getAbsolutePath());
                                    }
                                }
                            }
                        }
                        emitter.onNext(fileList);
                    }
                } else {
                    emitter.onNext(null);
                    Log.e("traverseFolder1", "文件不存在!");
                }
                Log.e("ScanTask", "doInBackground: " + "Scan time = " + (System.currentTimeMillis() - s1) + "ms");
                emitter.onComplete();
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public void getVideoPath(BehaviorSubject<ActivityEvent> lifecycle, onVideoPathCallback callback) {
        Observable.zip(getHomeVideoControl(), getLocalVideo(), new BiFunction<BaseResponse<List<VideoInfo>>, List<String>, ArrayList<LocalVideoInfo>>() {
            @Override
            public ArrayList<LocalVideoInfo> apply(BaseResponse<List<VideoInfo>> listBaseResponse, List<String> strings) throws Throwable {
                ArrayList<LocalVideoInfo> LocalVideoInfos = new ArrayList<>();
                if (listBaseResponse.code == 200) {
                    List<VideoInfo> videoInfos = listBaseResponse.data;
//                    List<String> videoList = videoInfos.stream().map(VideoInfo::getFile).collect(Collectors.toList());
                    for (VideoInfo videoInfo : videoInfos) {
                        LocalVideoInfo localVideoInfo = new LocalVideoInfo();
                        localVideoInfo.setFile(videoInfo.getFile());
                        localVideoInfo.setFile_md5(videoInfo.getFile_md5());
                        localVideoInfo.setDuration(videoInfo.getDuration());
                        if (JgyUtils.getInstance().fileExists(videoInfo.getFile())) {
                            localVideoInfo.setLocalPath(JgyUtils.getInstance().getUrlLocalPath(videoInfo.getFile()));
                        }
                        LocalVideoInfos.add(localVideoInfo);
                    }
                }
                if (strings != null && strings.size() != 0) {
                    for (String s : strings) {
                        LocalVideoInfo localVideoInfo = new LocalVideoInfo();
                        localVideoInfo.setLocalPath(s);
                        LocalVideoInfos.add(localVideoInfo);
                    }
                }
                return LocalVideoInfos;
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
                .subscribe(new Observer<ArrayList<LocalVideoInfo>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        Log.e("getVideoPath", "onSubscribe: ");
                    }

                    @Override
                    public void onNext(@NonNull ArrayList<LocalVideoInfo> strings) {
                        Log.e("getVideoPath", "onNext: " + strings);
                        callback.setVideoList(strings);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e("getVideoPath", "onError: " + e.getMessage());
                        onComplete();
                    }

                    @Override
                    public void onComplete() {
                        Log.e("getVideoPath", "onComplete: ");
                        callback.onComplete();
                    }
                });
    }

    public interface onPhotoCallback {
        void setPhotoList(ArrayList<PhotoInfo> photoList);

        void onComplete();
    }

    public void getHomePhoto(boolean refresh, BehaviorSubject<ActivityEvent> lifecycle, onPhotoCallback callback) {
        ConnectMode connectMode = ConnectMode.ONE_MINUTE;
        if (refresh) {
            connectMode = ConnectMode.DEFAULT;
        }
        if (ConnectManager.getInstance().isNeedConnect(UrlAddress.HOME_PHOTO, connectMode)) {
            getHomePhoto(lifecycle, callback);
        } else {
            String jsonString = mCacheHelper.getAsString(UrlAddress.HOME_PHOTO);
            //为 "" 是已经请求成功的
            if (jsonString == null) {
                getHomePhoto(lifecycle, callback);
            } else {
                getHomePhotoCache(jsonString, callback);
            }
        }
    }


    public void getHomePhotoCache(String jsonString, onPhotoCallback callback) {
        Gson gson = new Gson();
        Type type = new TypeToken<List<PhotoInfo>>() {
        }.getType();
        try {
            ArrayList<PhotoInfo> photoInfoList = gson.fromJson(jsonString, type);
            if (callback != null) {
                callback.setPhotoList(photoInfoList);
            }
        } catch (Exception e) {
            Log.e(TAG, "getHomePhotoCache: " + e.getMessage());
            if (callback != null) {
                callback.setPhotoList(null);
            }
        }
        if (callback != null) {
            callback.onComplete();
        }
    }

    public void getHomePhoto(BehaviorSubject<ActivityEvent> lifecycle, onPhotoCallback callback) {
        getHomePhotoControl()
                .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
                .subscribe(getHomePhotoObserver(callback));
    }

    public void getHomePhotoFragment(BehaviorSubject<FragmentEvent> lifecycle, onPhotoCallback callback) {
        getHomePhotoControl()
                .compose(RxLifecycle.bindUntilEvent(lifecycle, FragmentEvent.DESTROY))
                .subscribe(getHomePhotoObserver(callback));
    }

    public void getHomePhoto(onPhotoCallback callback) {
        getHomePhotoControl()
                .subscribe(getHomePhotoObserver(callback));
    }

    public void getHomePhoto() {
        getHomePhotoControl()
                .subscribe(getHomePhotoObserver(null));
    }

    private Observer<BaseResponse<ArrayList<PhotoInfo>>> getHomePhotoObserver(onPhotoCallback callback) {
        return new Observer<BaseResponse<ArrayList<PhotoInfo>>>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                Log.e("getHomePhotoObserver", "onSubscribe: ");
            }

            @Override
            public void onNext(@NonNull BaseResponse<ArrayList<PhotoInfo>> listBaseResponse) {
                Log.e("getHomePhotoObserver", "onNext: " + listBaseResponse);
                int code = listBaseResponse.code;
                if (code == 200) {
                    ArrayList<PhotoInfo> photoInfoList = listBaseResponse.data;
                    for (PhotoInfo photoInfo : photoInfoList) {
                        if (!JgyUtils.getInstance().fileExists(photoInfo.getFile())) {
                            JgyUtils.getInstance().ariaDownload(photoInfo.getFile(), GsonUtils.getJsonObject(GsonUtils.toJSONString(photoInfo)));
                        }
                    }
                    mCacheHelper.put(UrlAddress.HOME_PHOTO, GsonUtils.toJSONString(photoInfoList));
                    if (callback != null) {
                        callback.setPhotoList(photoInfoList);
                    }
                } else {
                    mCacheHelper.put(UrlAddress.HOME_PHOTO, "");
                    if (callback != null) {
                        callback.setPhotoList(null);
                    }
                }
            }

            @Override
            public void onError(@NonNull Throwable e) {
                Log.e("getHomePhotoObserver", "onError: " + e.getMessage());
                ToastUtil.show("网络连接失败");
                String jsonString = mCacheHelper.getAsString(UrlAddress.HOME_PHOTO);
                getHomePhotoCache(jsonString, callback);
                onComplete();
            }

            @Override
            public void onComplete() {
                Log.e("getHomePhotoObserver", "onComplete: ");
                if (callback != null) {
                    callback.onComplete();
                }
            }
        };
    }

}
