package com.qmth.boot.core.fss.store.impl;

import com.qmth.boot.core.fss.exception.OssApiException;
import com.qmth.boot.core.fss.store.FileStore;
import com.qmth.boot.core.fss.utils.FssSigner;
import com.qmth.boot.core.fss.utils.OssApiParam;
import com.qmth.boot.core.fss.utils.OssConfig;
import com.qmth.boot.core.fss.utils.OssSigner;
import com.qmth.boot.tools.io.IOUtils;
import com.qmth.boot.tools.uuid.FastUUID;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class OssStore implements FileStore {

    private OkHttpClient client;

    private OssConfig config;

    private File tempDir;

    public OssStore(OssConfig config, String tempPath) {
        this.client = new OkHttpClient.Builder().connectionPool(new ConnectionPool())
                .connectTimeout(Duration.ofSeconds(10)).readTimeout(Duration.ofSeconds(30)).build();
        this.config = config;
        this.tempDir = new File(tempPath);
    }

    private Headers buildHeader(OssApiParam header, String authorization) {
        Headers.Builder builder = new Headers.Builder();
        builder.add("Date", OssSigner.getGmtDateTime(header.getDate()));
        builder.add("x-oss-date", OssSigner.getIso8601DateTime(header.getDate()));
        builder.add("x-oss-content-sha256", "UNSIGNED-PAYLOAD");
        if (header.getCopySource() != null) {
            builder.add("x-oss-copy-source", header.getCopySource());
        }
        if (header.getContentMd5() != null) {
            builder.add("content-md5", header.getContentMd5());
        }
        if (header.getContentType() != null) {
            builder.add("content-type", header.getContentType());
        }
        builder.add("Authorization", authorization);
        return builder.build();
    }

    @Override
    public void copy(String source, String target) throws Exception {
        source = formatPath(source);
        target = formatPath(target);
        OssApiParam header = new OssApiParam().setCopySource("/" + config.getBucket() + "/" + source);
        Request request = new Request.Builder().url(config.getEndpoint() + "/" + target)
                .headers(buildHeader(header, OssSigner.buildHeader(config, "put", target, header)))
                .put(RequestBody.create(null, new byte[0])).build();
        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) {
            String error = response.body() != null ? new String(response.body().bytes(), StandardCharsets.UTF_8) : "";
            throw new OssApiException(response.code(), error);
        }
    }

    @Override
    public void write(String path, InputStream ins, String md5) throws Exception {
        File tempFile = null;
        try {
            path = formatPath(path);
            OssApiParam param = new OssApiParam().setContentMd5(toBase64(md5))
                    .setContentType(URLConnection.guessContentTypeFromName(path));
            tempFile = writeToTemp(ins);
            RequestBody body = RequestBody.create(null, tempFile);
            Request request = new Request.Builder().url(config.getEndpoint() + "/" + path)
                    .headers(buildHeader(param, OssSigner.buildHeader(config, "put", path, param))).put(body).build();
            Response response = client.newCall(request).execute();
            if (!response.isSuccessful()) {
                String error =
                        response.body() != null ? new String(response.body().bytes(), StandardCharsets.UTF_8) : "";
                throw new OssApiException(response.code(), error);
            }
        } finally {
            if (tempFile != null) {
                tempFile.delete();
            }
        }
    }

    @Override
    public InputStream read(String path) throws Exception {
        path = formatPath(path);
        OssApiParam param = new OssApiParam();
        Request request = new Request.Builder().url(config.getEndpoint() + "/" + path)
                .headers(buildHeader(param, OssSigner.buildHeader(config, "get", path, param))).get().build();
        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) {
            String error = response.body() != null ? new String(response.body().bytes(), StandardCharsets.UTF_8) : "";
            throw new OssApiException(response.code(), error);
        } else {
            return response.body() != null ? response.body().byteStream() : null;
        }
    }

    @Override
    public boolean exist(String path) throws IOException {
        path = formatPath(path);
        OssApiParam param = new OssApiParam();
        Request request = new Request.Builder().url(config.getEndpoint() + "/" + path)
                .headers(buildHeader(param, OssSigner.buildHeader(config, "head", path, param))).head().build();
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return true;
        } else if (response.code() == 404) {
            return false;
        } else {
            String error = response.body() != null ? new String(response.body().bytes(), StandardCharsets.UTF_8) : "";
            throw new OssApiException(response.code(), error);
        }
    }

    @Override
    public boolean delete(String path) throws IOException {
        path = formatPath(path);
        OssApiParam param = new OssApiParam();
        Request request = new Request.Builder().url(config.getEndpoint() + "/" + path)
                .headers(buildHeader(param, OssSigner.buildHeader(config, "delete", path, param))).delete().build();
        Response response = client.newCall(request).execute();
        if (response.code() != 200 && response.code() != 204) {
            throw new OssApiException(response.code(), "");
        } else {
            return true;
        }
    }

    @Override
    public String getServerUrl(String path, Duration expireDuration) {
        path = formatPath(path);
        String url = getServer().concat(path);
        if (StringUtils.isNotBlank(config.getPortalHost()) && expireDuration != null) {
            url = url.concat("?").concat(OssSigner.buildQuery(config, "get", path, expireDuration));
        }
        return url;
    }

    @Override
    public String getServer() {
        return config.getPortal();
    }

    @Override
    public FssSigner getFssSigner() {
        return null;
    }

    private File writeToTemp(InputStream inputStream) throws IOException {
        //临时目录创建临时文件
        File tempFile = new File(tempDir, FastUUID.get());
        FileOutputStream outputStream = new FileOutputStream(tempFile);
        IOUtils.copy(inputStream, outputStream);
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
        return tempFile;
    }
}
