
package xn.xxp.app;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Environment;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.util.Pair;

import com.blankj.utilcode.util.ActivityUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.blankj.utilcode.util.Utils;
import com.google.gson.reflect.TypeToken;
import com.njlz.event.BuildModelEvent;
import com.njlz.face.FaceUtils;
import com.njlz.smartdata.FaceBuildBean;
import com.njlz.smartdata.FaceConfig;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import http.OkHttpUtils;
import http.client.HttpTool;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import xn.xxp.home.auth.ChoiceAuthActivity;
import xn.xxp.home.setting.FaceListActivity;
import xn.xxp.room.RoomTool;
import xn.xxp.room.bean.Face;
import xn.xxp.room.dao.FaceDao;

public enum SyncFaceDeBugTool {
    INSTANCE;

    private static final String TAG = "SyncFaceTool";
    private static final int MAX_DOWNLOAD_THREADS = 4;
    private volatile boolean isBuildFaceEnd = false;
    private List<Face> faceList;
    public volatile boolean isSyncFace = false;
    private long lastBuildTime = 0;

    @WorkerThread
    public void doWork() {
        if (ThreadUtils.isMainThread()) {
            LogUtils.e("该方法必须在子线程调用");
            return;
        }

        if (!checkActivityState()) return;


        if (isSyncFace) {
            LogUtils.d("正在同步人脸");
            return;
        }

        isSyncFace = true;
        try {
            FaceUtils.getInstance().init(Utils.getApp());
            FaceConfig faceConfig = FaceUtils.getInstance().getFaceConfig();
            LogUtils.json("底库配置", faceConfig);

            EventBus.getDefault().register(this);

            List<Pair<String, String>> faceUrlList = fetchLabFaceList();
            if (faceUrlList == null || faceUrlList.isEmpty()) {
                LogUtils.d("不需要下载变更人脸");
                return;
            }

            List<FaceBuildBean> faceBuildBeanList = downloadFaceImages(faceUrlList);
            if (faceBuildBeanList != null && !faceBuildBeanList.isEmpty()) {
                processFaceBuilding(faceBuildBeanList);
            }

            cleanRedundantFaces();
            LogUtils.d("最终底库", FaceUtils.getInstance().getAllFaceId());
        } catch (Exception e) {
            LogUtils.e(Log.getStackTraceString(e));
        } finally {
            cleanupResources();
            isSyncFace = false;
        }
    }

    private boolean checkActivityState() {
        Activity topActivity = ActivityUtils.getTopActivity();
        if (topActivity instanceof ChoiceAuthActivity) {
            LogUtils.d("人脸识别中,不去同步人脸!");
            return false;
        }
        if (topActivity instanceof FaceListActivity) {
            LogUtils.d("正在查阅底库,不去同步人脸!");
            return false;
        }
        return true;
    }

    private List<Pair<String, String>> fetchLabFaceList() {
        try (Response response = HttpTool.getLabFaceList()) {
            if (response == null || !response.isSuccessful()) return null;

            JSONObject jsonObject = new JSONObject(response.body().string());
            if (jsonObject.getInt("code") != 200) return null;

            Type faceListType = new TypeToken<List<Face>>() {
            }.getType();
            faceList = GsonUtils.fromJson(jsonObject.getJSONArray("data").toString(), faceListType);
            if (faceList == null || faceList.isEmpty()) return Collections.emptyList();

            return filterNeedDownloadFaces();
        } catch (Exception e) {
            LogUtils.e(Log.getStackTraceString(e));
            return null;
        }
    }

    private List<Pair<String, String>> filterNeedDownloadFaces() {
        List<Pair<String, String>> results = new ArrayList<>();
        FaceDao faceDao = RoomTool.getInstance().faceDao();
        List<String> existingFaceIds = FaceUtils.getInstance().getAllFaceId();

        for (Face face : faceList) {
            Face localFace = faceDao.getById(face.getUserId());
            if (shouldDownloadFace(localFace, face, existingFaceIds)) {
                results.add(Pair.create(
                        String.valueOf(face.getUserId()),
                        HttpTool.checkUrl(face.getFaceUrl())
                ));
            }
        }
        return results;
    }

    private boolean shouldDownloadFace(Face localFace, Face remoteFace, List<String> existingFaceIds) {
        if (localFace == null) return true;
        if (remoteFace.getLastUpdated() <= localFace.getLastUpdated()) return false;
        return !existingFaceIds.contains(String.valueOf(remoteFace.getUserId()));
    }

    private List<FaceBuildBean> downloadFaceImages(List<Pair<String, String>> faceUrlList) {
        ExecutorService executor = Executors.newFixedThreadPool(MAX_DOWNLOAD_THREADS);
        ConcurrentHashMap<String, File> downloadedFiles = new ConcurrentHashMap<>();
        CountDownLatch latch = new CountDownLatch(faceUrlList.size());
        AtomicInteger failureCount = new AtomicInteger(0);

        try {
            String downloadDir = createDownloadDirectory();

            for (Pair<String, String> pair : faceUrlList) {
                executor.submit(() -> {
                    try {
                        File outputFile = new File(downloadDir, pair.first + ".jpg");
                        if (downloadImage(pair.second, outputFile)) {
                            downloadedFiles.put(pair.first, outputFile);
                        }
                    } finally {
                        latch.countDown();
                    }
                });
            }

            latch.wait();
            executor.shutdown();

            if (failureCount.get() > 0) {
                LogUtils.w("部分人脸下载失败，失败数量：" + failureCount.get());
            }

            return buildFaceBeans(downloadedFiles);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            executor.shutdownNow();
        }
    }

    private boolean downloadImage(String url, File outputFile) {
        Request request = new Request.Builder().url(url).build();
        try (Response response = OkHttpUtils.client.newCall(request).execute()) {
            if (!response.isSuccessful() || response.body() == null) {
                LogUtils.w("下载失败: " + url);
                return false;
            }

            try (InputStream is = response.body().byteStream();
                 BufferedSink sink = Okio.buffer(Okio.sink(outputFile))) {
                sink.writeAll(Okio.source(is));
                return true;
            }
        } catch (IOException e) {
            LogUtils.e("下载异常: " + url, e);
            return false;
        }
    }

    @NonNull
    private String createDownloadDirectory() {
        @SuppressLint("SdCardPath")
        String dirPath = Environment.getExternalStorageDirectory() + "/Download/facePic/";
        FileUtils.createOrExistsDir(dirPath);
        FileUtils.deleteAllInDir(dirPath);
        return dirPath;
    }

    private List<FaceBuildBean> buildFaceBeans(ConcurrentHashMap<String, File> downloadedFiles) {
        List<FaceBuildBean> beans = new ArrayList<>();
        downloadedFiles.forEach((id, file) -> {
            FaceBuildBean bean = new FaceBuildBean();
            bean.setPicPath(file.getAbsolutePath());
            bean.setFaceUuid(id);
            beans.add(bean);
        });
        return beans;
    }

    private void processFaceBuilding(List<FaceBuildBean> faceBuildBeanList)
            throws InterruptedException {

        FaceUtils.getInstance().faceBuild(faceBuildBeanList);
        lastBuildTime = System.currentTimeMillis();
        isBuildFaceEnd = false;

        while (!isBuildFaceEnd) {
            Thread.sleep(10);
            if (System.currentTimeMillis() - lastBuildTime > 2000) {
                isBuildFaceEnd = true;
            }
        }
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onReceive(BuildModelEvent event) {
        if (event.isDone()) {
            processBuildResults();
            isBuildFaceEnd = true;
        }
        lastBuildTime = System.currentTimeMillis();
    }

    private void processBuildResults() {
        List<FaceBuildBean> results = FaceUtils.getInstance().getFaceResult();
        if (results == null || results.isEmpty()) return;

        FaceDao faceDao = RoomTool.getInstance().faceDao();
        results.forEach(bean -> {
            if (bean.getErrCode() != 0) {
                LogUtils.e("建模失败", bean.getFaceUuid(), bean.getErrCode());
            } else {
                updateFaceDatabase(faceDao, bean.getFaceUuid());
            }
        });
    }

    private void updateFaceDatabase(FaceDao faceDao, String faceUuid) {
        if (faceList == null) return;

        long uuid = Long.parseLong(faceUuid);
        for (Face face : faceList) {
            if (face.getUserId() == uuid) {
                faceDao.insertAll(face);
                break;
            }
        }
    }

    private void cleanRedundantFaces() {
        List<String> existingIds = FaceUtils.getInstance().getAllFaceId();
        if (existingIds == null || existingIds.isEmpty()) return;

        FaceDao faceDao = RoomTool.getInstance().faceDao();
        existingIds.forEach(id -> {
            if (!containsFace(id)) {
                FaceUtils.getInstance().deleteFace(id);
                faceDao.delete(Long.parseLong(id));
            }
        });
    }

    private boolean containsFace(String faceId) {
        return faceList.stream().anyMatch(f ->
                String.valueOf(f.getUserId()).equals(faceId));
    }

    private void cleanupResources() {
        try {
            EventBus.getDefault().unregister(this);
            FaceUtils.getInstance().destroy();
        } catch (Exception e) {
            LogUtils.e(Log.getStackTraceString(e));
        }
    }
}