package com.lingniu.ingest.historyapp;

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

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

class MavenModuleProfileTest {

    private static final List<String> ACTIVE_APP_POMS = List.of(
            "modules/apps/gb32960-ingest-app/pom.xml",
            "modules/apps/jt808-ingest-app/pom.xml",
            "modules/apps/yutong-mqtt-app/pom.xml",
            "modules/apps/vehicle-history-app/pom.xml",
            "modules/apps/vehicle-analytics-app/pom.xml");

    @Test
    void commandGatewayIsOutsideDefaultProductionReactor() throws Exception {
        Document pom = rootPom();

        assertThat(defaultModules(pom))
                .doesNotContain("modules/apps/command-gateway")
                .doesNotContain("modules/protocols/protocol-jt1078");

        assertThat(profileModules(pom, "optional-command-gateway"))
                .containsExactly(
                        "modules/protocols/protocol-jt1078",
                        "modules/apps/command-gateway");
    }

    @Test
    void jsatl12AttachmentProtocolIsOptionalAndOutsideDefaultProductionReactor() throws Exception {
        Document pom = rootPom();

        assertThat(defaultModules(pom))
                .doesNotContain("modules/protocols/protocol-jsatl12");

        assertThat(profileModules(pom, "optional-attachments"))
                .containsExactly("modules/protocols/protocol-jsatl12");
    }

    @Test
    void latestStateServiceIsOptionalAndOutsideDefaultProductionReactor() throws Exception {
        Document pom = rootPom();

        assertThat(defaultModules(pom))
                .doesNotContain("modules/services/vehicle-state-service");

        assertThat(profileModules(pom, "optional-latest-state"))
                .containsExactly("modules/services/vehicle-state-service");
    }

    @Test
    void rawArchiveStorePrototypeIsRemovedFromBuildSurface() throws Exception {
        Document pom = rootPom();

        assertThat(defaultModules(pom))
                .doesNotContain("modules/sinks/raw-archive-store");

        assertThat(profileModules(pom, "optional-raw-archive-store"))
                .as("raw bytes are written by sink-archive; raw-archive-store was an unused prototype")
                .isEmpty();
        assertThat(Files.exists(repositoryRoot().resolve("modules/sinks/raw-archive-store")))
                .isFalse();
    }

    @Test
    void eventFileStoreCompatibilityPathIsRemovedFromProductionStack() throws Exception {
        Document pom = rootPom();
        String readme = Files.readString(repositoryRoot().resolve("README.md"));

        assertThat(defaultModules(pom))
                .doesNotContain("modules/sinks/event-file-store");
        assertThat(profileModules(pom, "optional-event-file-store"))
                .as("DuckDB/Parquet event-file-store has been replaced by TDengine raw history")
                .isEmpty();
        assertThat(Files.exists(repositoryRoot().resolve("modules/sinks/event-file-store")))
                .isFalse();
        assertThat(rootDependencyManagementArtifacts(pom))
                .doesNotContain("event-file-store");
        assertThat(hasProjectProperty(pom, "duckdb.version"))
                .isFalse();
        assertThat(hasDependency(pom, "org.duckdb", "duckdb_jdbc"))
                .isFalse();
        assertThat(readme)
                .doesNotContain("event-file-store")
                .doesNotContain("DuckDB")
                .doesNotContain("Parquet");
    }

    @Test
    void kafkaSinkModuleUsesKafkaNamingInsteadOfMqNaming() throws Exception {
        Document pom = rootPom();
        Document kafkaSinkPom = modulePom("modules/sinks/sink-kafka/pom.xml");
        String readme = Files.readString(repositoryRoot().resolve("README.md"));
        String legacyModule = "modules/sinks/sink-" + "mq";
        String legacyArtifact = "sink-" + "mq";

        assertThat(defaultModules(pom))
                .contains("modules/sinks/sink-kafka")
                .doesNotContain(legacyModule);
        assertThat(rootDependencyManagementArtifacts(pom))
                .contains("sink-kafka")
                .doesNotContain(legacyArtifact);
        assertThat(firstDirectChild(kafkaSinkPom.getDocumentElement(), "artifactId").getTextContent())
                .isEqualTo("sink-kafka");
        assertThat(readme)
                .contains("sink-kafka/")
                .doesNotContain(legacyArtifact);
    }

    @Test
    void xindaIsLegacyOnlyAndOutsideDefaultProductionReactor() throws Exception {
        Document pom = rootPom();

        assertThat(defaultModules(pom))
                .doesNotContain("modules/inbound/inbound-xinda-push")
                .doesNotContain("modules/apps/xinda-push-app");

        assertThat(profileModules(pom, "legacy-xinda"))
                .containsExactly(
                        "modules/inbound/inbound-xinda-push",
                        "modules/apps/xinda-push-app");
    }

    @Test
    void privateLingniuRepositoryIsScopedToLegacyXindaProfile() throws Exception {
        Document pom = rootPom();

        assertThat(repositoryIds(pom.getDocumentElement()))
                .doesNotContain("lingniu-nexus");
        assertThat(profileRepositoryIds(pom, "legacy-xinda"))
                .containsExactly("lingniu-nexus");
    }

    @Test
    void optionalModuleVersionsAreScopedToTheirProfiles() throws Exception {
        Document pom = rootPom();

        assertThat(rootDependencyManagementArtifacts(pom))
                .doesNotContain(
                        "protocol-jt1078",
                        "protocol-jsatl12",
                        "vehicle-state-service",
                        "raw-archive-store",
                        "command-gateway",
                        "inbound-xinda-push",
                        "xinda-push-app");

        assertThat(profileDependencyManagementArtifacts(pom, "optional-command-gateway"))
                .containsExactly("protocol-jt1078", "command-gateway");
        assertThat(profileDependencyManagementArtifacts(pom, "optional-attachments"))
                .containsExactly("protocol-jsatl12");
        assertThat(profileDependencyManagementArtifacts(pom, "optional-latest-state"))
                .containsExactly("vehicle-state-service");
        assertThat(profileDependencyManagementArtifacts(pom, "optional-raw-archive-store"))
                .isEmpty();
        assertThat(profileDependencyManagementArtifacts(pom, "optional-event-file-store"))
                .isEmpty();
        assertThat(profileDependencyManagementArtifacts(pom, "legacy-xinda"))
                .containsExactly("inbound-xinda-push", "xinda-push-app");
    }

    @Test
    void appsDirectoryContainsOnlyRealMavenModules() throws Exception {
        try (Stream<Path> apps = Files.list(repositoryRoot().resolve("modules/apps"))) {
            assertThat(apps.filter(Files::isDirectory)
                    .filter(path -> !Files.exists(path.resolve("pom.xml")))
                    .map(path -> path.getFileName().toString()))
                    .isEmpty();
        }
    }

    @Test
    void readmeMatchesCurrentRuntimeVersionsAndSplitAppResponsibilities() throws Exception {
        Document pom = rootPom();
        String readme = Files.readString(repositoryRoot().resolve("README.md"));

        assertThat(readme)
                .contains("Spring Boot " + projectProperty(pom, "spring-boot.version"))
                .contains("Netty " + projectProperty(pom, "netty.version"))
                .contains("Eclipse Paho " + projectProperty(pom, "paho-mqtt.version"))
                .contains("session-core/            设备会话 + 鉴权 + Token + Redis SessionStore")
                .contains("| 事件流 | **Kafka**（唯一生产消息通道，vin 分区，保证单车有序） |")
                .contains("| 构建 | Maven |")
                .contains("protocol-jt1078/         JT/T 1078（optional-command-gateway profile")
                .contains("protocol-jsatl12/        苏标主动安全报警附件（optional-attachments profile）")
                .contains("vehicle-state-service/   Kafka 全字段事件消费 + Redis 热状态查询（optional-latest-state profile）")
                .contains("command-gateway/         可选 HTTP → 设备下行命令（optional-command-gateway profile）")
                .contains("vehicle-history-app/     TDengine 历史查询 + RAW JSON")
                .contains("vehicle-analytics-app/   JT808 每日里程指标消费")
                .contains("协议接入应用只负责收、解析、校验、规整和投递 Kafka")
                .contains("生产链路只支持 Kafka")
                .contains("`vehicle-analytics-app` 将 JT808 `daily_mileage_km` 写入 MySQL `vehicle_stat_metric`")
                .contains("mvn -pl :gb32960-ingest-app,:jt808-ingest-app,:yutong-mqtt-app,:vehicle-history-app,:vehicle-analytics-app -am package -Dmaven.test.skip=true")
                .doesNotContain("Spring Boot 3.4")
                .doesNotContain("Netty 4.1")
                .doesNotContain("HiveMQ MQTT Client")
                .doesNotContain("RabbitMQ")
                .doesNotContain("RocketMQ")
                .doesNotContain("其他 MQ 后端")
                .doesNotContain("Spotless")
                .doesNotContain("ArchUnit")
                .doesNotContain("mvn clean install -DskipTests")
                .doesNotContain("-DskipTests")
                .doesNotContain("Caffeine 内存降级")
                .doesNotContain("Redis/Memory SessionStore")
                .doesNotContain("event-file-store")
                .doesNotContain("DuckDB")
                .doesNotContain("Parquet")
                .doesNotContain("Parquet + DuckDB 文件型明细库")
                .doesNotContain("车辆状态/日统计消费")
                .doesNotContain("本服务不写业务库")
                .doesNotContain("里程统计全部由 Kafka 下游消费者实现")
                .doesNotContain("| 消息队列 |");
    }

    @Test
    void yutongMqttBuildSurfaceUsesOnlyPahoClient() throws Exception {
        Document pom = rootPom();

        assertThat(hasProjectProperty(pom, "paho-mqtt.version"))
                .isTrue();
        assertThat(hasDependency(pom, "org.eclipse.paho", "org.eclipse.paho.client.mqttv3"))
                .isTrue();
        assertThat(hasProjectProperty(pom, "fusesource-mqtt.version"))
                .isFalse();
        assertThat(hasDependency(pom, "org.fusesource.mqtt-client", "mqtt-client"))
                .isFalse();
    }

    @Test
    void activeAppBootPackagingConfigurationIsManagedOnceInRootPom() throws Exception {
        Document pom = rootPom();
        Element managedBootPlugin = managedPlugin(pom, "org.springframework.boot", "spring-boot-maven-plugin");

        assertThat(managedBootPlugin).isNotNull();
        assertThat(managedBootPlugin.getTextContent())
                .contains("${spring-boot.version}")
                .contains("--sun-misc-unsafe-memory-access=allow")
                .contains("repackage")
                .contains("build-info");

        for (String appPom : ACTIVE_APP_POMS) {
            Document app = modulePom(appPom);
            Element bootPlugin = buildPlugin(app, "org.springframework.boot", "spring-boot-maven-plugin");

            assertThat(bootPlugin)
                    .as(appPom)
                    .isNotNull();
            assertThat(firstDirectChild(bootPlugin, "configuration"))
                    .as(appPom + " should inherit shared JVM arguments from root pluginManagement")
                    .isNull();
            assertThat(firstDirectChild(bootPlugin, "executions"))
                    .as(appPom + " should inherit repackage/build-info executions from root pluginManagement")
                    .isNull();
        }
    }

    @Test
    void observabilityBuildSurfaceUsesMicrometerWithoutUnusedOpenTelemetryBom() throws Exception {
        Document pom = rootPom();
        Document observabilityPom = modulePom("modules/core/observability/pom.xml");
        String readme = Files.readString(repositoryRoot().resolve("README.md"));
        String moduleDataFlow = Files.readString(repositoryRoot().resolve("docs/module-data-flow.html"));
        String traceSpan = Files.readString(repositoryRoot()
                .resolve("modules/core/ingest-api/src/main/java/com/lingniu/ingest/api/annotation/TraceSpan.java"));

        assertThat(hasProjectProperty(pom, "micrometer.version"))
                .isTrue();
        assertThat(hasDependency(pom, "io.micrometer", "micrometer-bom"))
                .isTrue();
        assertThat(hasProjectProperty(pom, "otel.version"))
                .isFalse();
        assertThat(hasDependency(pom, "io.opentelemetry", "opentelemetry-bom"))
                .isFalse();
        assertThat(hasDependency(observabilityPom, "com.lingniu.ingest", "ingest-core"))
                .as("observability is an interceptor API module and should not depend on core implementation")
                .isFalse();
        assertThat(productionJavaContains("io.opentelemetry"))
                .isFalse();
        assertThat(readme)
                .contains("| 可观测 | Micrometer + Prometheus |")
                .doesNotContain("OpenTelemetry");
        assertThat(moduleDataFlow)
                .contains("observability<br/>metrics / health")
                .doesNotContain("observability<br/>metrics / tracing / health");
        assertThat(traceSpan)
                .doesNotContain("OpenTelemetry")
                .doesNotContain("span");
    }

    @Test
    void consumerAppsDoNotDependOnPipelineObservabilityInterceptor() throws Exception {
        for (String appPom : List.of(
                "modules/apps/vehicle-history-app/pom.xml",
                "modules/apps/vehicle-analytics-app/pom.xml")) {
            assertThat(hasDependency(modulePom(appPom), "com.lingniu.ingest", "observability"))
                    .as(appPom + " is a Kafka consumer/query runtime, not an ingest pipeline runtime")
                    .isFalse();
        }
    }

    @Test
    void springdocUiIsOnlyPackagedWithAppsThatExposeRestApis() throws Exception {
        for (String appPom : List.of(
                "modules/apps/gb32960-ingest-app/pom.xml",
                "modules/apps/jt808-ingest-app/pom.xml",
                "modules/apps/yutong-mqtt-app/pom.xml")) {
            assertThat(hasDependency(modulePom(appPom), "org.springdoc", "springdoc-openapi-starter-webmvc-ui"))
                    .as(appPom + " exposes Actuator only; Swagger UI belongs to query/API apps")
                    .isFalse();
        }
        for (String appPom : List.of(
                "modules/apps/vehicle-history-app/pom.xml",
                "modules/apps/vehicle-analytics-app/pom.xml")) {
            assertThat(hasDependency(modulePom(appPom), "org.springdoc", "springdoc-openapi-starter-webmvc-ui"))
                    .as(appPom + " exposes REST APIs")
                    .isTrue();
        }
    }

    @Test
    void buildSurfaceDoesNotManageLombokWhenProductionSourcesDoNotUseIt() throws Exception {
        Document pom = rootPom();

        assertThat(hasProjectProperty(pom, "lombok.version"))
                .isFalse();
        assertThat(hasDependency(pom, "org.projectlombok", "lombok"))
                .isFalse();
        assertThat(productionJavaContains("lombok."))
                .isFalse();
    }

    @Test
    void buildSurfaceDoesNotManageArchUnitWhenThereAreNoArchitectureTests() throws Exception {
        Document pom = rootPom();

        assertThat(hasProjectProperty(pom, "archunit.version"))
                .isFalse();
        assertThat(hasDependency(pom, "com.tngtech.archunit", "archunit-junit5"))
                .isFalse();
        assertThat(javaSourcesContain("import com.tngtech." + "archunit"))
                .isFalse();
    }

    @Test
    void buildSurfaceDoesNotManageTestcontainersWhenKafkaTestsUseInMemoryFakes() throws Exception {
        Document pom = rootPom();
        Document kafkaSinkPom = modulePom("modules/sinks/sink-kafka/pom.xml");

        assertThat(hasProjectProperty(pom, "testcontainers.version"))
                .isFalse();
        assertThat(hasDependency(pom, "org.testcontainers", "testcontainers-bom"))
                .isFalse();
        assertThat(hasDependency(kafkaSinkPom, "org.testcontainers", "kafka"))
                .isFalse();
        assertThat(javaSourcesContain("import org." + "testcontainers"))
                .isFalse();
    }

    @Test
    void productionDocsDoNotAdvertiseMemorySessionStore() throws Exception {
        String runbook = Files.readString(repositoryRoot()
                .resolve("docs/operations/gb32960-service-split-runbook.md"));
        String moduleDataFlow = Files.readString(repositoryRoot().resolve("docs/module-data-flow.html"));
        String redesignSpec = Files.readString(repositoryRoot()
                .resolve("docs/superpowers/specs/2026-06-29-vehicle-ingest-redesign.md"));

        assertThat(runbook)
                .contains("Session state requires Redis")
                .contains("REDIS_HOST=127.0.0.1")
                .contains("REDIS_DATABASE=50")
                .doesNotContain("SESSION_STORE=memory")
                .doesNotContain("without Redis");
        assertThat(moduleDataFlow)
                .contains("SessionStore 仅保留 Redis 后端")
                .doesNotContain("SessionStore 支持 <code>memory</code>")
                .doesNotContain("无 Redis 降级");
        assertThat(redesignSpec)
                .contains("Reads Redis latest state")
                .doesNotContain("Redis or in-memory")
                .doesNotContain("Redis/in-memory");
    }

    @Test
    void sessionCorePomDescriptionDoesNotAdvertiseRedisAsFallback() throws Exception {
        Document sessionCorePom = modulePom("modules/core/session-core/pom.xml");
        String description = firstDirectChild(sessionCorePom.getDocumentElement(), "description").getTextContent();

        assertThat(description)
                .contains("Redis SessionStore")
                .doesNotContain("兜底")
                .doesNotContain("fallback")
                .doesNotContain("memory");
    }

    @Test
    void rootPomDescriptionMatchesCurrentRuntimeStack() throws Exception {
        Document pom = rootPom();
        String description = firstDirectChild(pom.getDocumentElement(), "description").getTextContent();

        assertThat(description)
                .contains("只支持 Kafka")
                .contains("Java 25")
                .contains("Spring Boot " + projectProperty(pom, "spring-boot.version"))
                .contains("Netty " + projectProperty(pom, "netty.version"))
                .doesNotContain("Spring Boot 3.4");
    }

    @Test
    void defaultProductionSurfaceOnlySupportsKafkaAsMessageBackbone() throws Exception {
        List<Path> productionSurface = List.of(
                repositoryRoot().resolve("README.md"),
                repositoryRoot().resolve("pom.xml"),
                repositoryRoot().resolve("deploy/portainer/docker-compose.yml"),
                repositoryRoot().resolve("deploy/local/launchctl/README.md"),
                repositoryRoot().resolve("docs/operations/gb32960-service-split-runbook.md"),
                repositoryRoot().resolve("docs/operations/vehicle-ingest-tdengine-verification.md"),
                repositoryRoot().resolve("docs/operations/jt808-daily-mileage-runbook.md"),
                repositoryRoot().resolve("docs/target-architecture.md"));

        for (Path path : productionSurface) {
            String text = Files.readString(path);

            assertThat(text)
                    .as(path.toString())
                    .doesNotContain("sink-mq")
                    .doesNotContain("RabbitMQ")
                    .doesNotContain("RocketMQ")
                    .doesNotContain("Pulsar")
                    .doesNotContain("AMQP")
                    .doesNotContain("其他 MQ 后端");
        }
    }

    @Test
    void architectureDecisionsMatchCurrentDefaultProductionSurface() throws Exception {
        String decisions = Files.readString(repositoryRoot().resolve("DECISIONS.md"));

        assertThat(decisions)
                .contains("## ADR-004  信达 Push：Legacy only")
                .contains("## ADR-011  默认生产面：三协议接入 + 历史 + 统计")
                .contains("## ADR-012  JSATL12 附件上传：Optional only")
                .contains("GB32960、JT808、Yutong MQTT、vehicle-history-app、vehicle-analytics-app")
                .contains("Xinda Push 仅保留在 `legacy-xinda` profile")
                .contains("JSATL12 仅保留在 `optional-attachments` profile")
                .contains("vehicle-state-service 仅保留在 `optional-latest-state` profile")
                .contains("raw-archive-store 原型已删除")
                .contains("协议接入应用不直接写业务库")
                .contains("`vehicle-analytics-app` 写入 MySQL `vehicle_stat_metric`")
                .doesNotContain("event-file-store")
                .doesNotContain("optional-event-file-store")
                .doesNotContain("optional-raw-archive-store")
                .doesNotContain("## ADR-004  信达 Push：保留模块但彻底重写\n- **Status**: Accepted")
                .doesNotContain("Spring Boot 3.4.x");
    }

    @Test
    void productionDocsMatchCompactHistoryAndJt808OnlyAnalyticsSurface() throws Exception {
        String moduleDataFlow = Files.readString(repositoryRoot().resolve("docs/module-data-flow.html"));
        String tdengineVerification = Files.readString(repositoryRoot()
                .resolve("docs/operations/vehicle-ingest-tdengine-verification.md"));
        String runbook = Files.readString(repositoryRoot()
                .resolve("docs/operations/gb32960-service-split-runbook.md"));

        assertThat(moduleDataFlow)
                .contains("TDengine raw_frames + locations")
                .contains("TDengine <code>raw_frames</code> 和位置表")
                .doesNotContain("telemetry field 行映射")
                .doesNotContain("可选 telemetry field 表");
        assertThat(tdengineVerification)
                .contains("history 写入 raw、location 到 TDengine；不写 telemetry_fields")
                .doesNotContain("按需开启 telemetry field");
        assertThat(runbook)
                .contains("| `vehicle.event.gb32960.v1` | `gb32960-ingest-app` | `vehicle-history-app` |")
                .contains("| `vehicle.event.jt808.v1` | `jt808-ingest-app` | `vehicle-history-app`, `vehicle-analytics-app` |")
                .doesNotContain("| `vehicle.event.gb32960.v1` | `gb32960-ingest-app` | `vehicle-history-app`, `vehicle-analytics-app` |");
    }

    @Test
    void supersededDesignDocsPointToCurrentArchitecture() throws Exception {
        for (String path : List.of(
                "docs/superpowers/specs/2026-06-23-gb32960-production-readiness-design.md",
                "docs/superpowers/specs/2026-06-23-gb32960-service-split-design.md")) {
            String doc = Files.readString(repositoryRoot().resolve(path));

            assertThat(doc)
                    .as(path)
                    .startsWith("> **Superseded:**")
                    .contains("docs/target-architecture.md")
                    .contains("docs/superpowers/specs/2026-06-29-vehicle-ingest-redesign.md");
        }
    }

    @Test
    void duckDbAndParquetAreNotPartOfTheCurrentBuildSurface() throws Exception {
        Document pom = rootPom();

        assertThat(hasProjectProperty(pom, "duckdb.version"))
                .isFalse();
        assertThat(hasDependency(pom, "org.duckdb", "duckdb_jdbc"))
                .isFalse();
        assertThat(Files.exists(repositoryRoot().resolve("modules/sinks/event-file-store")))
                .isFalse();
    }

    @Test
    void legacyEventFileStoreContractsAreRemovedFromProductionSources() throws Exception {
        Path root = repositoryRoot();

        assertThat(Files.exists(root.resolve("modules/core/ingest-api/src/main/java/com/lingniu/ingest/api/history")))
                .isFalse();
        assertThat(Files.exists(root.resolve(
                "modules/services/event-history-service/src/main/java/com/lingniu/ingest/eventhistory/EventHistoryController.java")))
                .isFalse();
        assertThat(Files.exists(root.resolve(
                "modules/services/event-history-service/src/main/java/com/lingniu/ingest/eventhistory/TelemetryEnvelopeRecordMapper.java")))
                .isFalse();
        assertThat(productionJavaContains("EventFileStore"))
                .isFalse();
        assertThat(productionJavaContains("EventFileRecord"))
                .isFalse();
        assertThat(productionJavaContains("EventFileQuery"))
                .isFalse();
    }

    private static Document rootPom() throws Exception {
        return modulePom("pom.xml");
    }

    private static Document modulePom(String path) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        return factory.newDocumentBuilder().parse(Files.newInputStream(repositoryRoot().resolve(path)));
    }

    private static String projectProperty(Document pom, String name) {
        Element properties = firstDirectChild(pom.getDocumentElement(), "properties");
        Element property = firstDirectChild(properties, name);
        if (property == null) {
            throw new IllegalArgumentException("project property not found: " + name);
        }
        return property.getTextContent().trim();
    }

    private static boolean hasProjectProperty(Document pom, String name) {
        Element properties = firstDirectChild(pom.getDocumentElement(), "properties");
        return properties != null && firstDirectChild(properties, name) != null;
    }

    private static List<String> defaultModules(Document pom) {
        Element project = pom.getDocumentElement();
        return directModules(firstDirectChild(project, "modules"));
    }

    private static List<String> profileModules(Document pom, String profileId) {
        Element profile = profile(pom, profileId);
        if (profile == null) {
            return List.of();
        }
        return directModules(firstDirectChild(profile, "modules"));
    }

    private static List<String> rootDependencyManagementArtifacts(Document pom) {
        return dependencyManagementArtifacts(pom.getDocumentElement());
    }

    private static List<String> profileDependencyManagementArtifacts(Document pom, String profileId) {
        Element profile = profile(pom, profileId);
        if (profile == null) {
            return List.of();
        }
        return dependencyManagementArtifacts(profile);
    }

    private static List<String> profileRepositoryIds(Document pom, String profileId) {
        Element profile = profile(pom, profileId);
        if (profile == null) {
            return List.of();
        }
        return repositoryIds(profile);
    }

    private static List<String> repositoryIds(Element owner) {
        Element repositories = firstDirectChild(owner, "repositories");
        if (repositories == null) {
            return List.of();
        }
        List<String> ids = new ArrayList<>();
        NodeList children = repositories.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (!(children.item(i) instanceof Element repository) || !"repository".equals(repository.getTagName())) {
                continue;
            }
            Element id = firstDirectChild(repository, "id");
            if (id != null) {
                ids.add(id.getTextContent().trim());
            }
        }
        return List.copyOf(ids);
    }

    private static Element profile(Document pom, String profileId) {
        Element profiles = firstDirectChild(pom.getDocumentElement(), "profiles");
        if (profiles == null) {
            return null;
        }
        NodeList children = profiles.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (!(children.item(i) instanceof Element profile) || !"profile".equals(profile.getTagName())) {
                continue;
            }
            Element id = firstDirectChild(profile, "id");
            if (id != null && profileId.equals(id.getTextContent().trim())) {
                return profile;
            }
        }
        return null;
    }

    private static List<String> directModules(Element modules) {
        if (modules == null) {
            return List.of();
        }
        List<String> out = new ArrayList<>();
        NodeList children = modules.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (children.item(i) instanceof Element child && "module".equals(child.getTagName())) {
                out.add(child.getTextContent().trim());
            }
        }
        return List.copyOf(out);
    }

    private static List<String> dependencyManagementArtifacts(Element owner) {
        Element dependencyManagement = firstDirectChild(owner, "dependencyManagement");
        if (dependencyManagement == null) {
            return List.of();
        }
        Element dependencies = firstDirectChild(dependencyManagement, "dependencies");
        if (dependencies == null) {
            return List.of();
        }
        List<String> artifacts = new ArrayList<>();
        NodeList children = dependencies.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (!(children.item(i) instanceof Element dependency) || !"dependency".equals(dependency.getTagName())) {
                continue;
            }
            Element groupId = firstDirectChild(dependency, "groupId");
            Element artifactId = firstDirectChild(dependency, "artifactId");
            if (groupId != null
                    && artifactId != null
                    && "com.lingniu.ingest".equals(groupId.getTextContent().trim())) {
                artifacts.add(artifactId.getTextContent().trim());
            }
        }
        return List.copyOf(artifacts);
    }

    private static Element managedPlugin(Document pom, String groupId, String artifactId) {
        Element build = firstDirectChild(pom.getDocumentElement(), "build");
        Element pluginManagement = build == null ? null : firstDirectChild(build, "pluginManagement");
        return plugin(pluginManagement, groupId, artifactId);
    }

    private static Element buildPlugin(Document pom, String groupId, String artifactId) {
        Element build = firstDirectChild(pom.getDocumentElement(), "build");
        return plugin(build, groupId, artifactId);
    }

    private static Element plugin(Element owner, String groupId, String artifactId) {
        Element plugins = owner == null ? null : firstDirectChild(owner, "plugins");
        if (plugins == null) {
            return null;
        }
        NodeList children = plugins.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (!(children.item(i) instanceof Element plugin) || !"plugin".equals(plugin.getTagName())) {
                continue;
            }
            Element currentGroupId = firstDirectChild(plugin, "groupId");
            Element currentArtifactId = firstDirectChild(plugin, "artifactId");
            if (currentGroupId != null
                    && currentArtifactId != null
                    && groupId.equals(currentGroupId.getTextContent().trim())
                    && artifactId.equals(currentArtifactId.getTextContent().trim())) {
                return plugin;
            }
        }
        return null;
    }

    private static boolean hasDependency(Document pom, String groupId, String artifactId) {
        Element owner = pom.getDocumentElement();
        Element dependencyManagement = firstDirectChild(owner, "dependencyManagement");
        Element dependencies = firstDirectChild(owner, "dependencies");
        if (hasDependencyIn(dependencies, groupId, artifactId)) {
            return true;
        }
        if (dependencyManagement == null) {
            return false;
        }
        return hasDependencyIn(firstDirectChild(dependencyManagement, "dependencies"), groupId, artifactId);
    }

    private static boolean hasDependencyIn(Element dependencies, String groupId, String artifactId) {
        if (dependencies == null) {
            return false;
        }
        NodeList children = dependencies.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (!(children.item(i) instanceof Element dependency) || !"dependency".equals(dependency.getTagName())) {
                continue;
            }
            Element currentGroupId = firstDirectChild(dependency, "groupId");
            Element currentArtifactId = firstDirectChild(dependency, "artifactId");
            if (currentGroupId != null
                    && currentArtifactId != null
                    && groupId.equals(currentGroupId.getTextContent().trim())
                    && artifactId.equals(currentArtifactId.getTextContent().trim())) {
                return true;
            }
        }
        return false;
    }

    private static boolean productionJavaContains(String token) throws Exception {
        return javaSourcesContain(token, false);
    }

    private static boolean javaSourcesContain(String token) throws Exception {
        return javaSourcesContain(token, true);
    }

    private static boolean javaSourcesContain(String token, boolean includeTests) throws Exception {
        try (Stream<Path> files = Files.walk(repositoryRoot().resolve("modules"))) {
            return files
                    .filter(Files::isRegularFile)
                    .filter(path -> path.toString().endsWith(".java"))
                    .filter(path -> includeTests || !path.toString().contains("/src/test/"))
                    .anyMatch(path -> {
                        try {
                            return Files.readString(path).contains(token);
                        } catch (Exception e) {
                            throw new IllegalStateException("failed to read " + path, e);
                        }
                    });
        }
    }

    private static Element firstDirectChild(Element parent, String tagName) {
        NodeList children = parent.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            if (children.item(i) instanceof Element child && tagName.equals(child.getTagName())) {
                return child;
            }
        }
        return null;
    }

    private static Path repositoryRoot() {
        Path current = Path.of(System.getProperty("user.dir")).toAbsolutePath();
        while (current != null) {
            if (Files.exists(current.resolve("pom.xml")) && Files.exists(current.resolve("modules"))) {
                return current;
            }
            current = current.getParent();
        }
        throw new IllegalStateException("repository root not found");
    }
}
