/*
 * 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.surefire.its.fixture;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.maven.it.VerificationException;
import org.apache.maven.it.Verifier;
import org.hamcrest.Matcher;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.MatcherAssert.assertThat;

/**
 * A specialized verifier that enforces a standard use case for surefire IT's
 *
 * @author Kristian Rosenvold
 */
public class OutputValidator {
    final Verifier verifier;

    private final File baseDir;

    public OutputValidator(Verifier verifier) {
        this.verifier = verifier;
        this.baseDir = new File(verifier.getBasedir());
    }

    public OutputValidator verifyTextInLog(String text) {
        try {
            verifier.verifyTextInLog(text);
        } catch (VerificationException e) {
            throw new SurefireVerifierException(e);
        }
        return this;
    }

    public OutputValidator verifyErrorFreeLog() {
        try {
            verifier.verifyErrorFreeLog();
        } catch (VerificationException e) {
            throw new SurefireVerifierException(e);
        }
        return this;
    }

    public OutputValidator verifyErrorFree(int total) {
        try {
            verifier.verifyErrorFreeLog();
            this.assertTestSuiteResults(total, 0, 0, 0);
            return this;
        } catch (VerificationException e) {
            throw new SurefireVerifierException(e);
        }
    }

    public OutputValidator assertThatLogLine(Matcher<String> line, Matcher<Integer> nTimes)
            throws VerificationException {
        int counter = loadLogLines(line).size();
        assertThat("log pattern does not match nTimes", counter, nTimes);
        return this;
    }

    public List<String> loadLogLines() throws VerificationException {
        return verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false);
    }

    public List<String> loadLogLines(Matcher<String> line) throws VerificationException {
        List<String> matchedLines = new ArrayList<>();
        for (String log : loadLogLines()) {
            if (line.matches(log)) {
                matchedLines.add(log);
            }
        }
        return matchedLines;
    }

    public List<String> loadFile(File file, Charset charset) {
        //noinspection unchecked
        try {
            return FileUtils.readLines(file, charset.name());
        } catch (IOException e) {
            throw new SurefireVerifierException(e);
        }
    }

    public String getBasedir() {
        return verifier.getBasedir();
    }

    /**
     * Returns a file, referenced from the extracted root (where pom.xml is located)
     *
     * @param path The subdirectory under basedir
     * @return A file
     */
    public File getSubFile(String path) {
        return new File(getBasedir(), path);
    }

    public OutputValidator assertTestSuiteResults(int total, int errors, int failures, int skipped) {
        HelperAssertions.assertTestSuiteResults(total, errors, failures, skipped, baseDir);
        return this;
    }

    public OutputValidator assertTestSuiteResults(int total, int errors, int failures, int skipped, int flakes) {
        HelperAssertions.assertTestSuiteResults(total, errors, failures, skipped, flakes, baseDir);
        return this;
    }

    public OutputValidator assertTestSuiteResults(int total) {
        HelperAssertions.assertTestSuiteResults(total, baseDir);
        return this;
    }

    public OutputValidator assertIntegrationTestSuiteResults(int total, int errors, int failures, int skipped) {
        HelperAssertions.assertIntegrationTestSuiteResults(total, errors, failures, skipped, baseDir);
        return this;
    }

    public OutputValidator assertIntegrationTestSuiteResults(int total) {
        HelperAssertions.assertIntegrationTestSuiteResults(total, baseDir);
        return this;
    }

    public TestFile getTargetFile(String modulePath, String fileName) {
        File targetDir = getSubFile(modulePath + "/target");
        return new TestFile(new File(targetDir, fileName), this);
    }

    public TestFile getTargetFile(String fileName) {
        File targetDir = getSubFile("target");
        return new TestFile(new File(targetDir, fileName), this);
    }

    public TestFile getSurefireReportsFile(String fileName, Charset charset) {
        File targetDir = getSurefireReportsDirectory();
        return new TestFile(new File(targetDir, fileName), charset, this);
    }

    public TestFile getSurefireReportsFile(String fileName) {
        return getSurefireReportsFile(fileName, null);
    }

    public TestFile getSurefireReportsXmlFile(String fileName) {
        File targetDir = getSurefireReportsDirectory();
        return new TestFile(new File(targetDir, fileName), UTF_8, this);
    }

    public File getSurefireReportsDirectory() {
        return getSubFile("target/surefire-reports");
    }

    public TestFile getSiteFile(String fileName) {
        File targetDir = getSubFile("target/site");
        return new TestFile(new File(targetDir, fileName), this);
    }

    public File getBaseDir() {
        return baseDir;
    }

    public String[] getStringsOrderInLog(String[] strings) throws VerificationException {
        String[] retArr = new String[strings.length];
        List<String> strList = new ArrayList<>(Arrays.asList(strings));
        int i = 0;
        for (String line : loadLogLines()) {
            for (int j = 0; j < strList.size(); j++) {
                if (line.startsWith(strList.get(j))) {
                    retArr[i] = strList.get(j);
                    ++i;
                    if (i == strings.length) {
                        return retArr;
                    }
                    strList.remove(j);
                    break;
                }
            }
        }
        return retArr;
    }

    public boolean stringsAppearInSpecificOrderInLog(String[] strings) throws VerificationException {
        int i = 0;
        for (String line : loadLogLines()) {
            if (line.startsWith(strings[i])) {
                if (i == strings.length - 1) {
                    return true;
                }
                ++i;
            }
        }
        return false;
    }
}
