package com.ycwl.basic.facebody.adapter;

import cn.hutool.core.codec.Base64;
import com.baidu.aip.face.AipFace;
import com.ycwl.basic.facebody.entity.AddFaceResp;
import com.ycwl.basic.facebody.entity.BceFaceBodyConfig;
import com.ycwl.basic.facebody.entity.SearchFaceResp;
import com.ycwl.basic.facebody.entity.SearchFaceResultItem;
import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter;
import com.ycwl.basic.utils.ratelimiter.IRateLimiter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class BceFaceBodyAdapter implements IFaceBodyAdapter {
    protected static final Map<String, AipFace> clients = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> addEntityLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> addFaceLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> addDbLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> listDbLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> listFaceLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> searchFaceLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> deleteDbLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> deleteEntityLimiters = new ConcurrentHashMap<>();
    private static final Map<String, IRateLimiter> deleteFaceLimiters = new ConcurrentHashMap<>();

    @Getter  // 添加getter,支持获取appId和addQps
    private BceFaceBodyConfig config;

    public boolean setConfig(BceFaceBodyConfig config) {
        this.config = config;
        return true;
    }

    @Override
    public boolean loadConfig(Map<String, String> _config) {
        BceFaceBodyConfig config = new BceFaceBodyConfig();
        config.setAppId(_config.get("appId"));
        config.setApiKey(_config.get("apiKey"));
        config.setSecretKey(_config.get("secretKey"));
        config.setAddQps(Float.parseFloat(_config.get("addQps")));
        config.setSearchQps(Float.parseFloat(_config.get("searchQps")));
        this.config = config;
        return true;
    }

    @Override
    public boolean addFaceDb(String dbName) {
        IRateLimiter addDbLimiter = getLimiter(LOCK_TYPE.ADD_DB);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            try {
                addDbLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.groupAdd(dbName, options);
            if (response.getInt("error_code") == 0) {
                return true;
            } else {
                log.warn("创建人脸库失败！{}", response);
                return false;
            }
        } catch (Exception e) {
            log.error("创建人脸库失败！", e);
            return false;
        }
    }

    @Override
    public boolean deleteFaceDb(String dbName) {
        IRateLimiter deleteDbLimiter = getLimiter(LOCK_TYPE.DELETE_DB);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            try {
                deleteDbLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.groupDelete(dbName, options);
            if (response.getInt("error_code") == 0) {
                return true;
            } else {
                log.warn("删除人脸库失败！{}", response);
                return false;
            }
        } catch (Exception e) {
            log.error("删除人脸库失败！", e);
            return false;
        }
    }

    @Override
    public List<String> listFaceDb() {
        IRateLimiter listDbLimiter = getLimiter(LOCK_TYPE.LIST_DB);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            options.put("start", "0");
            options.put("length", "1000");
            try {
                listDbLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.getGroupList(options);
            if (response.getInt("error_code") == 0) {
                JSONObject resultObj = response.getJSONObject("result");
                if (resultObj != null) {
                    JSONArray data = resultObj.getJSONArray("group_id_list");
                    List<String> result = new ArrayList<>();
                    for (int i = 0; i < data.length(); i++) {
                        result.add(data.getString(i));
                    }
                    return result;
                } else {
                    return Collections.emptyList();
                }
            } else {
                log.warn("获取人脸库列表失败！{}", response);
                return Collections.emptyList();
            }
        } catch (Exception e) {
            log.error("获取人脸库列表失败！", e);
            return Collections.emptyList();
        }
    }

    @Override
    public AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData) {
        IRateLimiter addEntityLimiter = getLimiter(LOCK_TYPE.ADD_FACE);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            options.put("user_info", extData);
//            options.put("quality_control", "LOW");
            options.put("action_type", "REPLACE");
            // QPS控制已由外层调度器管理,这里不再需要限流
            // 移除阻塞等待: addEntityLimiter.acquire()
            JSONObject response = client.addUser(faceUrl, "URL", dbName, entityId, options);
            int errorCode = response.getInt("error_code");
            if (errorCode == 0) {
                AddFaceResp resp = new AddFaceResp();
                resp.setScore(100f);
                return resp;
            } else if (errorCode == 222204) {
                // error_code: 222204 表示无法正常访问URL图片,尝试下载并转换为base64后重试
//                log.warn("无法正常访问URL图片,错误码: 222204,尝试下载图片转base64后重试,URL: {}", faceUrl);
                String base64Image = downloadImageAsBase64(faceUrl);
                if (base64Image != null) {
                    // 重试时也不需要限流,由外层调度器控制
                    JSONObject retryResponse = client.addUser(base64Image, "BASE64", dbName, entityId, options);
                    if (retryResponse.getInt("error_code") == 0) {
                        AddFaceResp resp = new AddFaceResp();
                        resp.setScore(100f);
                        return resp;
                    } else {
                        log.warn("使用base64重试添加人脸仍失败！{}", retryResponse);
                        return null;
                    }
                } else {
                    log.error("下载图片转base64失败,无法重试,URL: {}", faceUrl);
                    return null;
                }
            } else {
                log.warn("创建人脸失败！{}", response);
                return null;
            }
        } catch (Exception e) {
            log.error("创建人脸失败！", e);
            return null;
        }
    }

    @Override
    public boolean deleteFace(String dbName, String entityId) {
        IRateLimiter deleteFaceLimiter = getLimiter(LOCK_TYPE.DELETE_FACE);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            List<String> tokenList = listUserFace(dbName, entityId);
            if (tokenList == null) {
                return false;
            }
            AtomicInteger count = new AtomicInteger(0);
            tokenList.forEach(faceToken -> {
                try {
                    try {
                        deleteFaceLimiter.acquire();
                    } catch (InterruptedException ignored) {
                    }
                    JSONObject response = client.faceDelete(entityId, dbName, faceToken, options);
                    if (response.getInt("error_code") != 0) {
                        log.warn("删除人脸失败！{}", response);
                    } else {
                        count.incrementAndGet();
                    }
                } catch (Exception e) {
                    log.error("删除人脸失败！", e);
                }
            });
            return Integer.valueOf(count.get()).equals(tokenList.size());
        } catch (Exception e) {
            log.error("删除人脸失败！", e);
            return false;
        }
    }

    @Override
    public List<String> listFace(String dbName, String prefix, Integer offset, Integer size) {
        IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            options.put("start", offset == null ? "0" : offset.toString());
            options.put("length", size == null ? "1000" : size.toString());
            try {
                listFaceLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.getGroupUsers(dbName, options);
            if (response.getInt("error_code") == 0) {
                JSONObject resultObj = response.getJSONObject("result");
                if (resultObj != null) {
                    JSONArray data = resultObj.getJSONArray("user_id_list");
                    List<String> result = new ArrayList<>();
                    for (int i = 0; i < data.length(); i++) {
                        result.add(data.getString(i));
                    }
                    return result;
                } else {
                    return Collections.emptyList();
                }
            } else {
                log.warn("获取人脸列表失败！{}", response);
                return Collections.emptyList();
            }
        } catch (Exception e) {
            log.error("获取人脸列表失败！", e);
            return Collections.emptyList();
        }
    }

    public List<String> listUserFace(String dbName, String entityId) {
        IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE);
        try {
            AipFace client = getClient();
            HashMap<String, String> options = new HashMap<>();
            try {
                listFaceLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.faceGetlist(entityId, dbName, options);
            if (response.getInt("error_code") == 0) {
                JSONObject resultObj = response.getJSONObject("result");
                if (resultObj != null) {
                    try {
                        JSONArray faceList = resultObj.getJSONArray("face_list");
                        List<String> result = new ArrayList<>();
                        for (int i = 0; i < faceList.length(); i++) {
                            JSONObject jsonObject = faceList.getJSONObject(i);
                            result.add(jsonObject.getString("face_token"));
                        }
                        return result;
                    } catch (Exception e) {
                        return Collections.emptyList();
                    }
                } else {
                    return Collections.emptyList();
                }
            } else if (response.getInt("error_code") == 223103) {
                // 用户不存在
                return Collections.emptyList();
            } else {
                log.warn("获取人脸列表失败！{}", response);
                return null;
            }
        } catch (Exception e) {
            log.error("获取人脸列表失败！", e);
            return null;
        }
    }

    @Override
    public SearchFaceResp searchFace(String dbName, String faceUrl) {
        IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE);
        SearchFaceResp resp = new SearchFaceResp();
        try {
            AipFace client = getClient();
            HashMap<String, Object> options = new HashMap<>();
            options.put("max_user_num", "50");
            try {
                searchFaceLimiter.acquire();
            } catch (InterruptedException ignored) {
            }
            JSONObject response = client.search(faceUrl, "URL", dbName, options);
            int errorCode = response.getInt("error_code");
            if (errorCode == 0) {
                resp.setOriginalFaceScore(100f);
                JSONObject resultObj = response.getJSONObject("result");
                if (resultObj == null) {
                    resp.setFirstMatchRate(0f);
                    return resp;
                }
                JSONArray userList = resultObj.getJSONArray("user_list");
                List<SearchFaceResultItem> result = new ArrayList<>();
                for (int i = 0; i < userList.length(); i++) {
                    JSONObject user = userList.getJSONObject(i);
                    SearchFaceResultItem item = new SearchFaceResultItem();
                    item.setDbName(dbName);
                    item.setFaceId(user.getString("user_id"));
                    item.setExtData(user.getString("user_info"));
                    item.setScore(user.getBigDecimal("score").divide(BigDecimal.valueOf(100), 6, RoundingMode.HALF_UP).floatValue());
                    result.add(item);
                }
                resp.setResult(result);
                if (!result.isEmpty()) {
                    resp.setFirstMatchRate(result.getFirst().getScore());
                }
                return resp;
            } else if (errorCode == 222204) {
                // error_code: 222204 表示无法正常访问URL图片,尝试下载并转换为base64后重试
//                log.warn("搜索人脸时无法正常访问URL图片,错误码: 222204,尝试下载图片转base64后重试,URL: {}", faceUrl);
                String base64Image = downloadImageAsBase64(faceUrl);
                if (base64Image != null) {
                    try {
                        searchFaceLimiter.acquire();
                    } catch (InterruptedException ignored) {
                    }
                    JSONObject retryResponse = client.search(base64Image, "BASE64", dbName, options);
                    if (retryResponse.getInt("error_code") == 0) {
                        log.info("使用base64重试搜索人脸成功");
                        resp.setOriginalFaceScore(100f);
                        JSONObject resultObj = retryResponse.getJSONObject("result");
                        if (resultObj == null) {
                            resp.setFirstMatchRate(0f);
                            return resp;
                        }
                        JSONArray userList = resultObj.getJSONArray("user_list");
                        List<SearchFaceResultItem> result = new ArrayList<>();
                        for (int i = 0; i < userList.length(); i++) {
                            JSONObject user = userList.getJSONObject(i);
                            SearchFaceResultItem item = new SearchFaceResultItem();
                            item.setDbName(dbName);
                            item.setFaceId(user.getString("user_id"));
                            item.setExtData(user.getString("user_info"));
                            item.setScore(user.getBigDecimal("score").divide(BigDecimal.valueOf(100), 6, RoundingMode.HALF_UP).floatValue());
                            result.add(item);
                        }
                        resp.setResult(result);
                        if (!result.isEmpty()) {
                            resp.setFirstMatchRate(result.getFirst().getScore());
                        }
                        return resp;
                    } else {
                        log.warn("使用base64重试搜索人脸仍失败！{}", retryResponse);
                        resp.setOriginalFaceScore(0f);
                        return resp;
                    }
                } else {
                    log.error("下载图片转base64失败,无法重试,URL: {}", faceUrl);
                    resp.setOriginalFaceScore(0f);
                    return resp;
                }
            } else {
                log.warn("搜索人脸失败,错误码: {}, 响应: {}", errorCode, response);
                resp.setOriginalFaceScore(0f);
                return resp;
            }
        } catch (Exception e) {
            log.error("搜索人脸失败！", e);
            return null;
        }
    }

    public AipFace getClient() {
        if (clients.containsKey(config.getAppId())) {
            return clients.get(config.getAppId());
        }
        synchronized (clients) {
            if (clients.containsKey(config.getAppId())) {
                return clients.get(config.getAppId());
            }
            AipFace client = new AipFace(config.getAppId(), config.getApiKey(), config.getSecretKey());
            client.setConnectionTimeoutInMillis(5000);
            client.setSocketTimeoutInMillis(60000);
            clients.put(config.getAppId(), client);
            return client;
        }
    }

    /**
     * 下载图片并转换为base64字符串
     *
     * @param imageUrl 图片URL
     * @return base64编码的图片字符串,失败返回null
     */
    private String downloadImageAsBase64(String imageUrl) {
        BufferedImage image = null;
        ByteArrayOutputStream baos = null;
        try {
            // 下载图片
            URL url = new URL(imageUrl);
            image = ImageIO.read(url);
            if (image == null) {
                log.error("无法读取图片,URL: {}", imageUrl);
                return null;
            }

            // 转换为字节数组
            baos = new ByteArrayOutputStream();
            String format = "jpg";
            if (imageUrl.toLowerCase().endsWith(".png")) {
                format = "png";
            }
            ImageIO.write(image, format, baos);
            byte[] imageBytes = baos.toByteArray();

            // 编码为base64
            return Base64.encode(imageBytes);
        } catch (IOException e) {
            log.error("下载图片或转换base64失败,URL: {}", imageUrl, e);
            return null;
        } finally {
            if (image != null) {
                image.flush();
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    log.warn("关闭ByteArrayOutputStream失败", e);
                }
            }
        }
    }

    private IRateLimiter getLimiter(LOCK_TYPE type) {
        return switch (type) {
            case ADD_DB ->
                    addDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            case ADD_FACE ->
                    addFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(config.getAddQps()));
            case LIST_DB ->
                    listDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            case LIST_FACE ->
                    listFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            case SEARCH_FACE ->
                    searchFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(config.getSearchQps()));
            case DELETE_DB ->
                    deleteDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            case DELETE_ENTITY ->
                    deleteEntityLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            case DELETE_FACE ->
                    deleteFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(105, TimeUnit.MILLISECONDS));
            default -> new FixedRateLimiter(500, TimeUnit.MILLISECONDS);
        };
    }

    protected enum LOCK_TYPE {
        ADD_DB,
        ADD_FACE,
        LIST_DB,
        LIST_FACE,
        SEARCH_FACE,
        DELETE_DB,
        DELETE_ENTITY,
        DELETE_FACE,
    }
}
