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

import cn.yunzhixue.ability.center.examsprint.domain.report.ReportType;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;

import java.lang.reflect.Constructor;
import java.net.URI;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

class AzureBlobExamSprintReportStorageTest {

    private static final Clock FIXED_CLOCK = Clock.fixed(Instant.parse("2026-01-03T08:00:00Z"), ZoneOffset.UTC);

    @Test
    void springSelectsTheAutowiredConstructor() throws Exception {
        AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();

        Constructor<?>[] candidateConstructors = beanPostProcessor.determineCandidateConstructors(
                AzureBlobExamSprintReportStorage.class,
                "azureBlobExamSprintReportStorage");

        Constructor<AzureBlobExamSprintReportStorage> expectedConstructor =
                AzureBlobExamSprintReportStorage.class.getConstructor(
                        String.class,
                        String.class,
                        String.class,
                        String.class,
                        String.class,
                        String.class,
                        Clock.class);

        assertThat(candidateConstructors).containsExactly(expectedConstructor);
    }

    @Test
    void generateDownloadUrlReturnsConfiguredBlobFileUrl() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);
        AzureBlobExamSprintReportStorage storage = new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                "https://dcjxbtest.blob.core.chinacloudapi.cn",
                FIXED_CLOCK);

        URI downloadUrl = storage.generateDownloadUrl(
                "exam-sprint-outlook-report-report-123.pdf",
                Duration.ofMinutes(15));

        assertThat(downloadUrl).isEqualTo(URI.create(
                "https://dcjxbtest.blob.core.chinacloudapi.cn/report/exam-sprint-outlook-report-report-123.pdf"));
        verifyNoInteractions(containerClient);
    }

    @Test
    void generateDownloadUrlNormalizesExtraSlashes() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);
        AzureBlobExamSprintReportStorage storage = new AzureBlobExamSprintReportStorage(
                containerClient,
                "/report/",
                "https://dcjxbtest.blob.core.chinacloudapi.cn/",
                FIXED_CLOCK);

        URI downloadUrl = storage.generateDownloadUrl(
                "/file.pdf/",
                Duration.ofMinutes(15));

        assertThat(downloadUrl).isEqualTo(URI.create(
                "https://dcjxbtest.blob.core.chinacloudapi.cn/report/file.pdf"));
    }

    @Test
    void generateDownloadUrlRejectsBlankStorageObjectKey() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);
        AzureBlobExamSprintReportStorage storage = new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                "https://dcjxbtest.blob.core.chinacloudapi.cn",
                FIXED_CLOCK);

        assertThatThrownBy(() -> storage.generateDownloadUrl("   ", Duration.ofMinutes(15)))
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("Azure storage storageObjectKey is incomplete");
    }

    @Test
    void generateDownloadUrlRejectsAllSlashStorageObjectKey() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);
        AzureBlobExamSprintReportStorage storage = new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                "https://dcjxbtest.blob.core.chinacloudapi.cn",
                FIXED_CLOCK);

        assertThatThrownBy(() -> storage.generateDownloadUrl("///", Duration.ofMinutes(15)))
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("Azure storage storageObjectKey is incomplete");
    }

    @Test
    void constructorRejectsAllSlashDownloadUrlPrefix() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);

        assertThatThrownBy(() -> new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                "///",
                FIXED_CLOCK))
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("Azure storage download-url-prefix is incomplete");
    }

    @Test
    void constructorRejectsBlankAfterTrimDownloadUrlPrefix() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);

        assertThatThrownBy(() -> new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                " / ",
                FIXED_CLOCK))
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("Azure storage download-url-prefix is incomplete");
    }

    @Test
    void uploadWritesUploadedAtMetadataFromInjectedClock() {
        BlobContainerClient containerClient = mock(BlobContainerClient.class);
        BlobClient blobClient = mock(BlobClient.class);
        when(containerClient.getBlobClient("file.pdf")).thenReturn(blobClient);
        AzureBlobExamSprintReportStorage storage = new AzureBlobExamSprintReportStorage(
                containerClient,
                "report",
                "https://dcjxbtest.blob.core.chinacloudapi.cn",
                FIXED_CLOCK);

        storage.upload(
                "report-123",
                ReportType.OUTLOOK,
                "file.pdf",
                new byte[]{1, 2, 3},
                Instant.parse("2026-01-10T00:00:00Z"));

        verify(blobClient).setMetadata(argThat(metadata ->
                "report-123".equals(metadata.get("reportId"))
                        && "OUTLOOK".equals(metadata.get("reportType"))
                        && "2026-01-10T00:00:00Z".equals(metadata.get("expiresAt"))
                        && FIXED_CLOCK.instant().toString().equals(metadata.get("uploadedAt"))));
    }
}
