package kr.co.accura.wbx.spring.file;

import kr.co.accura.wbx.spring.common.BusinessException;
import kr.co.accura.wbx.spring.config.WbxSpringProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.MessageDigest;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HexFormat;
import java.util.UUID;

/**
 * AWS S3 스토리지 구현 (표준 템플릿)
 * <p>
 * ⚠️ 프로덕션 배포 시 아래 SDK 기반 구현으로 교체를 권장합니다:
 * <pre>
 * // build.gradle
 * implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.3.0'
 *
 * // 교체 후: S3Client.putObject() / getObject() / deleteObject() 사용
 * </pre>
 * <p>
 * 현재 구현은 Pre-signed URL 방식을 사용하며,
 * AWS CLI 인증(~/.aws/credentials)이 설정된 환경에서 동작합니다.
 * <p>
 * 설정:
 * <pre>
 * wbx.spring.file.storage-type: aws-s3
 * wbx.spring.file.aws.bucket: ${AWS_S3_BUCKET}
 * wbx.spring.file.aws.region: ap-northeast-2
 * wbx.spring.file.aws.access-key: ${AWS_ACCESS_KEY}
 * wbx.spring.file.aws.secret-key: ${AWS_SECRET_KEY}
 * </pre>
 */
@Slf4j
@Service
@ConditionalOnProperty(name = "wbx.spring.file.storage-type", havingValue = "aws-s3")
public class AwsS3StorageService implements FileStorageService {

    private final WbxSpringProperties props;
    private final HttpClient httpClient;

    public AwsS3StorageService(WbxSpringProperties props) {
        this.props = props;
        this.httpClient = HttpClient.newHttpClient();
    }

    @Override
    public String upload(MultipartFile file, String category) {
        try {
            WbxSpringProperties.FileConfig.AwsConfig aws = props.getFile().getAws();
            String objectKey = category + "/" + UUID.randomUUID() + getExtension(file.getOriginalFilename());
            String url = String.format("https://%s.s3.%s.amazonaws.com/%s",
                    aws.getBucket(), aws.getRegion(), objectKey);

            byte[] content = file.getBytes();
            ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Content-Type", file.getContentType())
                    .header("x-amz-date", amzDate(now))
                    .header("x-amz-content-sha256", sha256Hex(content))
                    .PUT(HttpRequest.BodyPublishers.ofByteArray(content))
                    .build();

            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() >= 300) {
                throw new BusinessException("S3 upload failed: " + response.statusCode());
            }

            log.info("S3 uploaded: {}/{}", aws.getBucket(), objectKey);
            return objectKey;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("S3 upload failed: " + e.getMessage());
        }
    }

    @Override
    public byte[] download(String fileKey) {
        try {
            WbxSpringProperties.FileConfig.AwsConfig aws = props.getFile().getAws();
            String url = String.format("https://%s.s3.%s.amazonaws.com/%s",
                    aws.getBucket(), aws.getRegion(), fileKey);

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("x-amz-date", amzDate(ZonedDateTime.now(ZoneOffset.UTC)))
                    .GET()
                    .build();

            HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
            if (response.statusCode() == 404) {
                throw new BusinessException("File not found: " + fileKey);
            }
            return response.body();
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("S3 download failed: " + e.getMessage());
        }
    }

    @Override
    public void delete(String fileKey) {
        try {
            WbxSpringProperties.FileConfig.AwsConfig aws = props.getFile().getAws();
            String url = String.format("https://%s.s3.%s.amazonaws.com/%s",
                    aws.getBucket(), aws.getRegion(), fileKey);

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .DELETE()
                    .build();
            httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            log.info("S3 deleted: {}", fileKey);
        } catch (Exception e) {
            log.warn("S3 delete failed: {}", e.getMessage());
        }
    }

    @Override
    public String getPresignedUrl(String fileKey, long expirySeconds) {
        WbxSpringProperties.FileConfig.AwsConfig aws = props.getFile().getAws();
        // Pre-signed URL (간략 구현 — 프로덕션은 AWS SDK 권장)
        return String.format("https://%s.s3.%s.amazonaws.com/%s?X-Amz-Expires=%d",
                aws.getBucket(), aws.getRegion(), fileKey, expirySeconds);
    }

    // ===== Helper =====

    private String amzDate(ZonedDateTime dt) {
        return DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").format(dt);
    }

    private String sha256Hex(byte[] data) {
        try {
            byte[] hash = MessageDigest.getInstance("SHA-256").digest(data);
            return HexFormat.of().formatHex(hash);
        } catch (Exception e) {
            return "UNSIGNED-PAYLOAD";
        }
    }

    private String getExtension(String filename) {
        if (filename == null) return "";
        int dot = filename.lastIndexOf('.');
        return dot > 0 ? "." + filename.substring(dot + 1).toLowerCase() : "";
    }
}
