package tests;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.TreeMap;

import data.TestDatabase;
import objects.SoccerMatch;
import objects.SoccerMatchAnalysis;
import tests.objects.LeagueDTO;

@java.lang.SuppressWarnings("squid:S106")
public class RelevanceTest extends TestClass {

    // Premiere League Siffror
    // best lookback 16 getScoringDiffLastGames
    // best lookback 17 scoringTotal
    // best lookback 19 winLossRationHomeAndAway
    // best lookback 19 winLossRatio

    // Championship
    // best lookback 8 getScoringDiffLastGames
    // best lookback 9 scoringTotal
    // best lookback 6 winLossRationHomeAndAway
    // best lookback 5 winLossRatio

    // Allsvenskan
    // best lookback 12 getScoringDiffLastGames
    // best lookback 18 scoringTotal
    // best lookback 14 winLossRationHomeAndAway
    // best lookback 17 winLossRatio

    // Superettan
    // best lookback 8 getScoringDiffLastGames
    // best lookback 4 scoringTotal
    // best lookback 12 winLossRationHomeAndAway
    // best lookback 2 winLossRatio

    // MLS
    // best lookback 9 getScoringDiffLastGames
    // best lookback 9 scoringTotal
    // best lookback 7 winLossRationHomeAndAway
    // best lookback 15 winLossRatio

    int scoringDiffLastGames = 8;
    int scoringTotal = 4;
    int winLossRationHomeAndAway = 12;
    int winLossRatio = 2;

    Map<Float, Integer> winsOnDiff = new HashMap<>();
    Map<Float, Integer> lossesOnDiff = new HashMap<>();
    int totalWins = 0;
    int totalLosses = 0;
    int draws = 0;
    int drawsCorrect = 0;

    int gamesLookback = 10;

    float bestPercent = 0;
    int bestLookback = 0;

    List<Res> resultsDiffTotalGoals = new ArrayList<>();
    List<Res> resultsDiffHomeAway = new ArrayList<>();
    List<Res> resultsWinningForm = new ArrayList<>();
    List<Res> resultsWinnignFormHomeVsAway = new ArrayList<>();

    List<Res> results = new ArrayList<>();
    Map<String, Float> resultsCombined = new HashMap<>();

    String season;

    private float bestDrawPercent;

    @Override
    public void runTest() {
        TestDatabase database = TestDatabase.getInstance();
        LeagueDTO leagueDTO = database.getLeagueDTO(leagueId);
        LinkedHashMap<String, List<SoccerMatch>> matches = getMatchesOrderedBySeason(leagueDTO);

        boolean skipFirst = false;
        for (Entry<String, List<SoccerMatch>> seasonMatches : matches.entrySet()) {
            if (skipFirst) {
                skipFirst = false;
                continue;
            }
            for (int i = 1; i < 20; i++) {
                gamesLookback = i;
                season = seasonMatches.getKey();
                analyzeDiffHomeAway(seasonMatches.getValue(), resultsDiffHomeAway);
                analyzeDiffTotalGoals(seasonMatches.getValue(), resultsDiffTotalGoals);
                analyzeWinnigForm(seasonMatches.getValue(), resultsWinningForm);
                analyzeWinnigFormHomeVsAway(seasonMatches.getValue(), resultsWinnignFormHomeVsAway);
//            System.out.println("Season " + seasonMatches.getKey());
//            analyzeCombination(seasonMatches.getValue());
//            break;
            }
        }

        resultsCombined.forEach((k, v) -> System.out.println(k + " result " + v));

        resultsDiffHomeAway.stream().sorted((r1, r2) -> Float.compare(r2.getResult(), r1.getResult()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsDiffHomeAway Lookback " + e.getLookback() + " value " + e.getResult()));
        resultsDiffTotalGoals.stream().sorted((r1, r2) -> Float.compare(r2.getResult(), r1.getResult()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsDiffTotalGoals Lookback " + e.getLookback() + " value " + e.getResult()));

        resultsWinningForm.stream().sorted((r1, r2) -> Float.compare(r2.getResult(), r1.getResult()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsWinningForm Lookback " + e.getLookback() + " value " + e.getResult()));

        resultsWinnignFormHomeVsAway.stream().sorted((r1, r2) -> Float.compare(r2.getResult(), r1.getResult()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsWinnignFormHomeVsAway Lookback " + e.getLookback() + " value "
                                + e.getResult()));

        resultsDiffHomeAway.stream()
                .sorted((r1, r2) -> Float
                        .compare(r2.getDrawPercent(), r1.getDrawPercent()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsDiffHomeAway Draws correct " + e.getDrawPercent()
                                + " played "
                                + e.getDrawsPlayed() + " lookback " + e.getLookback()));

        resultsDiffTotalGoals.stream()
                .sorted((r1, r2) -> Float.compare(r2.getDrawPercent(), r1.getDrawPercent()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsDiffTotalGoals Draws correct " + e.getDrawPercent()
                                + " played "
                                + e.getDrawsPlayed() + " lookback " + e.getLookback()));

        resultsWinningForm.stream()
                .sorted((r1, r2) -> Float
                        .compare(r2.getDrawPercent(), r1.getDrawPercent()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsWinningForm Draws correct " + e.getDrawPercent()
                                + " played "
                                + e.getDrawsPlayed() + " lookback " + e.getLookback()));

        resultsWinnignFormHomeVsAway.stream()
                .sorted((r1, r2) -> Float.compare(r2.getDrawPercent(), r1.getDrawPercent()))
                .limit(5)
                .forEach(e -> System.out
                        .println("resultsWinnignFormHomeVsAway Draws correct " + e.getDrawPercent()
                                + " played "
                                + e.getDrawsPlayed() + " lookback " + e.getLookback()));

        System.out.println("Best percent " + bestPercent + " best lookback " + bestLookback);

    }

    private void analyzeDiffHomeAway(List<SoccerMatch> matches, List<Res> resultsDiffHomeAway) {
        System.out.println("Analysis Diff Home Away");
        winsOnDiff = new HashMap<>();
        lossesOnDiff = new HashMap<>();
        totalWins = 0;
        totalLosses = 0;
        draws = 0;
        drawsCorrect = 0;

        for (SoccerMatch soccerMatch : matches) {
            SoccerMatchAnalysis soccerMatchAnalysis = new SoccerMatchAnalysis(soccerMatch);
            checkBetResults(soccerMatch, soccerMatchAnalysis.getScoringDiffLastGames(gamesLookback));
        }

        TreeMap<Float, Integer> lossesSorted = sortMap(lossesOnDiff);
        TreeMap<Float, Integer> winsSorted = sortMap(winsOnDiff);

        printResults(winsSorted, lossesSorted, true, resultsDiffHomeAway);
    }

    private TreeMap<Float, Integer> sortMap(Map<Float, Integer> map) {
        map.entrySet().stream().sorted(Map.Entry.comparingByKey());
        TreeMap<Float, Integer> winsSorted = new TreeMap<>(map);
        return winsSorted;
    }

    private void analyzeDiffTotalGoals(List<SoccerMatch> matches, List<Res> resultsDiffTotalGoals) {
        System.out.println("Analysis Diff total goals");
        winsOnDiff = new HashMap<>();
        lossesOnDiff = new HashMap<>();
        totalWins = 0;
        totalLosses = 0;
        draws = 0;
        drawsCorrect = 0;

        for (SoccerMatch soccerMatch : matches) {
            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(soccerMatch);
            int homeTeamGoals = analysis.scoringTotal(gamesLookback, true);
            int awayTeamGoals = analysis.scoringTotal(gamesLookback, false);

            int diff = homeTeamGoals - awayTeamGoals;

            checkBetResults(soccerMatch, diff);
        }

        TreeMap<Float, Integer> winsSorted = sortMap(winsOnDiff);
        TreeMap<Float, Integer> lossesSorted = sortMap(lossesOnDiff);

        printResults(winsSorted, lossesSorted, true, resultsDiffTotalGoals);
    }

    private void analyzeWinnigForm(List<SoccerMatch> matches, List<Res> resultsWinningForm) {
        System.out.println("annalysis Winning form homeAndAway!");
        winsOnDiff = new HashMap<>();
        lossesOnDiff = new HashMap<>();
        totalWins = 0;
        totalLosses = 0;
        draws = 0;
        drawsCorrect = 0;

        for (SoccerMatch soccerMatch : matches) {
            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(soccerMatch);
            int homeWinsCount = analysis.winLossRatioHomeAndAway(true, gamesLookback);
            int awayWinsCount = analysis.winLossRatioHomeAndAway(false, gamesLookback);

            int diff = homeWinsCount - awayWinsCount;

            checkBetResults(soccerMatch, diff);
        }

        TreeMap<Float, Integer> winsSorted = sortMap(winsOnDiff);
        TreeMap<Float, Integer> lossesSorted = sortMap(lossesOnDiff);

        printResults(winsSorted, lossesSorted, true, resultsWinningForm);
    }

    private void analyzeWinnigFormHomeVsAway(List<SoccerMatch> matches,
            List<Res> resultsWinnignFormHomeVsAway) {
        System.out.println("annalysis Winning form homeVsAway!");
        winsOnDiff = new HashMap<>();
        lossesOnDiff = new HashMap<>();
        totalWins = 0;
        totalLosses = 0;
        draws = 0;
        drawsCorrect = 0;

        for (SoccerMatch soccerMatch : matches) {
            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(soccerMatch);
            int homeWinsCount = analysis.winLossRatio(gamesLookback, true);
            int awayWindCount = analysis.winLossRatio(gamesLookback, false);

            int diff = homeWinsCount - awayWindCount;

            checkBetResults(soccerMatch, diff);
        }

        TreeMap<Float, Integer> winsSorted = sortMap(winsOnDiff);
        TreeMap<Float, Integer> lossesSorted = sortMap(lossesOnDiff);

        printResults(winsSorted, lossesSorted, true, resultsWinnignFormHomeVsAway);
    }

    private void analyzeCombination(List<SoccerMatch> matches) {
        System.out.println("analysis Combination!");
        winsOnDiff = new HashMap<>();
        lossesOnDiff = new HashMap<>();
        totalWins = 0;
        totalLosses = 0;
        draws = 0;
        drawsCorrect = 0;

        int betOnHome = 0;
        int betOnAway = 0;

        int noBet = 0;

        for (SoccerMatch soccerMatch : matches) {
            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(soccerMatch);
            int homeWinsCount = analysis.winLossRatio(winLossRatio, true);
            int awayWindCount = analysis.winLossRatio(winLossRatio, false);

            int homeWinLossRatioCount = analysis.winLossRatioHomeAndAway(true, winLossRationHomeAndAway);
            int awayWinLossRatioCount = analysis.winLossRatioHomeAndAway(false, winLossRationHomeAndAway);

            int homeScoringTotal = analysis.scoringTotal(scoringTotal, true);
            int awayScoringTotal = analysis.scoringTotal(scoringTotal, false);

            int scoringDiffLastGamesDiff = analysis.getScoringDiffLastGames(scoringDiffLastGames);
            int winsCountDiff = homeWinsCount - awayWindCount;
            int winLossRatioDiff = homeWinLossRatioCount - awayWinLossRatioCount;
            int scoringTotalDiff = homeScoringTotal - awayScoringTotal;

            int testValue = 0;
            if (scoringDiffLastGamesDiff < 0 && winsCountDiff < 0 && winLossRatioDiff < 0 && scoringTotalDiff < 0) {
                testValue = (scoringDiffLastGamesDiff + winsCountDiff + winLossRatioDiff + scoringTotalDiff) / 4;
                betOnAway++;
            } else if (scoringDiffLastGamesDiff > 0 && winsCountDiff > 0 && winLossRatioDiff > 0
                    && scoringTotalDiff > 0) {
                testValue = (scoringDiffLastGamesDiff + winsCountDiff + winLossRatioDiff + scoringTotalDiff) / 4;
                betOnHome++;
            } else {
                noBet++;
            }

            if (testValue != 0) {
                checkBetResults(soccerMatch, testValue);
            }
        }

        TreeMap<Float, Integer> winsSorted = sortMap(winsOnDiff);
        TreeMap<Float, Integer> lossesSorted = sortMap(lossesOnDiff);

        printResults(winsSorted, lossesSorted, true, results);

        resultsCombined.put(season, (winsSorted.size() / ((float) (winsSorted.size() + lossesSorted.size()))));
        System.out.println("No Bet " + noBet);
        System.out.println("Bet on home: " + betOnHome + " bet on away: " + betOnAway);
    }

    private void checkBetResults(SoccerMatch soccerMatch, float decider) {
        if (decider > 0) { // Bet on HomeTeam
            if (soccerMatch.getHomeScore() > soccerMatch.getAwayScore()) {
                winsOnDiff.put(decider, winsOnDiff.getOrDefault(decider, 0) + 1);
                totalWins++;
            } else {
                lossesOnDiff.put(decider, lossesOnDiff.getOrDefault(decider, 0) + 1);
                totalLosses++;
            }
        } else if (decider < 0) { // Bet on away
            if (soccerMatch.getHomeScore() < soccerMatch.getAwayScore()) {
                winsOnDiff.put(decider, winsOnDiff.getOrDefault(decider, 0) + 1);
                totalWins++;
            } else {
                lossesOnDiff.put(decider, lossesOnDiff.getOrDefault(decider, 0) + 1);
                totalLosses++;
            }
        } else { // draw??
            draws++;
            if (soccerMatch.getHomeScore() == soccerMatch.getAwayScore()) {
                drawsCorrect++;
            }
        }
    }

    private void printResults(TreeMap<Float, Integer> winsSorted, TreeMap<Float, Integer> lossesSorted,
            boolean onlyPercentages, List<Res> resultsArray) {
        StringBuilder winsBuilder = new StringBuilder();
        winsSorted.forEach((k, v) -> winsBuilder.append(k + " wins " + v + "\n"));
        StringBuilder lossesBuilder = new StringBuilder();
        lossesSorted.forEach((k, v) -> lossesBuilder.append(k + " losses " + v + "\n"));

        if (!onlyPercentages) {
            System.out.println("Analyze Home Away Result");
            System.out.println("Wins");
            System.out.println(winsBuilder.toString());
            System.out.println("Losses");
            System.out.println(lossesBuilder.toString());
        }

        float winValue = totalWins / (float) (totalWins + totalLosses);
        Optional<Res> optional = resultsArray.stream().filter(p -> p.getLookback() == gamesLookback).findFirst();
        Res result;
        if (optional.isPresent()) {
            result = optional.get();
            result.setResult(result.getResult() + winValue);
            result.setDrawsPlayed(result.getDrawsPlayed() + draws);
            result.setDrawCorrect(result.getDrawCorrect() + drawsCorrect);
            resultsArray.set(gamesLookback, result);
        } else {
            result = new Res(gamesLookback, winValue, drawsCorrect, draws);
            resultsArray.add(result);
        }

        if (winValue > bestPercent) {
            bestPercent = winValue;
            bestLookback = gamesLookback;
        }

        float drawValue = drawsCorrect / (float) draws;
        if (drawValue > bestDrawPercent) {
            bestDrawPercent = drawValue;
            bestDrawLookback = gamesLookback;
        }

        System.out.println("Totals " + gamesLookback);
        System.out.println(winValue + " % total " + (totalWins + totalLosses));
        System.out.println("Draws " + drawsCorrect / (float) draws + " % total " + draws);
    }

    class Res {

        int lookback = 0;
        float result = 0;
        float drawCorrect = 0;
        int drawsPlayed = 0;

        public Res() {
        }

        public Res(int lookback, float result, float drawCorrect, int drawsPlayed) {
            super();
            this.lookback = lookback;
            this.result = result;
            this.drawCorrect = drawCorrect;
            this.drawsPlayed = drawsPlayed;
        }

        public int getLookback() {
            return lookback;
        }

        public float getResult() {
            return result;
        }

        public float getDrawCorrect() {
            return drawCorrect;
        }

        public int getDrawsPlayed() {
            return drawsPlayed;
        }

        public void setLookback(int lookback) {
            this.lookback = lookback;
        }

        public void setResult(float result) {
            this.result = result;
        }

        public void setDrawCorrect(float drawResult) {
            this.drawCorrect = drawResult;
        }

        public void setDrawsPlayed(int drawsPlayed) {
            this.drawsPlayed = drawsPlayed;
        }

        public float getDrawPercent() {
            return drawCorrect / drawsPlayed;
        }
    }
}
