/*
 * 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.it;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

import org.junit.jupiter.api.Test;

import static java.nio.file.FileVisitResult.CONTINUE;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * This is a test case for a new check introduced with <a href="https://issues.apache.org/jira/browse/MNG-4660">MNG-4660</a>.
 * That check verifies if a packaged artifact within the Reactor is up-to-date with the outputDirectory of the same project.
 *
 * @author Maarten Mulders
 * @author Martin Kanters
 */
public class MavenITmng4660OutdatedPackagedArtifact extends AbstractMavenIntegrationTestCase {
    public MavenITmng4660OutdatedPackagedArtifact() {
        super("[4.0.0-alpha-1,)");
    }

    /**
     * Test that Maven logs a warning when a packaged artifact is found that is older than the outputDirectory of the
     * same artifact.
     *
     * @throws Exception in case of failure
     */
    @Test
    public void testShouldWarnWhenPackagedArtifactIsOutdated() throws Exception {
        final File testDir = extractResources("/mng-4660-outdated-packaged-artifact");
        Files.createDirectories(testDir.toPath().resolve(".mvn"));

        // 1. Package the whole project
        final Verifier verifier1 = newVerifier(testDir.getAbsolutePath());
        verifier1.deleteDirectory("target");
        verifier1.deleteArtifacts("org.apache.maven.its.mng4660");

        verifier1.addCliArgument("package");
        verifier1.execute();

        Path module1Jar =
                testDir.toPath().resolve("module-a/target/module-a-1.0.jar").toAbsolutePath();
        verifier1.verifyErrorFreeLog();
        verifier1.verifyFilePresent(module1Jar.toString());

        if (System.getProperty("java.version", "").startsWith("1.")) {
            // Simulating the delay between two invocations. It also makes sure we're not hit by tests that run so fast,
            // that the difference in file modification time (see below) is too small to observe. Java 8 on Linux and
            // macOS returns that value with "just" second precision, which is not detailed enough.
            Thread.sleep(1_000);
        }

        // 2. Create a properties file with some content and compile only that module (module A).
        final Verifier verifier2 = newVerifier(testDir.getAbsolutePath());
        final Path resourcesDirectory =
                Files.createDirectories(Paths.get(testDir.toString(), "module-a", "src", "main", "resources"));
        final Path fileToWrite = resourcesDirectory.resolve("example.properties");
        Files.writeString(fileToWrite, "x=42");

        verifier2.setAutoclean(false);
        verifier2.addCliArgument("--projects");
        verifier2.addCliArgument(":module-a");
        verifier2.addCliArgument("compile");
        verifier2.execute();

        Path module1PropertiesFile = testDir.toPath()
                .resolve("module-a/target/classes/example.properties")
                .toAbsolutePath();

        verifier2.verifyFilePresent(module1PropertiesFile.toString());
        assertTrue(
                Files.getLastModifiedTime(module1PropertiesFile).compareTo(Files.getLastModifiedTime(module1Jar)) >= 0);

        Path module1Class = testDir.toPath()
                .resolve("module-a/target/classes/org/apache/maven/it/Example.class")
                .toAbsolutePath();
        verifier2.verifyErrorFreeLog();
        verifier2.verifyFilePresent(module1Class.toString());

        // 3. Resume project build from module B, that depends on module A we just touched. Its packaged artifact
        // is no longer in sync with its compiled artifacts.
        final Verifier verifier3 = newVerifier(testDir.getAbsolutePath());
        verifier3.setAutoclean(false);
        verifier3.addCliArgument("--resume-from");
        verifier3.addCliArgument(":module-b");
        verifier3.addCliArgument("compile");
        verifier3.execute();

        verifier3.verifyErrorFreeLog();
        try {
            verifier3.verifyTextInLog(
                    "File '"
                            + Paths.get("module-a", "target", "classes", "example.properties")
                            + "' is more recent than the packaged artifact for 'module-a', please run a full `mvn package` build");
        } catch (VerificationException e) {
            final StringBuilder message = new StringBuilder(e.getMessage());
            message.append(System.lineSeparator());

            message.append("  ")
                    .append(module1Jar.toAbsolutePath())
                    .append(" -> ")
                    .append(Files.getLastModifiedTime(module1Jar))
                    .append(System.lineSeparator());

            message.append(System.lineSeparator());

            Path outputDirectory = Paths.get(testDir.toString(), "module-a", "target", "classes");

            Files.walkFileTree(outputDirectory, new FileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                    return CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    message.append("  ")
                            .append(file.toAbsolutePath())
                            .append(" -> ")
                            .append(attrs.lastModifiedTime())
                            .append(System.lineSeparator());
                    return CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    return CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
                    return CONTINUE;
                }
            });

            throw new VerificationException(message.toString(), e.getCause());
        }
    }
}
