package com.esv.freight.file.common.component;

import com.alibaba.fastjson.JSONObject;
import com.esv.freight.file.common.constants.TianRangOcrConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Random;
import java.util.UUID;

/**
 * @description: 天壤OCR组件
 * @project: freight-file-service
 * @name: com.esv.freight.file.common.component.TianRangOcrComponent
 * @author: 黄朝斌
 * @email: huangchaobin@esvtek.com
 * @createTime: 2020/06/28 11:16
 * @version:1.0
 */
@Component
@Slf4j
public class TianRangOcrComponent {

    /**
     * description 图片文件OCR
     * param [ocrType, bytes]
     * return java.lang.String
     * author HuangChaobin
     * createTime 2020/06/28 14:14
     **/
    public String ocr(int ocrType, byte[] bytes) {
        // 创建临时文件
        File temp;
        try {
            temp = this.createTmpFile(bytes);
        } catch (IOException e) {
            log.error("创建临时文件失败：{}", e.getMessage(), e);
            return null;
        }

        // 构造Http请求内容
        CloseableHttpClient http = HttpClients.createDefault();
        HttpPost post = new HttpPost(TianRangOcrConstants.BASE_URL + "/public/api/esvtek/ai/" + ocrType);
        try {
            this.setPostUri(post, ocrType);

            FileBody fileBody = new FileBody(temp);
            MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
            multipartEntity.addPart(TianRangOcrConstants.MEDIA_ID, fileBody);
            post.setEntity(multipartEntity.build());
        } catch (Exception e) {
            log.error("构造OCR Http请求内容失败：{}", e.getMessage(), e);
            return null;
        } finally {
            temp.deleteOnExit();
        }

        // 执行Http请求
        String result = null;
        try {
            long startTime = System.currentTimeMillis();
            HttpResponse response = http.execute(post);
            long endTime = System.currentTimeMillis();
            log.info("ocr http request spend time {} ms", endTime - startTime);
            result = EntityUtils.toString(response.getEntity());
            log.info("ocr result: {}", result);
        } catch (IOException e) {
            log.error("OCR Http请求失败：{}", e.getMessage(), e);
        }

        return result;
    }

    /**
     * description 获取OCR数据
     * param [result]
     * return com.alibaba.fastjson.JSONObject
     * author HuangChaobin
     * createTime 2020/06/28 14:21
     **/
    public JSONObject getOcrData(String result) {
        if (null == result) {
            return null;
        }
        JSONObject resultJson = JSONObject.parseObject(result);
        if (TianRangOcrConstants.SUCCESS_CODE == resultJson.getIntValue("code")) {
            return resultJson.getJSONObject("data");
        } else {
            return null;
        }
    }

    private void setPostUri(HttpPost post, int ocrType) throws URISyntaxException {
        String nonce = this.generateNonce(6);
        Long timestamp = System.currentTimeMillis();
        StringBuffer sb = new StringBuffer();
        sb.append("POST");
        sb.append("\n");
        sb.append("/public/api/esvtek/ai/" + ocrType);
        sb.append("\n");
        sb.append("appkey=");
        sb.append(TianRangOcrConstants.APP_KEY);
        sb.append("&nonce=");
        sb.append(nonce);
        sb.append("&timestamp=");
        sb.append(timestamp);
        String sign = this.sha256_HMAC(sb.toString(), TianRangOcrConstants.APP_SECRET);

        URI uri = new URIBuilder(post.getURI()).
        addParameter("appkey", TianRangOcrConstants.APP_KEY).
                        addParameter("timestamp", String.valueOf(timestamp)).
                        addParameter("nonce", nonce).
                        addParameter("signature", sign).
                        build();
        post.setURI(uri);
    }

    /**
     * description 创建临时文件
     * param [bytes]
     * return java.io.File
     * author HuangChaobin
     * createTime 2020/06/28 13:42
     **/
    private File createTmpFile(byte[] bytes) throws IOException {
        File temp = File.createTempFile(UUID.randomUUID().toString(), ".jpg");
        FileOutputStream fos = new FileOutputStream(temp);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bos.write(bytes);
        return temp;
    }

    /**
     * sha256_HMAC加密
     * @param message 消息
     * @param secret  秘钥
     * @return 加密后字符串
     */
    private String sha256_HMAC(String message, String secret) {
        String hash = "";
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (Exception e) {
            log.error("Error HmacSHA256 =========== {}", e.getMessage(), e);
        }

        return hash;
    }

    /**
     * 将加密后的字节数组转换成字符串
     *
     * @param b 字节数组
     * @return 字符串
     */
    private String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String sTmp;
        for (int n = 0; b != null && n < b.length; n++) {
            sTmp = Integer.toHexString(b[n] & 0XFF);
            if (1 == sTmp.length()) {
                hs.append('0');
            }
            hs.append(sTmp);
        }
        return hs.toString().toLowerCase();
    }

    /**
     * description 生成随机数
     * param [len]
     * return java.lang.String
     * author HuangChaobin
     * createTime 2020/06/28 13:48
     **/
    private String generateNonce(int len) {
        String letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            int number = random.nextInt(62);
            sb.append(letters.charAt(number));
        }
        return sb.toString();
    }
}
