package com.heyu.api.controller.car;

import com.heyu.api.baidu.request.financial.BTaxiReceiptRequest;
import com.heyu.api.controller.BaseController;
import com.heyu.api.data.annotation.EbAuthentication;
import com.heyu.api.data.constants.ApiConstants;
import com.heyu.api.data.utils.MapUtils;
import com.heyu.api.data.utils.R;
import com.heyu.api.data.utils.StringUtils;
import com.heyu.api.request.car.TaxiInvoiceRecognizeRequest;
import com.heyu.api.resp.car.RecognizeTaxiInvoiceResp;
import lombok.extern.slf4j.Slf4j;
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.util.Map;

/**
 * https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=财务票据OCR&api=rest%2F2.0%2Focr%2Fv1%2Ftaxi_receipt&method=post
 *
 * 出租车发票识别（百度OCR）
 *
 * 支持识别全国各大城市的出租车票的全部字段，包括发票号码、发票代码、电话、日期、金额、
 * 上下车时间、里程等。
 *
 * 接口返回参数：
 * <pre>
 * 参数              是否必须  类型      说明
 * log_id            是        uint64    请求标识码，随机数，唯一。
 * words_result_num  是        uint32    识别结果数，表示 words_result 的元素个数
 * words_result      是        object{}  识别结果数组
 *   + InvoiceCode           是  string  发票代码
 *   + InvoiceNum            是  string  发票号码
 *   + TaxiNum               是  string  车牌号
 *   + Date                  是  string  日期
 *   + Time                  是  string  上下车时间
 *   + PickupTime            是  string  上车时间
 *   + DropoffTime           是  string  下车时间
 *   + Fare                  是  string  金额
 *   + FuelOilSurcharge      是  string  燃油附加费
 *   + CallServiceSurcharge  是  string  叫车服务费
 *   + TotalFare             是  string  总金额
 *   + Location              是  string  开票城市
 *   + Province              是  string  省
 *   + City                  是  string  市
 *   + PricePerkm            是  string  单价
 *   + Distance              是  string  里程
 * pdf_file_size     否        string    传入PDF文件的总页数，当 pdf_file 参数有效时返回该字段
 * </pre>
 */
@Slf4j
@RestController
@RequestMapping("/taxi/invoice")
@SuppressWarnings("unchecked")
public class RecognizeTaxiInvoiceController extends BaseController {

    private static final String TAXI_RECEIPT_URI = "/rest/2.0/ocr/v1/taxi_receipt";

    @PostMapping("/recognize")
    @EbAuthentication(tencent = ApiConstants.TENCENT_AUTH)
    public R<RecognizeTaxiInvoiceResp> recognize(@RequestBody TaxiInvoiceRecognizeRequest request) {
        long start = System.currentTimeMillis();

        long t1 = System.currentTimeMillis();
        BTaxiReceiptRequest bRequest = toBaiduRequest(request);
        String checkMsg = checkRequest(bRequest);
        if (!isBlank(checkMsg)) {
            return R.error(checkMsg);
        }
        log.info("出租车发票识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1);

        long t2 = System.currentTimeMillis();
        Map<String, Object> data = requestBaidu(TAXI_RECEIPT_URI, getContent(bRequest));
        long baiduCost = System.currentTimeMillis() - t2;
        log.info("出租车发票识别-调用百度OCR接口耗时:{}ms", baiduCost);

        if (data == null) {
            log.info("出租车发票识别-识别失败, 耗时:{}ms", System.currentTimeMillis() - start);
            return R.error("出租车发票识别失败");
        }

        Map<String, Object> wordsResult = MapUtils.getByExpr(data, "words_result");
        if (wordsResult == null) {
            log.info("出租车发票识别-未识别到发票信息, 耗时:{}ms", System.currentTimeMillis() - start);
            return R.error("未识别到出租车发票信息");
        }

        long t3 = System.currentTimeMillis();
        RecognizeTaxiInvoiceResp resp = toResp(wordsResult);
        log.info("出租车发票识别-结果映射耗时:{}ms", System.currentTimeMillis() - t3);

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

    private BTaxiReceiptRequest toBaiduRequest(TaxiInvoiceRecognizeRequest request) {
        BTaxiReceiptRequest bRequest = new BTaxiReceiptRequest();
        if (request == null) {
            return bRequest;
        }
        if (StringUtils.isNotBlank(request.getImageBase64())) {
            bRequest.setImageBase64(encodeParam(request.getImageBase64()));
        }
        if (StringUtils.isNotBlank(request.getImageUrl())) {
            bRequest.setImageUrl(encodeParam(request.getImageUrl()));
        }
        return bRequest;
    }

    private String checkRequest(BTaxiReceiptRequest request) {
        if (!hasImage(request)) {
            return "imageUrl和imageBase64不能同时为空";
        }
        return null;
    }

    private boolean hasImage(BTaxiReceiptRequest request) {
        return request != null && (!isBlank(request.getImageUrl()) || !isBlank(request.getImageBase64()));
    }

    private String getContent(BTaxiReceiptRequest request) {
        return getImageContent(request).toString();
    }

    private StringBuffer getImageContent(BTaxiReceiptRequest request) {
        StringBuffer sb = new StringBuffer();
        if (StringUtils.isNotBlank(request.getImageBase64())) {
            sb.append("&image=").append(request.getImageBase64());
        }
        if (StringUtils.isNotBlank(request.getImageUrl())) {
            sb.append("&url=").append(request.getImageUrl());
        }
        return sb;
    }

    private RecognizeTaxiInvoiceResp toResp(Map<String, Object> wordsResult) {
        RecognizeTaxiInvoiceResp resp = new RecognizeTaxiInvoiceResp();
        resp.setDate((String) wordsResult.get("Date"));
        resp.setFare((String) wordsResult.get("Fare"));
        resp.setLocation((String) wordsResult.get("Location"));
        resp.setInvoiceCode((String) wordsResult.get("InvoiceCode"));
        resp.setInvoiceNum((String) wordsResult.get("InvoiceNum"));
        resp.setTaxiNum((String) wordsResult.get("TaxiNum"));
        resp.setTime((String) wordsResult.get("Time"));
        resp.setPickupTime((String) wordsResult.get("PickupTime"));
        resp.setDropoffTime((String) wordsResult.get("DropoffTime"));
        resp.setFuelOilSurcharge((String) wordsResult.get("FuelOilSurcharge"));
        resp.setCallServiceSurcharge((String) wordsResult.get("CallServiceSurcharge"));
        resp.setTotalFare((String) wordsResult.get("TotalFare"));
        resp.setProvince((String) wordsResult.get("Province"));
        resp.setCity((String) wordsResult.get("City"));
        resp.setPricePerkm((String) wordsResult.get("PricePerkm"));
        resp.setDistance((String) wordsResult.get("Distance"));
        return resp;
    }

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