package com.dji.sample.component.oss.service.impl;

import com.dji.sample.component.oss.model.OssConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
import software.amazon.awssdk.auth.signer.params.AwsS3V4SignerParams;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.regions.Region;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Map;

/**
 * VersityGW Admin API 客户端（S3 SigV4）。
 * 使用 {@link AwsS3V4Signer}，会自动带上并签名 x-amz-content-sha256。
 */
@Slf4j
@Component
public class VersityGwAdminClient {

    private static final String SERVICE = "s3";

    private final HttpClient httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    private final AwsS3V4Signer signer = AwsS3V4Signer.create();

    public void createUser(String accessKey, String secretKey, String role) {
        String body = ""
                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<Account>"
                + "<Access>" + xmlEscape(accessKey) + "</Access>"
                + "<Secret>" + xmlEscape(secretKey) + "</Secret>"
                + "<Role>" + xmlEscape(role) + "</Role>"
                + "</Account>";
        HttpResponse<String> response = execute(SdkHttpMethod.PATCH, "/create-user", null, body);
        int code = response.statusCode();
        if (code != 201 && code != 200) {
            throw new IllegalStateException("VersityGW create-user failed, status=" + code + ", body=" + response.body());
        }
        log.info("VersityGW temp user created: access={}", accessKey);
    }

    public void deleteUser(String accessKey) {
        HttpResponse<String> response = execute(
                SdkHttpMethod.PATCH, "/delete-user", Map.of("access", accessKey), null);
        int code = response.statusCode();
        if (code != 204 && code != 200 && code != 404) {
            throw new IllegalStateException("VersityGW delete-user failed, status=" + code + ", body=" + response.body());
        }
        log.info("VersityGW temp user deleted: access={}", accessKey);
    }

    public String listUsers() {
        HttpResponse<String> response = execute(SdkHttpMethod.PATCH, "/list-users", null, null);
        int code = response.statusCode();
        if (code != 200 && code != 204) {
            throw new IllegalStateException("VersityGW list-users failed, status=" + code + ", body=" + response.body());
        }
        return response.body() == null ? "" : response.body();
    }

    private HttpResponse<String> execute(SdkHttpMethod method, String path,
                                         Map<String, String> query, String body) {
        try {
            byte[] payload = body == null ? new byte[0] : body.getBytes(StandardCharsets.UTF_8);
            URI endpoint = URI.create(trimTrailingSlash(OssConfiguration.endpoint));
            SdkHttpFullRequest.Builder requestBuilder = SdkHttpFullRequest.builder()
                    .method(method)
                    .protocol(endpoint.getScheme())
                    .host(endpoint.getHost())
                    .port(resolvePort(endpoint))
                    .encodedPath(path)
                    .contentStreamProvider(() -> new ByteArrayInputStream(payload));
            if (query != null) {
                query.forEach(requestBuilder::putRawQueryParameter);
            }
            if (body != null) {
                requestBuilder.putHeader("Content-Type", "application/xml");
            }

            SdkHttpFullRequest signed = signer.sign(
                    requestBuilder.build(),
                    AwsS3V4SignerParams.builder()
                            .awsCredentials(AwsBasicCredentials.create(
                                    OssConfiguration.accessKey, OssConfiguration.secretKey))
                            .signingName(SERVICE)
                            .signingRegion(Region.of(OssConfiguration.region))
                            .build());

            HttpRequest.Builder httpBuilder = HttpRequest.newBuilder()
                    .uri(signed.getUri())
                    .timeout(Duration.ofSeconds(30))
                    .method(method.name(), HttpRequest.BodyPublishers.ofByteArray(payload));

            boolean hasContentSha256 = false;
            for (Map.Entry<String, List<String>> header : signed.headers().entrySet()) {
                String name = header.getKey();
                if ("Content-Length".equalsIgnoreCase(name) || "Host".equalsIgnoreCase(name)) {
                    continue;
                }
                if ("x-amz-content-sha256".equalsIgnoreCase(name)) {
                    hasContentSha256 = true;
                }
                for (String value : header.getValue()) {
                    httpBuilder.header(name, value);
                }
            }
            if (!hasContentSha256) {
                throw new IllegalStateException("Signed request missing x-amz-content-sha256 header");
            }

            return httpClient.send(httpBuilder.build(), HttpResponse.BodyHandlers.ofString());
        } catch (IllegalStateException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalStateException("VersityGW admin API call failed: " + path, e);
        }
    }

    private static int resolvePort(URI endpoint) {
        if (endpoint.getPort() != -1) {
            return endpoint.getPort();
        }
        return "https".equalsIgnoreCase(endpoint.getScheme()) ? 443 : 80;
    }

    private static String trimTrailingSlash(String endpoint) {
        if (endpoint != null && endpoint.endsWith("/")) {
            return endpoint.substring(0, endpoint.length() - 1);
        }
        return endpoint;
    }

    private static String xmlEscape(String value) {
        return value
                .replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "&apos;");
    }
}
