package net.northking.cctp.upperComputer.utils;

import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.ResultWrapper;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class HttpUtils {

    private static final RestTemplate restTemplate;

    private static final RestTemplate downLoadRestTemplate;

    private static final RestTemplate uiTreeRestTemplate;

    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    static {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(15000);
        factory.setReadTimeout(10*60*1000);
        restTemplate = new RestTemplate(factory);

        SimpleClientHttpRequestFactory downloadFactory = new SimpleClientHttpRequestFactory();
        downloadFactory.setConnectTimeout(15000);
        downloadFactory.setReadTimeout(10*60*1000);
        downLoadRestTemplate = new RestTemplate(downloadFactory);

        //读取数据不超时
        SimpleClientHttpRequestFactory uiTreeFactory = new SimpleClientHttpRequestFactory();
        uiTreeFactory.setConnectTimeout(15000);
        uiTreeRestTemplate = new RestTemplate(downloadFactory);

    }

    public static <T> T doPost(URI uri, HttpEntity<?> httpEntity, Class<T> responseType){
        ResponseEntity<T> response = null;
        try {
            response = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, responseType);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",(null == uri?"":uri.getPath()),e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", (null == uri?"":uri.getPath())));
        }
        return response.getBody();
    }

    public static <T> T doPost(String uri, HttpEntity<?> httpEntity, Class<T> responseType){
        URI uri1 = null;
        try {
            uri1 = new URI(uri);
        } catch (URISyntaxException e) {
            logger.error("请求地址有误：{}，原因",uri,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", uri));
        }
        ResponseEntity<T> response = null;
        try {
            response = restTemplate.exchange(uri1, HttpMethod.POST, httpEntity, responseType);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",uri,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", uri));
        }
        return response.getBody();
    }

    public static <T> T doGet(URI uri, Class<T> responseType){
        ResponseEntity<T> response = null;
        try {
            response = restTemplate.exchange(uri, HttpMethod.GET, null, responseType);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",(null == uri?"":uri.getPath()),e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", (null == uri?"":uri.getPath())));
        }
        return response.getBody();
    }


    public static void downloadFileToLocal(String sourceUrl,String targetDir,String fileName){
        RequestCallback callback = request -> request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
        long startTime = System.currentTimeMillis();
        downLoadRestTemplate.execute(sourceUrl, HttpMethod.GET, callback, clientHttpResponse -> {
            if (Files.exists(Paths.get(targetDir + "/" + fileName))) {
                Files.delete(Paths.get(targetDir + "/" + fileName));
            }
            Files.copy(clientHttpResponse.getBody(), Paths.get(targetDir + "/" + fileName), StandardCopyOption.REPLACE_EXISTING);
            return null;
        });
        long endTime = System.currentTimeMillis();
        logger.debug("===android download app request time: {}s",(endTime-startTime)/1000);
    }

    public static File downloadFileNoName(String sourceUrl,String targetDir,String tmpName){
        final String[] installAddr = {""};
        RequestCallback callback = request -> request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
        ResponseExtractor responseExtractor = clientHttpResponse -> {
            String fileName = null;
            HttpHeaders headers = clientHttpResponse.getHeaders();
            if (headers.containsKey("Content-Disposition")) {
                List<String> strings = headers.get("Content-Disposition");
                for (String str : strings) {
                    if (StringUtils.isNotBlank(str) && str.contains("filename=")) {
                        fileName = str.substring(str.indexOf("filename=") + 9);
                        break;
                    }
                }
            }
            if (StringUtils.isBlank(fileName)) {
                logger.warn("文件原名获取失败！");
                fileName = tmpName;
            }
            File appFile = new File(targetDir + "/" + fileName);
            logger.debug("临时文件地址："+appFile.getAbsolutePath());
            InputStream inputStream = clientHttpResponse.getBody();
            FileOutputStream outputStream = new FileOutputStream(appFile);
            byte[] data = new byte[1024 * 1024];
            int length = 0;
            while ((length = inputStream.read(data, 0, data.length)) > 0) {
                outputStream.write(data, 0, length);
            }
            installAddr[0] = appFile.getAbsolutePath();
            return appFile;
        };
        Object execute = downLoadRestTemplate.execute(sourceUrl, HttpMethod.GET, callback, responseExtractor);
        if (execute instanceof File) {
            return (File) execute;
        }
        return null;
    }

    public static String doUpload(String url, File file, HttpHeaders headers, String objId, String businessCode){
        URI uri1 = null;
        try {
            uri1 = new URI(url);
        } catch (URISyntaxException e) {
            logger.error("请求地址有误：{}，原因",url,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", url));
        }
        if (null == headers) {
            headers = new HttpHeaders();
        }
        headers.setContentType(MediaType.parseMediaType("multipart/form-data"));
        FileSystemResource resource = new FileSystemResource(file);
        LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("file", resource);
        paramMap.add("objId", objId);
        paramMap.add("businessCode", businessCode);
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(paramMap,headers);
        ResponseEntity<String> response = null;
        try {
            response = downLoadRestTemplate.exchange(uri1, HttpMethod.POST, entity, String.class);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",url,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", url));
        }
        return response.getBody();
    }

    public static String doUpload(String url, byte[] data, HttpHeaders headers, String objId, String businessCode){
        if (null == data || data.length <= 0) {
            throw new ParamMistakeException("上传的数据不能为空");
        }
        URI uri1 = null;
        try {
            uri1 = new URI(url);
        } catch (URISyntaxException e) {
            logger.error("请求地址有误：{}，原因",url,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", url));
        }
        if (null == headers) {
            headers = new HttpHeaders();
        }
        headers.setContentType(MediaType.parseMediaType("multipart/form-data"));
        ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(data);
        InputStreamResource resource = new InputStreamResource(arrayInputStream){
            @Override
            public long contentLength() throws IOException {
                return data.length;
            }
        };
        LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("file", resource);
        paramMap.add("objId", objId);
        paramMap.add("businessCode", businessCode);
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(paramMap,headers);
        ResponseEntity<String> response = null;
        try {
            response = downLoadRestTemplate.exchange(uri1, HttpMethod.POST, entity, String.class);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",url,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", url));
        }
        return response.getBody();
    }

    public static String doUpload(String url, File file, String tenantId, String objId, String businessCode){
        if (StringUtils.isBlank(tenantId)) {
            throw new ExecuteException("上传文件失败：缺少租户信息");
        }
        URI uri1 = null;
        try {
            uri1 = new URI(url);
        } catch (URISyntaxException e) {
            logger.error("请求地址有误：{}，原因",url,e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", url));
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("multipart/form-data"));
        FileSystemResource resource = new FileSystemResource(file);
        LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("file", resource);
        paramMap.add("tenantId",tenantId);
        paramMap.add("objId", objId);
        paramMap.add("businessCode", businessCode);
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(paramMap,headers);
        String fileId = null;
        try {
            logger.debug("请求地址：{}",url);
            ResultWrapper<Map<String, Object>> wrapper = downLoadRestTemplate.postForObject(url, entity, ResultWrapper.class);
            if (null != wrapper && wrapper.isSuccess()) {
                if (wrapper.getData() != null) {
                    if (wrapper.getData().containsKey("urlPath") && wrapper.getData().get("urlPath") != null) {
                        fileId = wrapper.getData().get("urlPath").toString();
                    }
                }
            } else {
                throw new ExecuteException("上传资源失败");
            }
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            throw new ExecuteException("上传资源失败：" + e.getMessage(), e);
        }
        return fileId;
    }

    public static Attachment upload(String url, byte[] data, String tenantId, String objId, String businessCode) {
        if (StringUtils.isBlank(tenantId)) {
            throw new ExecuteException("上传资源失败：缺少租户信息");
        }
        if (null == data || data.length <= 0) {
            throw new ParamMistakeException("上传的数据不能为空");
        }
        RestTemplate template = new RestTemplate();
        LinkedMultiValueMap<String, Object> multipartMap = new LinkedMultiValueMap<>();
        ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(data);
        InputStreamResource resource = new InputStreamResource(arrayInputStream){
            @Override
            public long contentLength() throws IOException {
                return data.length;
            }
            //上传二进制流时必须 要重写此方法，不然要GG
            @Override
            public String getFilename() {
                return System.currentTimeMillis() + ".png";
            }
        };
        multipartMap.add("file", resource);
        multipartMap.add("tenantId", tenantId);
        multipartMap.add("objId", objId);
        multipartMap.add("businessCode", businessCode);
        FormHttpMessageConverter converter = new FormHttpMessageConverter();
        converter.addPartConverter(new ResourceHttpMessageConverter());
        template.getMessageConverters().add(converter);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multipartMap, headers);
        Attachment result = null;
        try {
            logger.debug("请求地址：{}",url);
            ResultWrapper<Map<String, Object>> wrapper = template.postForObject(url, httpEntity, ResultWrapper.class);
            if (null != wrapper && wrapper.isSuccess()) {
                if (wrapper.getData() != null) {
                    result = new Attachment();
                    if (wrapper.getData().containsKey("id") && wrapper.getData().get("id") != null) {
                        result.setId(wrapper.getData().get("id").toString());
                    }
                    if (wrapper.getData().containsKey("urlPath") && wrapper.getData().get("urlPath") != null) {
                        result.setUrlPath(wrapper.getData().get("urlPath").toString());
                    }
                }
            } else {
                throw new ExecuteException("上传资源失败");
            }
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            throw new ExecuteException("上传资源失败：" + e.getMessage(), e);
        }
        return result;
    }
    /**
     * 上传资源
     * @param filePath
     * @param tenantId
     * @return
     */
    public static Attachment upload(String url, String filePath, String tenantId, String objId, String businessCode) {
        if (StringUtils.isBlank(tenantId)) {
            throw new ExecuteException("上传资源失败：缺少租户信息");
        }
        if (StringUtils.isBlank(filePath)) {
            throw new ExecuteException("上传资源失败：文件地址为空");
        }
        RestTemplate template = new RestTemplate();
        LinkedMultiValueMap<String, Object> multipartMap = new LinkedMultiValueMap<>();
        multipartMap.add("file", new FileSystemResource(filePath));
        multipartMap.add("tenantId", tenantId);
        multipartMap.add("objId", objId);
        multipartMap.add("businessCode", businessCode);
        FormHttpMessageConverter converter = new FormHttpMessageConverter();
        converter.addPartConverter(new ResourceHttpMessageConverter());
        template.getMessageConverters().add(converter);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multipartMap, headers);
        Attachment result = null;
        try {
            ResultWrapper<Map<String, Object>> wrapper = template.postForObject(url, httpEntity, ResultWrapper.class);
            if (null != wrapper && wrapper.isSuccess()) {
                if (wrapper.getData() != null) {
                    result = new Attachment();
                    if (wrapper.getData().containsKey("id") && wrapper.getData().get("id") != null) {
                        result.setId(wrapper.getData().get("id").toString());
                    }
                    if (wrapper.getData().containsKey("urlPath") && wrapper.getData().get("urlPath") != null) {
                        result.setUrlPath(wrapper.getData().get("urlPath").toString());
                    }
                }
            } else {
                throw new ExecuteException("上传资源失败");
            }
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            throw new ExecuteException("上传资源失败：" + e.getMessage(), e);
        }
        return result;
    }

    public static <T> T doGetUiTree(URI uri, Class<T> responseType) {
        ResponseEntity<T> response = null;
        try {
            response = uiTreeRestTemplate.exchange(uri, HttpMethod.GET, null, responseType);
        } catch (Exception e) {
            logger.error("请求出错,地址：{}，原因",(uri == null?"":uri.getPath()),e);
            throw new ParamMistakeException(String.format("http请求【%s】出错", (uri == null?"":uri.getPath())));
        }
        return response.getBody();
    }
}
