package cn.yunzhixue.ability.center.examsprint.infrastructure.report.storage;

import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportType;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportStorage;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobContainerClientBuilder;
import com.azure.storage.blob.models.BlobHttpHeaders;
import com.azure.storage.common.StorageSharedKeyCredential;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;

@Component
@ConditionalOnProperty(prefix = "ability.exam-sprint.report.storage", name = "type", havingValue = "azure")
public class AzureBlobExamSprintReportStorage implements ExamSprintReportStorage {

    private final BlobContainerClient containerClient;
    private final Clock clock;

    public AzureBlobExamSprintReportStorage(
            @Value("${ability.exam-sprint.report.storage.container-name:exam-sprint-reports}") String containerName,
            @Value("${ability.exam-sprint.report.storage.connection-string:}") String connectionString,
            @Value("${ability.exam-sprint.report.storage.endpoint:}") String endpoint,
            @Value("${ability.exam-sprint.report.storage.account-name:}") String accountName,
            @Value("${ability.exam-sprint.report.storage.account-key:}") String accountKey,
            Clock clock) {
        this(buildContainerClient(containerName, connectionString, endpoint, accountName, accountKey), clock);
        this.containerClient.createIfNotExists();
    }

    AzureBlobExamSprintReportStorage(BlobContainerClient containerClient, Clock clock) {
        this.containerClient = containerClient;
        this.clock = clock;
    }

    @Override
    public StoredExamSprintReportFile upload(
            String reportId,
            ExamSprintReportType reportType,
            String fileName,
            byte[] pdfBytes,
            Instant expiresAt) {
        String blobName = "exam-sprint-reports/" + reportType.name().toLowerCase() + "/" + reportId + "/" + fileName;
        BlobClient blobClient = containerClient.getBlobClient(blobName);
        blobClient.upload(new ByteArrayInputStream(pdfBytes), pdfBytes.length, true);
        blobClient.setHttpHeaders(new BlobHttpHeaders().setContentType("application/pdf"));
        blobClient.setMetadata(Map.of(
                "reportId", reportId,
                "reportType", reportType.name(),
                "expiresAt", expiresAt.toString(),
                "uploadedAt", clock.instant().toString()));
        return new StoredExamSprintReportFile(blobName, fileName);
    }

    @Override
    public URI generateDownloadUrl(String storageObjectKey, Duration ttl) {
        return URI.create("/api/exam-sprint/reports/" + reportId(storageObjectKey) + "/download");
    }

    @Override
    public Optional<StoredExamSprintReportContent> download(String storageObjectKey) {
        BlobClient blobClient = containerClient.getBlobClient(storageObjectKey);
        if (!blobClient.exists()) {
            return Optional.empty();
        }

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        blobClient.downloadStream(outputStream);
        String contentType = blobClient.getProperties().getContentType();
        return Optional.of(new StoredExamSprintReportContent(
                fileName(storageObjectKey),
                outputStream.toByteArray(),
                contentType == null || contentType.isBlank() ? "application/pdf" : contentType));
    }

    @Override
    public void delete(String storageObjectKey) {
        containerClient.getBlobClient(storageObjectKey).deleteIfExists();
    }

    private String fileName(String storageObjectKey) {
        return storageObjectKey.substring(storageObjectKey.lastIndexOf('/') + 1);
    }

    private String reportId(String storageObjectKey) {
        String[] segments = storageObjectKey.split("/");
        return segments[2];
    }

    private static BlobContainerClient buildContainerClient(
            String containerName,
            String connectionString,
            String endpoint,
            String accountName,
            String accountKey) {
        BlobContainerClientBuilder builder = new BlobContainerClientBuilder().containerName(containerName);

        if (hasText(connectionString)) {
            builder.connectionString(connectionString);
        } else if (hasText(endpoint) && hasText(accountName) && hasText(accountKey)) {
            builder.endpoint(endpoint)
                    .credential(new StorageSharedKeyCredential(accountName, accountKey));
        } else {
            throw new IllegalStateException("Azure storage configuration is incomplete");
        }

        return builder.buildClient();
    }

    private static boolean hasText(String value) {
        return value != null && !value.isBlank();
    }
}
