package com.heyu.api.controller.car;

import com.heyu.api.baidu.handle.traffic.BLicensePlateHandle;
import com.heyu.api.baidu.request.traffic.BLicensePlateRequest;
import com.heyu.api.baidu.response.traffic.BLicensePlateResp;
import com.heyu.api.controller.BaseController;
import com.heyu.api.data.annotation.NotIntercept;
import com.heyu.api.data.utils.ApiR;
import com.heyu.api.data.utils.R;
import com.heyu.api.request.car.LicensePlateRecognizeRequest;
import com.heyu.api.resp.car.RecognizeLicensePlateResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 车牌识别（百度OCR）
 *
 * 支持识别中国大陆机动车蓝牌、黄牌（单双行）、绿牌、大型新能源（黄绿）、领使馆车牌、警牌、武警牌（单双行）、
 * 军牌（单双行）、港澳出入境车牌、农用车牌、民航车牌、非机动车车牌（北京地区）的地域编号和车牌号，
 * 并能同时识别图像中的多张车牌。
 *
 * 百度文档：https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Flicense_plate&method=post
 */
@Slf4j
@RestController
@RequestMapping("/car/license/plate")
@NotIntercept
public class RecognizeLicensePlateController extends BaseController {

    @Autowired
    private BLicensePlateHandle bLicensePlateHandle;

    @PostMapping("/recognize")
    public R recognize(@RequestBody LicensePlateRecognizeRequest request) {
        long start = System.currentTimeMillis();

        if (request == null) {
            return R.error("请求参数不能为空");
        }
        if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) {
            return R.error("imageBase64和imageUrl不能同时为空");
        }
        log.info("车牌识别-参数校验耗时:{}ms", System.currentTimeMillis() - start);

        long t1 = System.currentTimeMillis();
        BLicensePlateRequest bRequest = toBaiduRequest(request);
        log.info("车牌识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1);

        long t2 = System.currentTimeMillis();
        ApiR<BLicensePlateResp> apiR = bLicensePlateHandle.handle(bRequest);
        long baiduCost = System.currentTimeMillis() - t2;
        log.info("车牌识别-调用百度OCR接口耗时:{}ms", baiduCost);

        if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) {
            return R.error(thirdError(apiR));
        }

        long t3 = System.currentTimeMillis();
        BLicensePlateResp bResp = apiR.getData();
        List<BLicensePlateResp.WordsResultDTO> plates = bResp.getWordsResult();
        if (CollectionUtils.isEmpty(plates)) {
            log.info("车牌识别-未识别到车牌信息, 耗时:{}ms", System.currentTimeMillis() - start);
            return R.error("未识别到车牌信息");
        }

        // 取第一张车牌
        BLicensePlateResp.WordsResultDTO plate = plates.get(0);
        RecognizeLicensePlateResp resp = new RecognizeLicensePlateResp();
        resp.setPlateNumber(plate.getNumber());
        resp.setPlateType(mapPlateColor(plate.getColor()));

        // 百度返回字符级置信度列表，取最小值作为整体置信度（保守策略），平均值作为车牌类型置信度
        List<Double> probabilities = plate.getProbability();
        if (CollectionUtils.isNotEmpty(probabilities)) {
            double minConf = probabilities.stream().mapToDouble(Double::doubleValue).min().orElse(0.0);
            resp.setConfidence((float) minConf);
            double avgConf = probabilities.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
            resp.setPlateTypeConfidence((float) avgConf);
        }
        log.info("车牌识别-结果映射耗时:{}ms", System.currentTimeMillis() - t3);

        log.info("车牌识别-总耗时:{}ms, 其中第三方(百度OCR):{}ms", System.currentTimeMillis() - start, baiduCost);
        return R.ok().setData(resp);
    }

    private BLicensePlateRequest toBaiduRequest(LicensePlateRecognizeRequest request) {
        BLicensePlateRequest bRequest = new BLicensePlateRequest();
        // base64编码后进行urlencode是百度API的要求，因为base64的+/=在form-urlencoded中有特殊含义
        if (isNotBlank(request.getImageBase64())) {
            bRequest.setImageBase64(encodeUrl(request.getImageBase64()));
        }
        // imageUrl 中可能包含 & 等特殊字符，需URL编码后再传给百度API，否则会被解析为form分隔符
        if (isNotBlank(request.getImageUrl())) {
            bRequest.setImageUrl(encodeUrl(request.getImageUrl()));
        }
        // detectComplete和detectRisk在BLicensePlateRequest中无默认值，handle的check()要求必须为true/false
        bRequest.setDetectComplete("false");
        bRequest.setDetectRisk("false");
        return bRequest;
    }

    private String encodeUrl(String url) {
        try {
            return URLEncoder.encode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URL编码失败:{}", url, e);
            return url;
        }
    }

    private boolean isNotBlank(String value) {
        return value != null && value.trim().length() > 0;
    }

    /**
     * 将百度车牌颜色映射为中文车牌类型
     */
    private String mapPlateColor(String color) {
        if (color == null) {
            return null;
        }
        switch (color.toLowerCase()) {
            case "blue":
                return "小型汽车";
            case "green":
                return "新能源车";
            case "yellow":
                return "大型汽车";
            case "white":
                return "警车";
            case "black":
                return "港澳车";
            default:
                return color;
        }
    }

}
