package ru.yandex.autotests.directapi.reports.backtoback;

import java.io.InputStream;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import ru.yandex.autotests.directapi.model.api5.reports.ReportsData;
import ru.yandex.autotests.irt.testutils.allure.AllureUtils;
import ru.yandex.autotests.irt.testutils.allure.LogSteps;
import ru.yandex.qatools.elliptics.Elliptics;
import ru.yandex.qatools.pagediffer.PageDiffer;
import ru.yandex.qatools.pagediffer.diff.Diff;
import ru.yandex.qatools.pagediffer.diff.report.DiffReportGenerator;
import ru.yandex.qatools.pagediffer.diff.report.StandardReportGenerator;
import ru.yandex.qatools.pagediffer.document.XmlDocument;

import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.fail;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by andy-ilyin on 02.12.16.
 */
public abstract class CompareReportsTest extends TestBase {
    private static LogSteps log = LogSteps.getLogger(TestBase.class);

    private static Set<String> testsToRun = null;

    private static Set<String> failedTests = new TreeSet<>();

    private static boolean ranTests = false;

    @BeforeClass
    public static void fillTestsToSkip() {
        if (!onlyRunFailedTests()) {
            return;
        }

        Elliptics elliptics = new Elliptics();

        String ellipticsFileName = getPreviouslyFailedTestsLocation();

        InputStream fileStream = elliptics.getAsStream(ellipticsFileName);
        assumeThat("файл с ранее упавшими тестами есть в elliptics", fileStream, not(nullValue()));

        testsToRun = new HashSet<>();
        Scanner s = new Scanner(fileStream);
        while (s.hasNextLine()) {
            testsToRun.add(s.nextLine().trim());
        }
    }

    @Test
    @Override
    public void test() {
        ranTests = true;

        if (testsToRun != null && !testsToRun.contains(testCase.requestJson)) {
            log.info("тест пропущен, " + testCase + " прошёл в предыдущем запуске");
            return;
        }

        // где бы ни возникло исключение при обработке этого случая, надо, чтобы он попал в множество
        // если исключения не было, ниже есть вызов remove()
        failedTests.add(testCase.requestJson);

        ReportsData report = api.userSteps.reportsSteps().callReportsXml(reportRequest, clientLogin);
        assumeThat("отчёт не пустой", report.getReportsLines(), not(empty()));

        String actualResult = getComparableReportString(report);

        String fileName = getEllipticsRequestsFileName();

        Elliptics elliptics = new Elliptics();

        String actualResultsFileName = getEllipticsActualResultsFileName();

        elliptics.update(actualResult, actualResultsFileName);

        log.info("ожидаемый отчёт берём из файла в elliptics: "
                + elliptics.getUrlForName(fileName));

        log.info("полученный отчёт записан в файл в elliptics: "
                + elliptics.getUrlForName(actualResultsFileName));

        AllureUtils.addHtmlAttachment(
                "ожидаемый и полученный отчёты",
                String.format("<p><a href=\"%s\">ожидаемый отчёт</a></p><p><a href=\"%s\">полученный отчёт</a></p>",
                        elliptics.getUrlForName(fileName),
                        elliptics.getUrlForName(actualResultsFileName)));

        String expectedResult = elliptics.getAsString(fileName);

        if (!actualResult.equals(expectedResult)) {
            XmlDocument expectedDocument = new XmlDocument(expectedResult);
            XmlDocument actualDocument = new XmlDocument(actualResult);

            Diff<XmlDocument> diff = PageDiffer.diff(expectedDocument, actualDocument);
            DiffReportGenerator<XmlDocument> reportGenerator = new StandardReportGenerator<>();
            String diffResult = reportGenerator.generateReport(diff).toString();

            String ellipticsDiffFileName = getEllipticsDiffFileName();
            elliptics.put(diffResult, ellipticsDiffFileName);
            log.info(elliptics.getUrlForName(ellipticsDiffFileName));

            String acceptCommands = String.format("curl %s%ncurl %s | curl --data-binary @- -X POST %s",
                    elliptics.deleteUrl(fileName),
                    elliptics.getUrl(actualResultsFileName),
                    elliptics.uploadUrl(fileName));

            log.info(acceptCommands);

            AllureUtils.addHtmlAttachment(
                    "различия + как принять",
                    String.format("<p><a href=\"%s\">различия</a></p>" +
                            "<p>Как принять:</p>" +
                            "<pre>%s</pre>",
                            elliptics.getUrlForName(ellipticsDiffFileName),
                            acceptCommands));

            fail("результат не совпадает с эталоном");
        }

        failedTests.remove(testCase.requestJson);
    }

    @AfterClass
    public static void saveFailedTests() {
        if (!ranTests) {
            return;
        }

        Elliptics elliptics = new Elliptics();
        String ellipticsFileName = getEllipticsFailedTestsFileName();

        log.info(elliptics.getUrlForName(ellipticsFileName));
        log.info("failed tests: " + failedTests.size());

        if (failedTests.isEmpty()) {
            elliptics.delete(ellipticsFileName);
        } else {
            String failedTestsFileContent = failedTests.stream()
                    .map(json -> json + "\n")
                    .collect(Collectors.joining());

            elliptics.put(failedTestsFileContent, ellipticsFileName);
        }
    }
}
