package cn.yunzhixue.ability.center.examsprint.configuration;

import cn.yunzhixue.ability.center.examsprint.application.report.ExamSprintReportProperties;
import cn.yunzhixue.ability.center.examsprint.application.report.ExamSprintReportWarmupRunner;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportPdfGenerator;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportRenderer;
import cn.yunzhixue.ability.center.examsprint.domain.report.ReportContent;
import cn.yunzhixue.ability.center.examsprint.domain.report.ReportType;
import cn.yunzhixue.ability.center.examsprint.infrastructure.report.pdf.PlaywrightPdfWorker;
import cn.yunzhixue.ability.center.examsprint.infrastructure.report.pdf.PlaywrightPdfWorkerFactory;
import cn.yunzhixue.ability.center.examsprint.infrastructure.report.pdf.PooledPlaywrightExamSprintReportPdfGenerator;
import cn.yunzhixue.ability.center.examsprint.infrastructure.report.rendering.outlook.ClasspathOutlookExamSprintReportRenderer;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.time.Duration;
import java.time.Instant;

import static org.assertj.core.api.Assertions.assertThat;

class ExamSprintReportRuntimeConfigurationTest {

    @Test
    void examSprintReportPropertiesCopiesStorageDownloadUrlPrefixFromBoundProperties() {
        ExamSprintReportRuntimeConfiguration configuration = new ExamSprintReportRuntimeConfiguration();
        ExamSprintReportRuntimeConfiguration.BoundExamSprintReportProperties bound =
                new ExamSprintReportRuntimeConfiguration.BoundExamSprintReportProperties();
        bound.getStorage().setDownloadUrlPrefix("https://dcjxbtest.blob.core.chinacloudapi.cn");

        ExamSprintReportProperties properties = configuration.examSprintReportProperties(bound);

        assertThat(properties.getStorage().getDownloadUrlPrefix())
                .isEqualTo("https://dcjxbtest.blob.core.chinacloudapi.cn");
    }

    @Test
    void examSprintReportPropertiesCopiesPdfSettingsFromBoundProperties() {
        ExamSprintReportRuntimeConfiguration configuration = new ExamSprintReportRuntimeConfiguration();
        ExamSprintReportRuntimeConfiguration.BoundExamSprintReportProperties bound =
                new ExamSprintReportRuntimeConfiguration.BoundExamSprintReportProperties();
        bound.getPdf().setPoolSize(6);
        bound.getPdf().setBorrowTimeout(Duration.ofSeconds(12));
        bound.getPdf().setLaunchTimeout(Duration.ofSeconds(13));
        bound.getPdf().setRenderTimeout(Duration.ofSeconds(14));

        ExamSprintReportProperties properties = configuration.examSprintReportProperties(bound);

        assertThat(properties.getPdf().getPoolSize()).isEqualTo(6);
        assertThat(properties.getPdf().getBorrowTimeout()).isEqualTo(Duration.ofSeconds(12));
        assertThat(properties.getPdf().getLaunchTimeout()).isEqualTo(Duration.ofSeconds(13));
        assertThat(properties.getPdf().getRenderTimeout()).isEqualTo(Duration.ofSeconds(14));
    }

    @Test
    void examSprintReportWarmupRunnerIsRegisteredAndWiredBySpringContext() {
        new ApplicationContextRunner()
                .withBean(PlaywrightPdfWorkerFactory.class,
                        ExamSprintReportRuntimeConfigurationTest::fakePlaywrightPdfWorkerFactory)
                .withUserConfiguration(ExamSprintReportRuntimeConfiguration.class, WarmupRunnerCollaboratorsConfiguration.class)
                .run(context -> assertThat(context)
                        .hasSingleBean(ExamSprintReportWarmupRunner.class)
                        .hasBean("examSprintReportExecutor"));
    }

    @Test
    void runtimeConfigurationRegistersPooledPdfGenerator() {
        PlaywrightPdfWorkerFactory fakeFactory = fakePlaywrightPdfWorkerFactory();

        new ApplicationContextRunner()
                .withBean(PlaywrightPdfWorkerFactory.class, () -> fakeFactory)
                .withUserConfiguration(ExamSprintReportRuntimeConfiguration.class, WarmupRunnerCollaboratorsConfiguration.class)
                .withPropertyValues(
                        "ability.exam-sprint.report.pdf.pool-size=1",
                        "ability.exam-sprint.report.pdf.borrow-timeout=1s",
                        "ability.exam-sprint.report.pdf.launch-timeout=30s",
                        "ability.exam-sprint.report.pdf.render-timeout=30s")
                .run(context -> {
                    assertThat(context).hasSingleBean(PlaywrightPdfWorkerFactory.class);
                    assertThat(context.getBean(PlaywrightPdfWorkerFactory.class)).isSameAs(fakeFactory);
                    assertThat(context).hasSingleBean(ExamSprintReportPdfGenerator.class);
                    assertThat(context.getBean(ExamSprintReportPdfGenerator.class))
                            .isInstanceOf(PooledPlaywrightExamSprintReportPdfGenerator.class);
                });
    }

    @Test
    void classpathOutlookExamSprintReportRendererIsAutowiredBySpringContext() {
        new ApplicationContextRunner()
                .withUserConfiguration(OutlookRendererConfiguration.class)
                .run(context -> assertThat(context)
                        .hasBean("classpathOutlookExamSprintReportRenderer")
                        .hasSingleBean(ClasspathOutlookExamSprintReportRenderer.class)
                        .hasSingleBean(ExamSprintReportRenderer.class));
    }

    private static PlaywrightPdfWorkerFactory fakePlaywrightPdfWorkerFactory() {
        return () -> new PlaywrightPdfWorker() {
            @Override
            public byte[] generate(String htmlContent) {
                return new byte[] {1};
            }

            @Override
            public void close() {
            }
        };
    }

    @Configuration
    static class WarmupRunnerCollaboratorsConfiguration {

        @Bean
        ObjectMapper objectMapper() {
            return new ObjectMapper();
        }

        @Bean
        ExamSprintReportRenderer examSprintReportRenderer() {
            return new ExamSprintReportRenderer() {
                @Override
                public boolean supports(ReportType reportType) {
                    return true;
                }

                @Override
                public String render(ReportContent content, Instant generatedAt) {
                    return "html";
                }
            };
        }
    }

    @Configuration
    @Import(ClasspathOutlookExamSprintReportRenderer.class)
    static class OutlookRendererConfiguration {

        @Bean
        ObjectMapper objectMapper() {
            return new ObjectMapper();
        }
    }
}
