package com.lingniu.ingest.eventhistory.config;

import com.lingniu.ingest.api.consumer.EnvelopeConsumerProcessor;
import com.lingniu.ingest.api.consumer.EnvelopeDeadLetterSink;
import com.lingniu.ingest.eventhistory.ApiExceptionHandler;
import com.lingniu.ingest.eventhistory.EventHistoryEnvelopeIngestor;
import com.lingniu.ingest.eventhistory.Gb32960DecodedFrameService;
import com.lingniu.ingest.eventhistory.Gb32960FrameController;
import com.lingniu.ingest.eventhistory.Jt808LocationHistoryController;
import com.lingniu.ingest.eventhistory.Jt808RawFrameHistoryController;
import com.lingniu.ingest.eventhistory.LocationHistoryController;
import com.lingniu.ingest.eventhistory.RawFrameHistoryController;
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960MessageDecoder;
import com.lingniu.ingest.tdenginehistory.TdengineHistoryReader;
import com.lingniu.ingest.tdenginehistory.TdengineHistoryWriter;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import java.nio.file.Path;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

class EventHistoryAutoConfigurationTest {

    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(EventHistoryAutoConfiguration.class))
            .withBean(EnvelopeDeadLetterSink.class, () -> record -> {})
            .withBean(TdengineHistoryWriter.class, () -> mock(TdengineHistoryWriter.class));

    @Test
    void createsHistoryBeansWhenEnabled() {
        contextRunner
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.sink.kafka.consumer.enabled=true")
                .run(context -> {
                    assertThat(context).hasSingleBean(EventHistoryEnvelopeIngestor.class);
                    assertThat(context.getBeansOfType(EnvelopeConsumerProcessor.class))
                            .containsOnlyKeys(
                                    "eventHistoryEnvelopeConsumerProcessor",
                                    "eventHistoryRawEnvelopeConsumerProcessor");
                    assertThat(context).doesNotHaveBean(Gb32960DecodedFrameService.class);
                    assertThat(context).doesNotHaveBean(Gb32960FrameController.class);
                });
    }

    @Test
    void backsOffWhenDisabled() {
        contextRunner.run(context -> {
            assertThat(context).doesNotHaveBean(EventHistoryEnvelopeIngestor.class);
        });
    }

    @Test
    void createsGb32960FrameBeansWhenDecoderExists() {
        contextRunner
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.event-history.api.specialized-enabled=true",
                        "lingniu.ingest.event-history.archive-path=/tmp/lingniu-test-archive")
                .withBean(Gb32960MessageDecoder.class, () -> mock(Gb32960MessageDecoder.class))
                .run(context -> {
                    assertThat(context).hasSingleBean(Gb32960DecodedFrameService.class);
                    assertThat(context).hasSingleBean(Gb32960FrameController.class);
                });
    }

    @Test
    void createsGb32960FrameBeansWithTdengineReader() {
        new ApplicationContextRunner()
                .withConfiguration(AutoConfigurations.of(EventHistoryAutoConfiguration.class))
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.event-history.api.specialized-enabled=true",
                        "lingniu.ingest.event-history.archive-path=/tmp/lingniu-test-archive")
                .withBean(Gb32960MessageDecoder.class, () -> mock(Gb32960MessageDecoder.class))
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> {
                    assertThat(context).hasSingleBean(Gb32960DecodedFrameService.class);
                    assertThat(context).hasSingleBean(Gb32960FrameController.class);
                });
    }

    @Test
    void createsHistoryConsumerWithTdengineWriter() {
        new ApplicationContextRunner()
                .withConfiguration(AutoConfigurations.of(EventHistoryAutoConfiguration.class))
                .withBean(EnvelopeDeadLetterSink.class, () -> record -> {})
                .withBean(TdengineHistoryWriter.class, () -> mock(TdengineHistoryWriter.class))
                .withPropertyValues("lingniu.ingest.event-history.enabled=true")
                .run(context -> {
                    assertThat(context).hasSingleBean(EventHistoryEnvelopeIngestor.class);
                    assertThat(context.getBeansOfType(EnvelopeConsumerProcessor.class))
                            .containsOnlyKeys(
                                    "eventHistoryEnvelopeConsumerProcessor",
                                    "eventHistoryRawEnvelopeConsumerProcessor");
                });
    }

    @Test
    void createsJt808LocationHistoryControllerWhenTdengineReaderExists() {
        contextRunner
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.event-history.api.specialized-enabled=true")
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> assertThat(context).hasSingleBean(Jt808LocationHistoryController.class));
    }

    @Test
    void createsGenericLocationAndRawControllersWithoutTelemetryFieldsByDefault() {
        contextRunner
                .withPropertyValues("lingniu.ingest.event-history.enabled=true")
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> {
                    assertThat(context).hasSingleBean(LocationHistoryController.class);
                    assertThat(context).hasSingleBean(RawFrameHistoryController.class);
                    assertThat(context).hasSingleBean(ApiExceptionHandler.class);
                    assertThat(context).doesNotHaveBean("telemetryFieldHistoryController");
                    assertThat(context).doesNotHaveBean(Jt808LocationHistoryController.class);
                    assertThat(context).doesNotHaveBean(Jt808RawFrameHistoryController.class);
                });
    }

    @Test
    void createsGenericRawFrameHistoryControllerWhenTdengineReaderExists() {
        contextRunner
                .withPropertyValues("lingniu.ingest.event-history.enabled=true")
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> assertThat(context).hasSingleBean(RawFrameHistoryController.class));
    }

    @Test
    void createsJt808RawFrameHistoryControllerWhenTdengineReaderExists() {
        contextRunner
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.event-history.api.specialized-enabled=true")
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> assertThat(context).hasSingleBean(Jt808RawFrameHistoryController.class));
    }

    @Test
    void rejectsRemovedTelemetryFieldsSwitch() {
        contextRunner
                .withPropertyValues(
                        "lingniu.ingest.event-history.enabled=true",
                        "lingniu.ingest.tdengine-history.telemetry-fields-enabled=true")
                .withBean(TdengineHistoryReader.class, () -> mock(TdengineHistoryReader.class))
                .run(context -> assertThat(context)
                        .hasFailed()
                        .getFailure()
                        .hasMessageContaining("tdengine-history.telemetry-fields-enabled has been removed")
                        .hasMessageContaining("raw_frames"));
    }

    @Test
    void parsesFileUriArchiveRoot() {
        assertThat(EventHistoryAutoConfiguration.archiveRoot("file:///tmp/lingniu-test-archive"))
                .isEqualTo(Path.of("/tmp/lingniu-test-archive"));
    }
}
