package cn.reghao.oss.sdk;

import cn.reghao.jutil.jdk.http.UploadParam;
import cn.reghao.jutil.jdk.http.WebRequest;
import cn.reghao.jutil.jdk.http.WebResponse;
import cn.reghao.jutil.jdk.io.FilePart;
import cn.reghao.jutil.jdk.result.WebResult;
import cn.reghao.jutil.jdk.security.DigestUtil;
import cn.reghao.jutil.jdk.serializer.JsonConverter;
import cn.reghao.jutil.tool.http.DefaultWebRequest;
import cn.reghao.oss.storeapi.rest.UploadFilePart;
import cn.reghao.oss.storeapi.rest.UploadFileRet;
import cn.reghao.oss.storeapi.rest.UploadPrepareRet;
import cn.reghao.oss.storeapi.rest.UploadedPart;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author reghao
 * @date 2023-06-02 16:19:04
 */
@Slf4j
public class ObjectMultipartUploadService {
    private final String endpoint;
    private final FilePart filePart = new FilePart();
    private final HttpClient httpClient = HttpClient.newBuilder().build();
    private final WebRequest webRequest = new DefaultWebRequest();

    public ObjectMultipartUploadService(String endpoint) {
        this.endpoint = endpoint;
    }

    public void create() throws URISyntaxException, IOException, InterruptedException {
        MultiPartBodyPublisher publisher = new MultiPartBodyPublisher();
        publisher.addPart("filename", "1.zip")
                .addPart("size", 1+"")
                .addPart("sha256sum", "1234567890");

        String authorization = "";
        String api = endpoint + "/?create";
        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
                .version(HttpClient.Version.HTTP_1_1)
                .header("Authorization", authorization)
                .header("Content-Type", "multipart/form-data; boundary=" + publisher.getBoundary())
                .POST(publisher.build())
                .build();

        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
        String body = httpResponse.body();
        Type type = new TypeToken<WebResult<UploadPrepareRet>>(){}.getType();
        WebResult<UploadPrepareRet> webResult = JsonConverter.jsonToObject(body, type);
        if (webResult.getCode() != 0) {
            String errMsg = webResult.getMsg();
        }

        UploadPrepareRet uploadPrepareRet = webResult.getData();
        System.out.println();
    }

    public void get() throws URISyntaxException, IOException, InterruptedException {
        String authorization = "";
        String api = endpoint + "/?multipart";
        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
                .version(HttpClient.Version.HTTP_1_1)
                .header("Authorization", authorization)
                //.header("Content-Type", "multipart/form-data; boundary=" + publisher.getBoundary())
                .GET()
                .build();

        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
        String body = httpResponse.body();
        Type type = new TypeToken<WebResult<UploadedPart>>(){}.getType();
        WebResult<UploadedPart> webResult = JsonConverter.jsonToObject(body, type);
        if (webResult.getCode() != 0) {
            String errMsg = webResult.getMsg();
        }

        UploadedPart uploadedPart = webResult.getData();
    }

    /**
     * jdk http 实现
     * 存在 bug: https://bugs.openjdk.org/browse/JDK-8222968
     *
     * @param
     * @return
     * @date 2023-05-24 14:56:50
     */
    public void postObject1(byte[] bytes, UploadFilePart uploadFilePart)
            throws URISyntaxException, IOException, InterruptedException {
        MultiPartBodyPublisher publisher = new MultiPartBodyPublisher();
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        publisher.addPart("file", () -> bais, uploadFilePart.getFilename(), "")
                //.addPart("file", Path.of(""))
                .addPart("channelId", uploadFilePart.getChannelId()+"")
                .addPart("identifier", uploadFilePart.getIdentifier())
                .addPart("filename", uploadFilePart.getFilename())
                .addPart("relativePath", uploadFilePart.getRelativePath())
                .addPart("totalSize", uploadFilePart.getTotalSize()+"")
                .addPart("chunkSize", uploadFilePart.getChunkSize()+"")
                .addPart("currentChunkSize", uploadFilePart.getCurrentChunkSize()+"")
                .addPart("totalChunks", uploadFilePart.getTotalChunks()+"")
                .addPart("chunkNumber", uploadFilePart.getChunkNumber()+"");

        String authorization = "";
        String api = endpoint + "/?multipart";
        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
                .version(HttpClient.Version.HTTP_1_1)
                .header("Authorization", authorization)
                .header("Content-Type", "multipart/form-data; boundary=" + publisher.getBoundary())
                .POST(publisher.build())
                .build();

        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
        System.out.println();
    }

    public void postObject(byte[] bytes, UploadFilePart uploadFilePart) {
        UploadParam uploadParam = new UploadParam(bytes, "");
        Map<String, String> params = new HashMap<>();
        params.put("channelId", uploadFilePart.getChannelId()+"");
        params.put("identifier", uploadFilePart.getIdentifier());
        params.put("filename", uploadFilePart.getFilename());
        params.put("relativePath", uploadFilePart.getRelativePath());
        params.put("totalSize", uploadFilePart.getTotalSize()+"");
        params.put("chunkSize", uploadFilePart.getChunkSize()+"");
        params.put("currentChunkSize", uploadFilePart.getCurrentChunkSize()+"");
        params.put("totalChunks", uploadFilePart.getTotalChunks()+"");
        params.put("chunkNumber", uploadFilePart.getChunkNumber()+"");
        uploadParam.setTextParams(params);

        String api = endpoint + "/?multipart";
        WebResponse webResponse = webRequest.upload(api, uploadParam, null);
        int statusCode = webResponse.getStatusCode();
        if (statusCode != 200) {
            log.error("请求失败");
        }

        String body = webResponse.getBody();
        Type type = new TypeToken<WebResult<UploadFileRet>>(){}.getType();
        WebResult<UploadFileRet> webResult = JsonConverter.jsonToObject(body, type);
        if (webResult.getCode() != 0) {
            String errMsg = webResult.getMsg();
        }

        UploadFileRet uploadFileRet = webResult.getData();
        if (!uploadFileRet.isMerged()) {
            log.info("继续上传");
        } else {
            log.info("分片上传并合并完成");
        }
    }

    public void upload(File file, int channelId) throws Exception {
        String identifier = DigestUtil.sha256sum(new FileInputStream(file));
        String filename = file.getName();
        String relativePath = file.getAbsolutePath();

        long totalSize = file.length();
        // 分段大小在 5MB - 5GB 之间，只有最后一个分段才允许小于 5MB，不可避免的
        int chunkSize = filePart.getPartSize();
        long totalChunks = totalSize/chunkSize;
        if (totalSize % chunkSize != 0) {
            totalChunks += 1;
        }

        for (long i = 0; i < totalChunks; i++) {
            long start = i*chunkSize;
            byte[] part = filePart.getPart(file.getAbsolutePath(), chunkSize, start);
            long chunkNumber = i + 1;
            int currentChunkSize = part.length;
            UploadFilePart uploadFilePart = new UploadFilePart(channelId, identifier, filename, relativePath,
                    totalSize, chunkSize, totalChunks, chunkNumber, currentChunkSize);
            postObject(part, uploadFilePart);
        }
    }
}
