package com.yingpaipay.business.service.impl;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yingpaipay.business.domain.bo.AppletScanBo;
import com.yingpaipay.business.domain.dto.TextInImageToWordDTO;
import com.yingpaipay.business.domain.dto.TextInPdfToMarkdownDTO;
import com.yingpaipay.business.domain.dto.TextInR;
import com.yingpaipay.business.service.ITextInService;
import com.yingpaipay.common.file.config.TextInConfig;
import com.yingpaipay.setting.domain.vo.TextinSettingVo;
import com.yingpaipay.setting.service.ITextinSettingService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Service
@RequiredArgsConstructor
@Slf4j
public class TextInServiceImpl implements ITextInService {

    private static final String HEADER_APP_ID = "x-ti-app-id";
    private static final String HEADER_SECRET_CODE = "x-ti-secret-code";

    private final TextInConfig config;

    private final ITextinSettingService settingService;

    private <T> T getResponse(HttpRequest request, Class<T> clazz) {
        TextinSettingVo setting = settingService.query();

        try (HttpResponse response = request
            .header(HEADER_APP_ID, setting.getAppId())
            .header(HEADER_SECRET_CODE, setting.getSecretCode())
            .execute()) {

            if (!response.isOk()) {
                throw new BusinessException(MessageUtils.message("textin.networkerror"));
            }


            String body = response.body();
            log.info("返回的实体为 : {}", body);

            if (StringUtils.isEmpty(body)) {
                throw new BusinessException(MessageUtils.message("textin.recognitionfail"));
            }

            // 由于 jsonUtils 中自带的parseObject中, Jackson会自动将未匹配的泛型强行匹配成LinkedHashMap, 由此在转换为真实的对象时会报错。
            TextInR<T> result;
            ObjectMapper mapper = JsonUtils.getObjectMapper();
            JavaType type = mapper.getTypeFactory().constructParametricType(TextInR.class, clazz);
            try {
                result = mapper.readValue(body, type);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }

            if (result == null) {
                throw new BusinessException(MessageUtils.message("textin.recognitionfail"));
            }
            if (result.isError()) {
                throw new RuntimeException(result.getMessage());
            }
            return result.getResult();
        } catch (Exception e) {
            log.error("向 TextIn 发送请求执行失败", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public String imageToPdf(AppletScanBo bo) {
        return getResponse(
            HttpRequest.post(config.getImageToPdf()).body(JsonUtils.toJsonString(bo)),
            String.class
        );
    }


    @Override
    public String imageToWord(String image) {
        return getResponse(
            HttpRequest.post(config.getImageToWord())
                .header(Header.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                .body(Base64.getDecoder().decode(image)),
            TextInImageToWordDTO.class
        ).getDocx();
    }

    @Override
    public String wordToImage(String docx) {

        String zipBase64 = getResponse(
            HttpRequest.post(config.getWordToImage())
                .header(Header.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                .body(docx),
            String.class
        );

        if (zipBase64.contains(",")) {
            zipBase64 = zipBase64.substring(zipBase64.indexOf(",") + 1);
        }

        try {
            byte[] zipData = Base64.getDecoder().decode(zipBase64);

            Path tempDir = Files.createTempDirectory("word_to_image_");

            try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipData))) {
                ZipEntry entry;
                while ((entry = zis.getNextEntry()) != null) {
                    String name = entry.getName();
                    if (name.contains("..")) {
                        continue;
                    }

                    Path filePath = tempDir.resolve(name).normalize();

                    Files.createDirectories(filePath.getParent());

                    if (!entry.isDirectory()) {
                        try (OutputStream fos = Files.newOutputStream(filePath)) {
                            byte[] buffer = new byte[8192];
                            int len;
                            while ((len = zis.read(buffer)) > 0) {
                                fos.write(buffer, 0, len);
                            }
                        }
                    }
                    zis.closeEntry();
                }
            }

        } catch (IllegalArgumentException e) {
            throw new RuntimeException("BASE64 格式无效", e);
        } catch (IOException e) {
            throw new RuntimeException("解压失败", e);
        }

        return "";
    }

    @Override
    public String wordToPdf(String docx) {
        return getResponse(
            HttpRequest.post(config.getWordToPdf())
                .header(Header.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                .body(Base64.getDecoder().decode(docx)),
            String.class
        );
    }


    @Override
    public TextInPdfToMarkdownDTO pdfToMarkdown(String file) {
        return getResponse(
            HttpRequest.post(config.getPdfToMarkdown())
                .header(Header.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                .body(Base64.getDecoder().decode(file)),
            TextInPdfToMarkdownDTO.class
        );
    }

}
