package UE16_110325_Streams3;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class UE16_Streams3 {

    public static final List<Winner> tdfWinners = Winner.tdfWinners;

    public static void main(String[] args) {
        System.out.println("1. Winners of Tours Less than 3500km - " + winnersLessThan3500Names());
        System.out.println("2. Winners of Tours Greater than 3500km - " + winnersGreaterThan3500Names());
        System.out.println("3. winnerObjectsOfToursLessThan3500kmLimit2 " + firstTwoWinnersNamesForToursLessThan3500());
        System.out.println("4. firstTwoWinnersOfToursLessThan3500km - " + firstTwoWinnersNamesAgain());
        System.out.println("5. distinctTDFWinners - " + distinctWinnerNames());
        System.out.println("6. numberOfDistinctWinners - " + countDistinctWinnerNames());
        System.out.println("7. skipEveryOtherTDFWinner - " + skipFirstTwoWinner());
        System.out.println("8. mapWinnerYearNamesToList " + winnerYearNameStrings());
        System.out.println("9. mapWinnerNameLengthToList " + winnerNameLengths());
        System.out.println("10. Wiggins wins - " + winnerYearWiggins());
        System.out.println("11. winnerYear2014 - " + winnerNameOfYear2014());
        System.out.println("12. totalDistance - " + totalDistance());
        System.out.println("13. shortestDistance - " + shortestDistance());
        System.out.println("14. longestDistance - " + longestDistance());
        System.out.println("15. fastestTDF winner - " + winnerHighestAvgSpeed());
        System.out.println("16. fastestTDF aveSpeed - " + highestAverageSpeed());
        System.out.println("17. allTDFWinnersTeams " + allTeamsCommaSeparated());
        System.out.println("18. winnersByNationality - " + winnersByNationalityNames());
        System.out.println("19. winsByNationalityCounting - " + winsByNationalityCounting());

        printLogStatistic(Path.of("src/UE16_110325_Stream3/access_log/access.log"));
    }

    public static void printLogStatistic(Path path) {
        try {
            List<String> lines = Files.readAllLines(path);
            System.out.println("Most common IP-Address: " + getMostCommonIP(lines));
            System.out.println("Most common Page: " + getMostCommonPage(lines));
            System.out.println("Count Download Bytes: " + countDownloadBytes(lines) + " Bytes");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String mostCommonByRegex(String regex, List<String> input) {
        Pattern pattern = Pattern.compile(regex);
        Map<String, Integer> objects = new HashMap<>();
        for (String line : input) {
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()) objects.put(matcher.group(1), objects.getOrDefault(matcher.group(1), 0) + 1);
        }
        return objects.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
    }


    public static String getMostCommonIP(List<String> lines) {
        return mostCommonByRegex("(?:\\S+\\s+){2}(\\d{1,3}(?:\\.\\d{1,3}){3})", lines);
    }

    public static String getMostCommonPage(List<String> lines) {
        return mostCommonByRegex("(https?://.*) - ", lines);
    }

    public static String getMostCommonDomain(List<String> lines) {
        return mostCommonByRegex("(?<=https?://)((?:\\w+\\.)*[\\w-]+\\.[a-zA-Z]{2,})", lines);
    }

    public static long countDownloadBytes(List<String> lines) {
        long bytes = 0;
        Pattern pattern = Pattern.compile("TCP_.+ (\\d+)");
        for (String line : lines) {
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()) bytes += Long.parseLong(matcher.group(1));
        }
        return bytes;
    }

    public static List<String> winnersLessThan3500Names() {
        return tdfWinners.stream().filter(w -> w.getLengthKm() < 3500).map(Winner::getName).collect(Collectors.toList());
    }

    public static List<String> winnersGreaterThan3500Names() {
        return tdfWinners.stream().filter(w -> w.getLengthKm() > 3500).map(Winner::getName).collect(Collectors.toList());
    }

    public static List<Winner> firstTwoWinnersNamesForToursLessThan3500() {
        return tdfWinners.stream().filter(w -> w.getLengthKm() < 3500).limit(2).collect(Collectors.toList());
    }

    public static List<String> firstTwoWinnersNamesAgain() {
        return tdfWinners.stream().filter(w -> w.getLengthKm() < 3500).limit(2).map(Winner::getName).collect(Collectors.toList());
    }

    public static List<String> distinctWinnerNames() {
        return tdfWinners.stream().map(Winner::getName).distinct().collect(Collectors.toList());
    }

    public static int countDistinctWinnerNames() {
        return distinctWinnerNames().size();
    }

    public static List<Winner> skipFirstTwoWinner() {
        return tdfWinners.stream().skip(2).collect(Collectors.toList());
    }

    public static List<String> winnerYearNameStrings() {
        return tdfWinners.stream().map(w -> w.getYear() + " - " + w.getName()).collect(Collectors.toList());
    }

    public static List<Integer> winnerNameLengths() {
        return tdfWinners.stream().map(w -> w.getName().length()).collect(Collectors.toList());
    }

    public static int winnerYearWiggins() {
        return tdfWinners.stream().filter(w -> w.getName().contains("Wiggins")).mapToInt(Winner::getYear).findFirst().orElse(-1);
    }

    public static String winnerNameOfYear2014() {
        return tdfWinners.stream().filter(w -> w.getYear() == 2014).map(Winner::getName).findFirst().orElse("");
    }

    public static int totalDistance() {
        return tdfWinners.stream().mapToInt(Winner::getLengthKm).sum();
    }

    public static int shortestDistance() {
        return tdfWinners.stream().mapToInt(Winner::getLengthKm).min().orElse(0);
    }

    public static int longestDistance() {
        return tdfWinners.stream().mapToInt(Winner::getLengthKm).max().orElse(0);
    }

    public static String winnerHighestAvgSpeed() {
        return tdfWinners.stream().max(Comparator.comparingDouble(Winner::getAveSpeed)).map(Winner::getName).orElse("");
    }

    public static double highestAverageSpeed() {
        return tdfWinners.stream().max(Comparator.comparingDouble(Winner::getAveSpeed)).map(Winner::getAveSpeed).orElse(-1.0);
    }

    public static String allTeamsCommaSeparated() {
        return tdfWinners.stream().map(Winner::getTeam).collect(Collectors.joining(", "));
    }

    public static Map<String, List<Winner>> winnersByNationalityNames() {
        return tdfWinners.stream().collect(Collectors.groupingBy(Winner::getNationality, Collectors.toList()));
    }

    public static Map<String, Long> winsByNationalityCounting() {
        return tdfWinners.stream().collect(Collectors.groupingBy(Winner::getNationality, Collectors.counting()));
    }
}