/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.maven.cling.invoker.mvnup.goals;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;

import eu.maveniverse.domtrip.Document;
import eu.maveniverse.domtrip.Element;
import org.apache.maven.api.cli.mvnup.UpgradeOptions;
import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * Unit tests for the {@link CompatibilityFixStrategy} class.
 * Tests Maven 4 compatibility fixes including duplicate dependency and plugin handling.
 */
@DisplayName("CompatibilityFixStrategy")
class CompatibilityFixStrategyTest {

    private CompatibilityFixStrategy strategy;

    @BeforeEach
    void setUp() {
        strategy = new CompatibilityFixStrategy();
    }

    private UpgradeContext createMockContext() {
        return TestUtils.createMockContext();
    }

    private UpgradeContext createMockContext(Path workingDirectory) {
        return TestUtils.createMockContext(workingDirectory);
    }

    private UpgradeContext createMockContext(UpgradeOptions options) {
        return TestUtils.createMockContext(options);
    }

    private UpgradeOptions createDefaultOptions() {
        return TestUtils.createDefaultOptions();
    }

    @Nested
    @DisplayName("Applicability")
    class ApplicabilityTests {

        @Test
        @DisplayName("should be applicable when --model option is true")
        void shouldBeApplicableWhenModelOptionTrue() {
            UpgradeOptions options = mock(UpgradeOptions.class);
            when(options.model()).thenReturn(Optional.of(true));
            when(options.all()).thenReturn(Optional.empty());

            UpgradeContext context = createMockContext(options);

            assertTrue(strategy.isApplicable(context), "Strategy should be applicable when --model is true");
        }

        @Test
        @DisplayName("should be applicable when --all option is specified")
        void shouldBeApplicableWhenAllOptionSpecified() {
            UpgradeOptions options = mock(UpgradeOptions.class);
            when(options.all()).thenReturn(Optional.of(true));
            when(options.model()).thenReturn(Optional.empty());

            UpgradeContext context = createMockContext(options);

            assertTrue(strategy.isApplicable(context), "Strategy should be applicable when --all is specified");
        }

        @Test
        @DisplayName("should be applicable by default when no specific options provided")
        void shouldBeApplicableByDefaultWhenNoSpecificOptions() {
            UpgradeOptions options = createDefaultOptions();

            UpgradeContext context = createMockContext(options);

            assertTrue(strategy.isApplicable(context), "Strategy should be applicable by default");
        }

        @Test
        @DisplayName("should not be applicable when --model option is false")
        void shouldNotBeApplicableWhenModelOptionFalse() {
            UpgradeOptions options = mock(UpgradeOptions.class);
            when(options.model()).thenReturn(Optional.of(false));
            when(options.all()).thenReturn(Optional.empty());

            UpgradeContext context = createMockContext(options);

            assertFalse(strategy.isApplicable(context), "Strategy should not be applicable when --model is false");
        }

        @Test
        @DisplayName("should handle all options disabled")
        void shouldHandleAllOptionsDisabled() {
            UpgradeContext context = TestUtils.createMockContext(TestUtils.createOptions(
                    false, // --all
                    false, // --infer
                    false, // --fix-model
                    false, // --plugins
                    null // --model
                    ));

            // Should apply default behavior when all options are explicitly disabled
            assertTrue(
                    strategy.isApplicable(context),
                    "Strategy should apply default behavior when all options are disabled");
        }
    }

    @Nested
    @DisplayName("Unsupported combine.children Attribute Fixes")
    class UnsupportedCombineChildrenFixesTests {

        @Test
        @DisplayName("should remove combine.children='override' attribute")
        void shouldRemoveCombineChildrenOverride() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs combine.children="override">
                                        <arg>-Xlint:all</arg>
                                    </compilerArgs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.children attribute");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.children"), "combine.children attribute should be removed entirely");
            assertTrue(xml.contains("compilerArgs"), "Element should still exist");
        }

        @Test
        @DisplayName("should remove any invalid combine.children value")
        void shouldRemoveAnyCombineChildrenInvalidValue() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs combine.children="invalid_value">
                                        <arg>-Xlint:all</arg>
                                    </compilerArgs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.children attribute");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.children"), "combine.children attribute should be removed entirely");
        }

        @Test
        @DisplayName("should not remove valid combine.children values")
        void shouldNotRemoveValidCombineChildrenValues() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs combine.children="append">
                                        <arg>-Xlint:all</arg>
                                    </compilerArgs>
                                    <includes combine.children="merge">
                                        <include>**/*.java</include>
                                    </includes>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POMs");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("combine.children=\"append\""), "append should be preserved");
            assertTrue(xml.contains("combine.children=\"merge\""), "merge should be preserved");
        }

        @Test
        @DisplayName("should remove invalid combine.children in profile plugin configuration")
        void shouldRemoveInvalidCombineChildrenInProfile() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <profiles>
                        <profile>
                            <id>release</id>
                            <build>
                                <plugins>
                                    <plugin>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-assembly-plugin</artifactId>
                                        <configuration>
                                            <descriptorRefs combine.children="override">
                                                <descriptorRef>src</descriptorRef>
                                            </descriptorRefs>
                                        </configuration>
                                    </plugin>
                                </plugins>
                            </build>
                        </profile>
                    </profiles>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.children in profile");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.children"), "combine.children attribute should be removed from profile");
        }
    }

    @Nested
    @DisplayName("Unsupported combine.self Attribute Fixes")
    class UnsupportedCombineSelfFixesTests {

        @Test
        @DisplayName("should remove combine.self='append' attribute")
        void shouldRemoveCombineSelfAppend() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-assembly-plugin</artifactId>
                                <configuration>
                                    <descriptorRefs combine.self="append">
                                        <descriptorRef>jar-with-dependencies</descriptorRef>
                                    </descriptorRefs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.self attribute");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.self"), "combine.self attribute should be removed entirely");
            assertTrue(xml.contains("descriptorRefs"), "Element should still exist");
        }

        @Test
        @DisplayName("should remove any invalid combine.self value")
        void shouldRemoveAnyCombineSelfInvalidValue() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs combine.self="invalid_value">
                                        <arg>-Xlint:all</arg>
                                    </compilerArgs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.self attribute");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.self"), "combine.self attribute should be removed entirely");
        }

        @Test
        @DisplayName("should not remove valid combine.self values")
        void shouldNotRemoveValidCombineSelfValues() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <source combine.self="override">11</source>
                                    <target combine.self="merge">11</target>
                                    <compilerArgs combine.self="remove"/>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POMs");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("combine.self=\"override\""), "override should be preserved");
            assertTrue(xml.contains("combine.self=\"merge\""), "merge should be preserved");
            assertTrue(xml.contains("combine.self=\"remove\""), "remove should be preserved");
        }

        @Test
        @DisplayName("should remove invalid combine.self in profile plugin configuration")
        void shouldRemoveInvalidCombineSelfInProfile() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <profiles>
                        <profile>
                            <id>release</id>
                            <build>
                                <plugins>
                                    <plugin>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-assembly-plugin</artifactId>
                                        <configuration>
                                            <descriptorRefs combine.self="append">
                                                <descriptorRef>src</descriptorRef>
                                            </descriptorRefs>
                                        </configuration>
                                    </plugin>
                                </plugins>
                            </build>
                        </profile>
                    </profiles>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.self in profile");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.self"), "combine.self attribute should be removed from profile");
        }

        @Test
        @DisplayName("should remove all occurrences of invalid combine.self across the POM")
        void shouldRemoveAllOccurrencesOfInvalidCombineSelf() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <pluginManagement>
                            <plugins>
                                <plugin>
                                    <groupId>org.apache.maven.plugins</groupId>
                                    <artifactId>maven-compiler-plugin</artifactId>
                                    <configuration>
                                        <compilerArgs combine.self="append">
                                            <arg>-Xlint:all</arg>
                                        </compilerArgs>
                                    </configuration>
                                </plugin>
                            </plugins>
                        </pluginManagement>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-assembly-plugin</artifactId>
                                <configuration>
                                    <descriptorRefs combine.self="append">
                                        <descriptorRef>jar-with-dependencies</descriptorRef>
                                    </descriptorRefs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                    <profiles>
                        <profile>
                            <id>release</id>
                            <build>
                                <plugins>
                                    <plugin>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-assembly-plugin</artifactId>
                                        <configuration>
                                            <descriptorRefs combine.self="append">
                                                <descriptorRef>src</descriptorRef>
                                            </descriptorRefs>
                                        </configuration>
                                    </plugin>
                                </plugins>
                            </build>
                        </profile>
                    </profiles>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed combine.self attributes");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("combine.self"), "All combine.self attributes should be removed");
        }
    }

    @Nested
    @DisplayName("Repository Expression Fixes")
    class RepositoryExpressionFixesTests {

        @Test
        @DisplayName("should replace ${basedir} with ${project.basedir} in repository URLs")
        void shouldReplaceBasedirInRepositoryUrls() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>local-repo</id>
                            <url>file://${basedir}/internal-repository</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed basedir expression");

            Element root = document.root();
            Element repositories = DomUtils.findChildElement(root, "repositories");
            Element repository = DomUtils.findChildElement(repositories, "repository");
            Element url = DomUtils.findChildElement(repository, "url");
            assertEquals(
                    "file://${project.basedir}/internal-repository",
                    url.textContent().trim(),
                    "Should have replaced ${basedir} with ${project.basedir}");
        }

        @Test
        @DisplayName("should replace ${pom.basedir} with ${project.basedir} in repository URLs")
        void shouldReplacePomBasedirInRepositoryUrls() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>local-repo</id>
                            <url>file://${pom.basedir}/lib</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed pom.basedir expression");

            Element root = document.root();
            Element repositories = DomUtils.findChildElement(root, "repositories");
            Element repository = DomUtils.findChildElement(repositories, "repository");
            Element url = DomUtils.findChildElement(repository, "url");
            assertEquals(
                    "file://${project.basedir}/lib",
                    url.textContent().trim(),
                    "Should have replaced ${pom.basedir} with ${project.basedir}");
        }

        @Test
        @DisplayName("should replace ${basedir} in pluginRepository URLs")
        void shouldReplaceBasedirInPluginRepositoryUrls() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <pluginRepositories>
                        <pluginRepository>
                            <id>local-plugins</id>
                            <url>file://${basedir}/plugin-repo</url>
                        </pluginRepository>
                    </pluginRepositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in pluginRepository");

            Element root = document.root();
            Element pluginRepositories = DomUtils.findChildElement(root, "pluginRepositories");
            Element pluginRepository = DomUtils.findChildElement(pluginRepositories, "pluginRepository");
            Element url = DomUtils.findChildElement(pluginRepository, "url");
            assertEquals(
                    "file://${project.basedir}/plugin-repo",
                    url.textContent().trim(),
                    "Should have replaced ${basedir} with ${project.basedir}");
        }

        @Test
        @DisplayName("should replace ${basedir} in profile repository URLs")
        void shouldReplaceBasedirInProfileRepositoryUrls() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <profiles>
                        <profile>
                            <id>local</id>
                            <repositories>
                                <repository>
                                    <id>local-repo</id>
                                    <url>file://${basedir}/repo</url>
                                </repository>
                            </repositories>
                        </profile>
                    </profiles>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in profile repository");

            Element root = document.root();
            Element profiles = DomUtils.findChildElement(root, "profiles");
            Element profile = DomUtils.findChildElement(profiles, "profile");
            Element repositories = DomUtils.findChildElement(profile, "repositories");
            Element repository = DomUtils.findChildElement(repositories, "repository");
            Element url = DomUtils.findChildElement(repository, "url");
            assertEquals(
                    "file://${project.basedir}/repo",
                    url.textContent().trim(),
                    "Should have replaced ${basedir} with ${project.basedir}");
        }

        @Test
        @DisplayName("should not modify repository URLs without deprecated expressions")
        void shouldNotModifyUrlsWithoutDeprecatedExpressions() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>central</id>
                            <url>https://repo.maven.apache.org/maven2</url>
                        </repository>
                        <repository>
                            <id>local-repo</id>
                            <url>file://${project.basedir}/repo</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POMs");
        }
    }

    @Nested
    @DisplayName("Undefined Property Expression Fixes")
    class UndefinedPropertyExpressionFixesTests {

        @Test
        @DisplayName("should comment out dependency with undefined property expression")
        void shouldCommentOutDependencyWithUndefinedProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                        <dependencies>
                            <dependency>
                                <groupId>com.google.guava</groupId>
                                <artifactId>guava</artifactId>
                                <version>${guava-version}</version>
                            </dependency>
                        </dependencies>
                    </dependencyManagement>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have commented out dependency");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
            assertTrue(xml.contains("guava-version"), "Should mention the undefined property");

            Element root = document.root();
            Element depMgmt = DomUtils.findChildElement(root, "dependencyManagement");
            Element deps = DomUtils.findChildElement(depMgmt, "dependencies");
            assertEquals(0, deps.childElements("dependency").count(), "Should have no dependency elements");
        }

        @Test
        @DisplayName("should not comment out dependency with defined property")
        void shouldNotCommentOutDependencyWithDefinedProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <properties>
                        <guava-version>30.0-jre</guava-version>
                    </properties>
                    <dependencyManagement>
                        <dependencies>
                            <dependency>
                                <groupId>com.google.guava</groupId>
                                <artifactId>guava</artifactId>
                                <version>${guava-version}</version>
                            </dependency>
                        </dependencies>
                    </dependencyManagement>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            strategy.doApply(context, pomMap);

            Element root = document.root();
            Element depMgmt = DomUtils.findChildElement(root, "dependencyManagement");
            Element deps = DomUtils.findChildElement(depMgmt, "dependencies");
            assertEquals(1, deps.childElements("dependency").count(), "Dependency should still be present");
        }

        @Test
        @DisplayName("should not comment out dependency with well-known built-in property")
        void shouldNotCommentOutDependencyWithBuiltinProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <dependencies>
                        <dependency>
                            <groupId>test</groupId>
                            <artifactId>test-dep</artifactId>
                            <version>${project.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            strategy.doApply(context, pomMap);

            Element root = document.root();
            Element deps = DomUtils.findChildElement(root, "dependencies");
            assertEquals(
                    1,
                    deps.childElements("dependency").count(),
                    "Dependency with built-in property should still be present");
        }

        @Test
        @DisplayName("should recognize property defined in another module POM")
        void shouldRecognizePropertyFromOtherPom() throws Exception {
            String parentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>parent</artifactId>
                    <version>1.0.0</version>
                    <properties>
                        <guava-version>30.0-jre</guava-version>
                    </properties>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>test</groupId>
                        <artifactId>parent</artifactId>
                        <version>1.0.0</version>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${guava-version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document parentDoc = Document.of(parentPom);
            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(
                    Paths.get("pom.xml"), parentDoc,
                    Paths.get("child/pom.xml"), childDoc);

            UpgradeContext context = createMockContext();
            strategy.doApply(context, pomMap);

            Element root = childDoc.root();
            Element deps = DomUtils.findChildElement(root, "dependencies");
            assertEquals(
                    1,
                    deps.childElements("dependency").count(),
                    "Dependency should not be commented out when property is defined in another POM");
        }
    }

    @Nested
    @DisplayName("Undefined Property Expression Fixes with Effective Model")
    class UndefinedPropertyEffectiveModelTests {

        @TempDir
        Path tempDir;

        @Test
        @DisplayName("should recognize property inherited from external parent via relativePath")
        void shouldRecognizePropertyFromExternalParent() throws Exception {
            String parentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>external-parent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                    <properties>
                        <guava.version>32.1.3-jre</guava.version>
                    </properties>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>external-parent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../pom.xml</relativePath>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${guava.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Path parentPomPath = tempDir.resolve("pom.xml");
            Path childDir = Files.createDirectories(tempDir.resolve("child"));
            Path childPomPath = childDir.resolve("pom.xml");

            Files.writeString(parentPomPath, parentPom);
            Files.writeString(childPomPath, childPom);

            Document parentDoc = Document.of(parentPom);
            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(
                    parentPomPath, parentDoc,
                    childPomPath, childDoc);

            UpgradeContext context = createMockContext(tempDir);
            strategy.doApply(context, pomMap);

            Element root = childDoc.root();
            Element deps = DomUtils.findChildElement(root, "dependencies");
            assertEquals(
                    1,
                    deps.childElements("dependency").count(),
                    "Dependency should not be commented out when property is inherited from external parent");
        }

        @Test
        @DisplayName("should comment out dependency when property is not in parent either")
        void shouldCommentOutWhenPropertyNotInParent() throws Exception {
            String parentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>external-parent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>external-parent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../pom.xml</relativePath>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${undefined.prop}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Path parentPomPath = tempDir.resolve("pom.xml");
            Path childDir = Files.createDirectories(tempDir.resolve("child"));
            Path childPomPath = childDir.resolve("pom.xml");

            Files.writeString(parentPomPath, parentPom);
            Files.writeString(childPomPath, childPom);

            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(childPomPath, childDoc);

            UpgradeContext context = createMockContext(tempDir);
            strategy.doApply(context, pomMap);

            String xml = DomUtils.toXml(childDoc);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
        }

        @Test
        @DisplayName("should handle partial undefined - only comment out dependency with undefined property")
        void shouldHandlePartialUndefined() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <properties>
                        <commons.version>3.14.0</commons.version>
                    </properties>
                    <dependencies>
                        <dependency>
                            <groupId>org.apache.commons</groupId>
                            <artifactId>commons-lang3</artifactId>
                            <version>${commons.version}</version>
                        </dependency>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${undefined.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Path pomPath = tempDir.resolve("pom.xml");
            Files.writeString(pomPath, pomXml);

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(pomPath, document);

            UpgradeContext context = createMockContext(tempDir);
            strategy.doApply(context, pomMap);

            Element root = document.root();
            Element deps = DomUtils.findChildElement(root, "dependencies");
            assertEquals(1, deps.childElements("dependency").count(), "Only defined-property dependency should remain");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
            assertTrue(xml.contains("'undefined.version'"), "Should mention the undefined property in comment");
            assertFalse(xml.contains("'commons.version'"), "Should not flag the defined property as undefined");
        }

        @Test
        @DisplayName("should recognize property from grandparent POM")
        void shouldRecognizePropertyFromGrandparent() throws Exception {
            String grandparentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>grandparent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                    <properties>
                        <guava.version>32.1.3-jre</guava.version>
                    </properties>
                </project>
                """;

            String parentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>grandparent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../pom.xml</relativePath>
                    </parent>
                    <artifactId>parent</artifactId>
                    <packaging>pom</packaging>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>parent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../parent/pom.xml</relativePath>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${guava.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Path grandparentPath = tempDir.resolve("pom.xml");
            Path parentDir = Files.createDirectories(tempDir.resolve("parent"));
            Path parentPath = parentDir.resolve("pom.xml");
            Path childDir = Files.createDirectories(tempDir.resolve("child"));
            Path childPomPath = childDir.resolve("pom.xml");

            Files.writeString(grandparentPath, grandparentPom);
            Files.writeString(parentPath, parentPom);
            Files.writeString(childPomPath, childPom);

            Document grandparentDoc = Document.of(grandparentPom);
            Document parentDoc = Document.of(parentPom);
            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(
                    grandparentPath, grandparentDoc,
                    parentPath, parentDoc,
                    childPomPath, childDoc);

            UpgradeContext context = createMockContext(tempDir);
            strategy.doApply(context, pomMap);

            Element root = childDoc.root();
            Element deps = DomUtils.findChildElement(root, "dependencies");
            assertEquals(
                    1,
                    deps.childElements("dependency").count(),
                    "Dependency should not be commented out when property is inherited from grandparent");
        }

        @Test
        @DisplayName("should not comment out when property is defined in external parent not in reactor")
        void shouldNotCommentOutWhenPropertyFromExternalParentNotInReactor() throws Exception {
            String externalParentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>external-parent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                    <properties>
                        <oak.version>1.62.0</oak.version>
                    </properties>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>external-parent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../external/pom.xml</relativePath>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencyManagement>
                        <dependencies>
                            <dependency>
                                <groupId>org.apache.jackrabbit</groupId>
                                <artifactId>oak-core</artifactId>
                                <version>${oak.version}</version>
                            </dependency>
                        </dependencies>
                    </dependencyManagement>
                </project>
                """;

            Path externalDir = Files.createDirectories(tempDir.resolve("external"));
            Path externalParentPath = externalDir.resolve("pom.xml");
            Path childDir = Files.createDirectories(tempDir.resolve("child"));
            Path childPomPath = childDir.resolve("pom.xml");

            Files.writeString(externalParentPath, externalParentPom);
            Files.writeString(childPomPath, childPom);

            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(childPomPath, childDoc);

            UpgradeContext context = createMockContext(childDir);
            strategy.doApply(context, pomMap);

            Element root = childDoc.root();
            Element depMgmt = DomUtils.findChildElement(root, "dependencyManagement");
            Element deps = DomUtils.findChildElement(depMgmt, "dependencies");
            assertEquals(
                    1,
                    deps.childElements("dependency").count(),
                    "Managed dependency should not be commented out when property is inherited from external parent");
        }

        @Test
        @DisplayName("should comment out when property is truly undefined even in effective model")
        void shouldCommentOutWhenPropertyTrulyUndefinedInEffectiveModel() throws Exception {
            String parentPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>parent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                </project>
                """;

            String childPom = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>parent</artifactId>
                        <version>1.0.0</version>
                        <relativePath>../pom.xml</relativePath>
                    </parent>
                    <artifactId>child</artifactId>
                    <dependencyManagement>
                        <dependencies>
                            <dependency>
                                <groupId>com.google.guava</groupId>
                                <artifactId>guava</artifactId>
                                <version>${truly.undefined.prop}</version>
                            </dependency>
                        </dependencies>
                    </dependencyManagement>
                </project>
                """;

            Path parentPath = tempDir.resolve("pom.xml");
            Path childDir = Files.createDirectories(tempDir.resolve("child"));
            Path childPomPath = childDir.resolve("pom.xml");

            Files.writeString(parentPath, parentPom);
            Files.writeString(childPomPath, childPom);

            Document childDoc = Document.of(childPom);
            Map<Path, Document> pomMap = Map.of(childPomPath, childDoc);

            UpgradeContext context = createMockContext(childDir);
            strategy.doApply(context, pomMap);

            String xml = DomUtils.toXml(childDoc);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
            assertTrue(xml.contains("truly.undefined.prop"), "Should mention the undefined property");
        }
    }

    @Nested
    @DisplayName("Undefined Property Expression in Repositories Fixes")
    class UndefinedPropertyExpressionInRepositoriesTests {

        @Test
        @DisplayName("should comment out repository with undefined property in id")
        void shouldCommentOutRepositoryWithUndefinedPropertyInId() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>${eclipseP2RepoId}</id>
                            <url>https://repo.example.com</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have commented out repository");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
            assertTrue(xml.contains("eclipseP2RepoId"), "Should mention the undefined property");

            Element root = document.root();
            Element repos = DomUtils.findChildElement(root, "repositories");
            assertEquals(0, repos.childElements("repository").count(), "Should have no repository elements");
        }

        @Test
        @DisplayName("should comment out repository with undefined property in url")
        void shouldCommentOutRepositoryWithUndefinedPropertyInUrl() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>my-repo</id>
                            <url>${undefinedBaseUrl}/releases</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have commented out repository");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");
            assertTrue(xml.contains("undefinedBaseUrl"), "Should mention the undefined property");
        }

        @Test
        @DisplayName("should not comment out repository with defined property")
        void shouldNotCommentOutRepositoryWithDefinedProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <properties>
                        <repoId>my-custom-repo</repoId>
                    </properties>
                    <repositories>
                        <repository>
                            <id>${repoId}</id>
                            <url>https://repo.example.com</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            strategy.doApply(context, pomMap);

            Element root = document.root();
            Element repos = DomUtils.findChildElement(root, "repositories");
            assertEquals(1, repos.childElements("repository").count(), "Repository should still be present");
        }

        @Test
        @DisplayName("should comment out plugin repository with undefined property")
        void shouldCommentOutPluginRepositoryWithUndefinedProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <pluginRepositories>
                        <pluginRepository>
                            <id>${eclipseP2RepoId}</id>
                            <url>https://plugins.example.com</url>
                        </pluginRepository>
                    </pluginRepositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have commented out plugin repository");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("mvnup: commented out"), "Should contain comment-out marker");

            Element root = document.root();
            Element repos = DomUtils.findChildElement(root, "pluginRepositories");
            assertEquals(
                    0, repos.childElements("pluginRepository").count(), "Should have no pluginRepository elements");
        }

        @Test
        @DisplayName("should not comment out repository with well-known property")
        void shouldNotCommentOutRepositoryWithWellKnownProperty() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <repositories>
                        <repository>
                            <id>local-repo</id>
                            <url>file://${project.basedir}/repo</url>
                        </repository>
                    </repositories>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            strategy.doApply(context, pomMap);

            Element root = document.root();
            Element repos = DomUtils.findChildElement(root, "repositories");
            assertEquals(1, repos.childElements("repository").count(), "Repository should still be present");
        }
    }

    @Nested
    @DisplayName("Deprecated Property Expression Fixes")
    class DeprecatedPropertyExpressionFixesTests {

        @Test
        @DisplayName("should replace ${version} with ${project.version} in dependency version")
        void shouldReplaceVersionExpression() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                        <dependencies>
                            <dependency>
                                <groupId>test</groupId>
                                <artifactId>test-dep</artifactId>
                                <version>${version}</version>
                            </dependency>
                        </dependencies>
                    </dependencyManagement>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-jar-plugin</artifactId>
                                <version>${version}</version>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed deprecated ${version}");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("${version}"), "Should not contain deprecated ${version}");
            assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
        }

        @Test
        @DisplayName("should replace ${groupId} and ${artifactId} expressions")
        void shouldReplaceGroupIdAndArtifactIdExpressions() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>my-project</artifactId>
                    <version>1.0.0</version>
                    <dependencies>
                        <dependency>
                            <groupId>${groupId}</groupId>
                            <artifactId>my-lib</artifactId>
                            <version>1.0.0</version>
                        </dependency>
                        <dependency>
                            <groupId>com.example</groupId>
                            <artifactId>${artifactId}-core</artifactId>
                            <version>1.0.0</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed deprecated expressions");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("${groupId}"), "Should not contain deprecated ${groupId}");
            assertFalse(xml.contains("${artifactId}"), "Should not contain deprecated ${artifactId}");
            assertTrue(xml.contains("${project.groupId}"), "Should contain ${project.groupId}");
            assertTrue(xml.contains("${project.artifactId}"), "Should contain ${project.artifactId}");
        }

        @Test
        @DisplayName("should replace all three deprecated expressions in one POM")
        void shouldReplaceAllThreeDeprecatedExpressions() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>my-project</artifactId>
                    <version>2.0.0</version>
                    <properties>
                        <selfCoords>${groupId}:${artifactId}:${version}</selfCoords>
                    </properties>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed deprecated expressions");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("${version}"), "Should not contain ${version}");
            assertFalse(xml.contains("${groupId}"), "Should not contain ${groupId}");
            assertFalse(xml.contains("${artifactId}"), "Should not contain ${artifactId}");
            assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
            assertTrue(xml.contains("${project.groupId}"), "Should contain ${project.groupId}");
            assertTrue(xml.contains("${project.artifactId}"), "Should contain ${project.artifactId}");
        }

        @Test
        @DisplayName("should not modify POMs without deprecated expressions")
        void shouldNotModifyPomsWithoutDeprecatedExpressions() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>my-project</artifactId>
                    <version>1.0.0</version>
                    <dependencies>
                        <dependency>
                            <groupId>com.example</groupId>
                            <artifactId>my-lib</artifactId>
                            <version>${project.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified POM without deprecated expressions");
        }

        @Test
        @DisplayName("should replace deprecated expressions in plugin configuration")
        void shouldReplaceDeprecatedExpressionsInPluginConfiguration() throws Exception {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.example</groupId>
                    <artifactId>my-project</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-jar-plugin</artifactId>
                                <configuration>
                                    <archive>
                                        <manifestEntries>
                                            <Implementation-Version>${version}</Implementation-Version>
                                        </manifestEntries>
                                    </archive>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Compatibility fix should succeed");
            assertTrue(result.modifiedCount() > 0, "Should have fixed deprecated ${version} in plugin config");

            String xml = DomUtils.toXml(document);
            assertFalse(xml.contains("${version}"), "Should not contain deprecated ${version}");
            assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
        }
    }

    @Nested
    @DisplayName("Strategy Description")
    class StrategyDescriptionTests {

        @Test
        @DisplayName("should provide meaningful description")
        void shouldProvideMeaningfulDescription() {
            String description = strategy.getDescription();

            assertNotNull(description, "Description should not be null");
            assertFalse(description.trim().isEmpty(), "Description should not be empty");
            assertTrue(
                    description.toLowerCase().contains("compatibility")
                            || description.toLowerCase().contains("fix"),
                    "Description should mention compatibility or fix");
        }
    }
}
