package com.datarecovery.master.module.imgrecover;

import static com.datarecovery.master.data.consts.EventId.EVENT_3000004;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Pair;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;

import com.atmob.app.lib.base.BaseViewModel;
import com.atmob.app.lib.livedata.SingleLiveEvent;
import com.atmob.common.logging.AtmobLog;
import com.atmob.common.runtime.ContextUtil;
import com.datarecovery.master.R;
import com.datarecovery.master.data.consts.AdFuncId;
import com.datarecovery.master.data.consts.EventId;
import com.datarecovery.master.handler.EventHelper;
import com.datarecovery.master.module.member.MemberType;
import com.datarecovery.master.sdk.ad.AtmobAdHelper;
import com.datarecovery.master.sdk.bugly.BuglyHelper;
import com.datarecovery.master.utils.BoxingUtil;
import com.datarecovery.master.utils.FileUtil;
import com.datarecovery.master.utils.ImageDeepDetector;
import com.datarecovery.master.utils.MediaStoreHelper;
import com.datarecovery.master.utils.ReverseArrayList;
import com.datarecovery.master.utils.ToastUtil;

import org.reactivestreams.Subscription;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import atmob.reactivex.rxjava3.annotations.NonNull;
import atmob.reactivex.rxjava3.core.FlowableSubscriber;
import atmob.reactivex.rxjava3.core.Single;
import atmob.reactivex.rxjava3.core.SingleObserver;
import atmob.reactivex.rxjava3.core.SingleOnSubscribe;
import atmob.reactivex.rxjava3.core.SingleSource;
import atmob.reactivex.rxjava3.disposables.Disposable;
import atmob.reactivex.rxjava3.schedulers.Schedulers;
import atmob.rxjava.utils.RxJavaUtil;
import dagger.hilt.android.lifecycle.HiltViewModel;


@HiltViewModel
public class ImageRecoverViewModel extends BaseViewModel {


    private final long SCANNING_COUNTDOWN = 1000 * 60 * 8;
    private final long SCANNING_IS_TRIAL_COUNTDOWN = 1000 * 5;

    private final List<ImageDeepDetector.ImageFile> detectedPhotoList = new ReverseArrayList<>();
    private final List<ImageDeepDetector.ImageFile> detectedWxList = new ReverseArrayList<>();
    private final List<ImageDeepDetector.ImageFile> detectedQQList = new ReverseArrayList<>();
    private final List<ImageDeepDetector.ImageFile> detectedOtherList = new ReverseArrayList<>();
    private final List<ImageDeepDetector.ImageFile> selectList = new ReverseArrayList<>();

    private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedPhotoImg = new MutableLiveData<>(detectedPhotoList);
    private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedWxImg = new MutableLiveData<>(detectedWxList);
    private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedQQImg = new MutableLiveData<>(detectedQQList);
    private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedOtherImg = new MutableLiveData<>(detectedOtherList);
    private final MutableLiveData<List<ImageDeepDetector.ImageFile>> selectedList = new MutableLiveData<>(selectList);

    private final SingleLiveEvent<List<Uri>> deleteUriListSdk11 = new SingleLiveEvent<>();
    private final SingleLiveEvent<ImageDeepDetector.ImageFile> failScrollPosition = new SingleLiveEvent<>();
    private final MutableLiveData<Boolean> showTrialView = new MutableLiveData<>();
    private final SingleLiveEvent<Pair<List<ImageDeepDetector.ImageFile>, Integer>> previewEvent = new SingleLiveEvent<>();
    private final SingleLiveEvent<Boolean> showScanDialogEvent = new SingleLiveEvent<>();
    private final SingleLiveEvent<Boolean> showLoadingEvent = new SingleLiveEvent<>();
    private final SingleLiveEvent<?> scrollTop = new SingleLiveEvent<>();
    private final SingleLiveEvent<?> notifyList = new SingleLiveEvent<>();
    private final MutableLiveData<String> barTitle = new MutableLiveData<>();
    private final MutableLiveData<Boolean> checkAll = new MutableLiveData<>(false);
    private final MutableLiveData<String> detectedLastFileName = new MutableLiveData<>();
    private final SingleLiveEvent<?> onExportClickEvent = new SingleLiveEvent<>();
    private LiveData<String> detectedPhotoTitle;
    private LiveData<String> detectedWxTitle;
    private LiveData<String> detectedQQTitle;
    private LiveData<String> detectedOtherTitle;
    private LiveData<String> selectedCountTxt;
    private Disposable scanDisposable;

    @MemberType
    private String type;

    private SingleObserver<? super Boolean> dealMediaObserver;

    private long detectTime;

    @Inject
    public ImageRecoverViewModel() {
        barTitle.setValue(ContextUtil.getContext().getString(R.string.iamge_recover_all));
        initLiveData();
    }

    public LiveData<ImageDeepDetector.ImageFile> getFailScrollPosition() {
        return failScrollPosition;
    }

    public LiveData<?> getScrollTop() {
        return scrollTop;
    }

    public LiveData<String> getDetectedLastFileName() {
        return detectedLastFileName;
    }

    public LiveData<Boolean> getShowTrialView() {
        return showTrialView;
    }

    public LiveData<List<Uri>> getDeleteUriListSdk11() {
        return deleteUriListSdk11;
    }

    public LiveData<?> getNotifyList() {
        return notifyList;
    }

    public LiveData<Boolean> getShowLoadingEvent() {
        return showLoadingEvent;
    }

    public LiveData<Pair<List<ImageDeepDetector.ImageFile>, Integer>> getPreviewEvent() {
        return previewEvent;
    }

    public LiveData<String> getSelectedCountTxt() {
        return selectedCountTxt;
    }

    public LiveData<List<ImageDeepDetector.ImageFile>> getSelectedList() {
        return selectedList;
    }

    public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedPhotoImg() {
        return detectedPhotoImg;
    }

    public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedWxImg() {
        return detectedWxImg;
    }

    public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedQQImg() {
        return detectedQQImg;
    }

    public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedOtherImg() {
        return detectedOtherImg;
    }

    public LiveData<String> getDetectedPhotoTitle() {
        return detectedPhotoTitle;
    }

    public LiveData<String> getDetectedWxTitle() {
        return detectedWxTitle;
    }

    public LiveData<String> getDetectedQQTitle() {
        return detectedQQTitle;
    }

    public LiveData<String> getDetectedOtherTitle() {
        return detectedOtherTitle;
    }

    public LiveData<Boolean> getShowScanDialogEvent() {
        return showScanDialogEvent;
    }

    public LiveData<Boolean> getCheckAll() {
        return checkAll;
    }

    public LiveData<String> getBarTitle() {
        return barTitle;
    }

    public LiveData<?> getOnExportClickEvent() {
        return onExportClickEvent;
    }

    public String getType() {
        return type;
    }

    public void setType(@MemberType String type) {
        if (!TextUtils.isEmpty(this.type)) {
            return;
        }
        this.type = type;
        switch (type) {
            case MemberType.APP_IMAGE_CLEAN:
                selectedCountTxt = Transformations.map(selectedList, list -> {
                    if (list == null || list.isEmpty()) {
                        return ContextUtil.getContext().getString(R.string.delete);
                    }
                    return ContextUtil.getContext().getString(R.string.delete_count, list.size());
                });
                break;
            case MemberType.APP_IMAGE_RECOVER:
                selectedCountTxt = Transformations.map(selectedList, list -> {
                    if (list == null || list.isEmpty()) {
                        return ContextUtil.getContext().getString(R.string.export);
                    }
                    return ContextUtil.getContext().getString(R.string.export_count, list.size());
                });
                break;
        }
    }

    private void initLiveData() {
        detectedPhotoTitle = Transformations.map(detectedPhotoImg, list ->
                ContextUtil.getContext().getString(R.string.photo_count, (list == null || list.isEmpty()) ? 0 : list.size()));
        detectedWxTitle = Transformations.map(detectedWxImg, list ->
                ContextUtil.getContext().getString(R.string.wx_count, (list == null || list.isEmpty()) ? 0 : list.size()));
        detectedQQTitle = Transformations.map(detectedQQImg, list ->
                ContextUtil.getContext().getString(R.string.qq_count, (list == null || list.isEmpty()) ? 0 : list.size()));
        detectedOtherTitle = Transformations.map(detectedOtherImg, list ->
                ContextUtil.getContext().getString(R.string.other_count, (list == null || list.isEmpty()) ? 0 : list.size()));
    }

    public void onCheckAllClick(boolean isCheck) {
        EventHelper.report(EventId.EVENT_3000006);
        checkAll.setValue(isCheck);
        setListCheck(detectedPhotoList, isCheck);
        setListCheck(detectedWxList, isCheck);
        setListCheck(detectedQQList, isCheck);
        setListCheck(detectedOtherList, isCheck);
        if (isCheck) {
            selectList.clear();
            selectList.addAll(detectedPhotoList);
            selectList.addAll(detectedWxList);
            selectList.addAll(detectedQQList);
            selectList.addAll(detectedOtherList);
        } else {
            selectList.clear();
        }
        selectedList.setValue(selectList);
    }

    private void setListCheck(List<ImageDeepDetector.ImageFile> list, boolean isCheck) {
        if (list == null || list.isEmpty()) {
            return;
        }
        for (ImageDeepDetector.ImageFile imageFile : list) {
            imageFile.setCheck(isCheck);
        }
    }

    public void startImageScanning() {
        if (scanDisposable != null && !scanDisposable.isDisposed()) {
            return;
        }
        ImageDeepDetector.detect(ContextUtil.getContext(), type)
                .take(SCANNING_COUNTDOWN, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new FlowableSubscriber<List<ImageDeepDetector.ImageFile>>() {
                    @Override
                    public void onSubscribe(@NonNull Subscription s) {
                        s.request(Integer.MAX_VALUE);
                        scanDisposable = Disposable.fromSubscription(s);
                        addDisposable(scanDisposable);
                        showScanDialogEvent.setValue(true);
                        checkAll.setValue(false);
                        detectTime = System.currentTimeMillis();
                        detectedPhotoList.clear();
                        detectedWxList.clear();
                        detectedQQList.clear();
                        detectedOtherList.clear();
                        selectList.clear();
                    }

                    @Override
                    public void onNext(List<ImageDeepDetector.ImageFile> imageFiles) {
                        if (imageFiles == null) {
                            return;
                        }
                        int photoCount = 0;
                        int wxCount = 0;
                        int qqCount = 0;
                        int otherCount = 0;
                        for (ImageDeepDetector.ImageFile imageFile : imageFiles) {
                            detectedLastFileName.setValue(imageFile.getName());
                            if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) {
                                detectedPhotoList.add(imageFile);
                                photoCount++;
                            } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) {
                                detectedWxList.add(imageFile);
                                wxCount++;
                            } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) {
                                detectedQQList.add(imageFile);
                                qqCount++;
                            } else {
                                detectedOtherList.add(imageFile);
                                otherCount++;
                            }
                        }
                        if (photoCount > 0) {
                            detectedPhotoImg.setValue(detectedPhotoList);
                        }
                        if (wxCount > 0) {
                            detectedWxImg.setValue(detectedWxList);
                        }
                        if (qqCount > 0) {
                            detectedQQImg.setValue(detectedQQList);
                        }
                        if (otherCount > 0) {
                            detectedOtherImg.setValue(detectedOtherList);
                        }
                        scrollTop.call();
                    }

                    @Override
                    public void onError(Throwable t) {
                        showScanDialogEvent.setValue(false);
                    }

                    @Override
                    public void onComplete() {
                        AtmobLog.d("ImageRecoverViewModel", System.currentTimeMillis() - detectTime + "ms");
                        showScanDialogEvent.setValue(false);
                    }
                });
    }

    public void cancelScan() {
        if (scanDisposable != null && !scanDisposable.isDisposed()) scanDisposable.dispose();
        showScanDialogEvent.setValue(false);
    }

    public void setItemCheck(@NonNull ImageDeepDetector.ImageFile imageFile) {
        imageFile.setCheck(!imageFile.isCheck());
        if (imageFile.isCheck()) {
            selectList.add(imageFile);
        } else {
            selectList.remove(imageFile);
        }
        selectedList.setValue(selectList);
    }

    public void checkPreview(@NonNull ImageDeepDetector.ImageFile imageFile) {
        if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN)) {
            return;
        }
        sendPreviewEvent(imageFile);
    }

    public void sendPreviewEvent(@NonNull ImageDeepDetector.ImageFile imageFile) {
        List<ImageDeepDetector.ImageFile> allDetectedList = getAllDetectedList();
        int position = getPosition(allDetectedList, imageFile);
        previewEvent.setValue(new Pair<>(allDetectedList, position));
    }

    public void onOperationClick() {
        if (selectList.isEmpty()) {
            return;
        }
        onExportClickEvent.call();
    }

    public void executeImageDelete() {
        if (selectList.isEmpty()) {
            return;
        }
        Single.just(Build.VERSION.SDK_INT)
                .map(sdkInt -> sdkInt >= Build.VERSION_CODES.R)
                .map(isSdk11 -> {
                    List<ImageDeepDetector.ImageFile> safList = new ArrayList<>();
                    List<ImageDeepDetector.ImageFile> uriList = new ArrayList<>();
                    if (isSdk11) {
                        for (ImageDeepDetector.ImageFile item : selectList) {
                            File tempFile = new File(item.getPath());
                            long mediaID = getFilePathToMediaID(tempFile.getAbsolutePath(), ContextUtil.getContext());
                            if (mediaID > 0) {
                                Uri externalUri = ContentUris.withAppendedId(MediaStore.Images.Media.getContentUri("external"), mediaID);
                                item.setExternalUri(externalUri);
                                uriList.add(item);
                            } else {
                                safList.add(item);
                            }
                        }
                    } else {
                        safList.addAll(selectList);
                    }
                    return new Pair<>(safList, uriList);
                })
                .flatMap(pair -> Single.zip(
                        imageDelete(pair.first).subscribeOn(Schedulers.io()),
                        externalDelete(pair.second).subscribeOn(Schedulers.io()),
                        Pair::new
                ))
                .compose(RxJavaUtil.SingleSchedule.io2Main())
                .subscribe(new SingleObserver<Pair<Pair<ImageDeepDetector.ImageFile, Exception>, Pair<ImageDeepDetector.ImageFile, Exception>>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showLoadingEvent.setValue(true);
                    }

                    @Override
                    public void onSuccess(@NonNull Pair<Pair<ImageDeepDetector.ImageFile, Exception>, Pair<ImageDeepDetector.ImageFile, Exception>> pairPairPair) {
                        showLoadingEvent.setValue(false);
                        if (pairPairPair.first.second != null) {
                            ToastUtil.show("部分图片清除失败：" + pairPairPair.first.second.getMessage(), ToastUtil.LENGTH_SHORT);
                            failScrollPosition.setValue(pairPairPair.first.first);
                            return;
                        }
                        if (pairPairPair.second.second instanceof CancelDeleteException) {
                            return;
                        }
                        if (pairPairPair.second.second != null) {
                            ToastUtil.show("部分图片清除失败：" + pairPairPair.second.second.getMessage(), ToastUtil.LENGTH_SHORT);
                            failScrollPosition.setValue(pairPairPair.second.first);
                            return;
                        }
                        if (pairPairPair.first.first == null && pairPairPair.second.first == null) {
                            ToastUtil.show(R.string.delete_success, ToastUtil.LENGTH_SHORT);
                        }
                        AtmobAdHelper.showInterstitial(AdFuncId.INTERSTITIAL_CLEAN_SUCCESS, null);

                        EventHelper.report(EventId.EVENT_3000902);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoadingEvent.setValue(false);
                        ToastUtil.show(R.string.delete_fail, ToastUtil.LENGTH_SHORT);
                    }
                });
    }

    private @NonNull Single<Pair<ImageDeepDetector.ImageFile, Exception>> externalDelete(List<ImageDeepDetector.ImageFile> externalList) {
        return Single.create((SingleOnSubscribe<List<Uri>>) emitter -> {
                    List<Uri> uriDeleteList = new ArrayList<>();
                    if (externalList.isEmpty()) {
                        emitter.onSuccess(uriDeleteList);
                        return;
                    }
                    for (ImageDeepDetector.ImageFile item : externalList) {
                        uriDeleteList.add(item.getExternalUri());
                    }
                    emitter.onSuccess(uriDeleteList);
                })
                .flatMap(uriDeleteList -> {
                    if (!uriDeleteList.isEmpty()) {
                        deleteUriListSdk11.postValue(uriDeleteList);
                        return (SingleSource<Boolean>) observer -> this.dealMediaObserver = observer;
                    } else {
                        return Single.just(true);
                    }
                })
                .flatMap(state -> {
                    if (BoxingUtil.boxing(state)) {
                        Exception exception = null;
                        ImageDeepDetector.ImageFile deleteError = null;
                        for (ImageDeepDetector.ImageFile item : externalList) {
                            try {
                                item.delete();
                                selectList.remove(item);
                                if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) {
                                    detectedPhotoList.remove(item);
                                } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) {
                                    detectedWxList.remove(item);
                                } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) {
                                    detectedQQList.remove(item);
                                } else {
                                    detectedOtherList.remove(item);
                                }
                            } catch (Exception throwable) {
                                exception = throwable;
                                deleteError = item;
                                BuglyHelper.postCatchedException(new Exception("图片删除External:", throwable));
                            }
                        }
                        selectedList.postValue(selectList);
                        notifyList.postValue(null);
                        return Single.just(new Pair<>(deleteError, exception));
                    } else {
                        return Single.just(new Pair<>(null, new CancelDeleteException()));
                    }
                });
    }

    private Single<Pair<ImageDeepDetector.ImageFile, Exception>> imageDelete(List<ImageDeepDetector.ImageFile> safList) {
        return Single.create(emitter -> {
            Exception exception = null;
            ImageDeepDetector.ImageFile deleteError = null;
            if (safList.isEmpty()) {
                emitter.onSuccess(new Pair<>(deleteError, exception));
                return;
            }
            for (ImageDeepDetector.ImageFile item : safList) {
                try {
                    item.delete();
                    selectList.remove(item);
                    if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) {
                        detectedPhotoList.remove(item);
                    } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) {
                        detectedWxList.remove(item);
                    } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) {
                        detectedQQList.remove(item);
                    } else {
                        detectedOtherList.remove(item);
                    }
                } catch (Exception throwable) {
                    exception = throwable;
                    deleteError = item;
                    BuglyHelper.postCatchedException(new Exception("图片删除:", throwable));
                }
            }
            selectedList.postValue(selectList);
            notifyList.postValue(null);
            emitter.onSuccess(new Pair<>(deleteError, exception));
        });
    }

    public long getFilePathToMediaID(String songPath, Context context) {
        long id = 0;
        ContentResolver cr = context.getContentResolver();

        Uri uri = MediaStore.Files.getContentUri("external");
        String selection = MediaStore.Audio.Media.DATA;
        String[] selectionArgs = {songPath};
        String[] projection = {MediaStore.Audio.Media._ID};

        try (Cursor cursor = cr.query(uri, projection, selection + "=?", selectionArgs, null)) {
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    int idIndex = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
                    id = Long.parseLong(cursor.getString(idIndex));
                }
            }
        }

        return id;
    }

    public void exportImage() {
        if (selectList.isEmpty()) {
            return;
        }
        Single.just(selectList.size())
                .map(oldSize -> {
                    Iterator<ImageDeepDetector.ImageFile> iterator = selectList.iterator();
                    int errorCount = 0;
                    Exception exception = null;
                    while (iterator.hasNext()) {
                        ImageDeepDetector.ImageFile item = iterator.next();
                        try {
                            MediaStoreHelper.saveToSharedStorage(MediaStoreHelper.TYPE_IMAGE, item.newInputStream(), FileUtil.getCreateFileName(MediaStoreHelper.TYPE_IMAGE, item.getName()));
                            item.setCheck(false);
                            iterator.remove();
                        } catch (Exception throwable) {
                            errorCount++;
                            exception = throwable;
                            BuglyHelper.postCatchedException(new Exception("图片导出:", throwable));
                        }
                    }
                    if (errorCount == oldSize) {
                        throw new Exception("导出失败" + (exception == null ? "" : ":" + exception.getMessage()));
                    } else if (errorCount > 0) {
                        throw new Exception("部分导出失败" + ":" + exception.getMessage());
                    }
                    return errorCount;
                })
                .compose(RxJavaUtil.SingleSchedule.io2Main())
                .subscribe(new SingleObserver<Integer>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showLoadingEvent.setValue(true);
                    }

                    @Override
                    public void onSuccess(@NonNull Integer integer) {
                        showLoadingEvent.setValue(false);
                        selectedList.setValue(selectList);
                        checkAll.setValue(false);
                        ToastUtil.show(R.string.export_success, ToastUtil.LENGTH_SHORT);
                        AtmobAdHelper.showInterstitial(AdFuncId.INTERSTITIAL_RECOVER_SUCCESS, null);
                        EventHelper.report(EVENT_3000004);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoadingEvent.setValue(false);
                        selectedList.setValue(selectList);
                        ToastUtil.show(e.getMessage(), ToastUtil.LENGTH_SHORT);
                        if (!selectList.isEmpty()) {
                            failScrollPosition.setValue(selectList.get(0));
                        }
                    }
                });
    }


    public List<ImageDeepDetector.ImageFile> getAllDetectedList() {
        List<ImageDeepDetector.ImageFile> allList = new ReverseArrayList<>();
        allList.addAll(detectedOtherList);
        allList.addAll(detectedQQList);
        allList.addAll(detectedWxList);
        allList.addAll(detectedPhotoList);
        return allList;
    }

    public int getPosition(@NonNull List<ImageDeepDetector.ImageFile> list, ImageDeepDetector.ImageFile target) {
        return list.indexOf(target);
    }

    public void callDealComplete(boolean isDelete) {
        if (dealMediaObserver != null) {
            dealMediaObserver.onSuccess(isDelete);
        }
    }

    private static class CancelDeleteException extends Exception {
    }
}
