package tests;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import objects.League;
import objects.SoccerMatch;
import objects.Team;

public class AddedScoringTest extends TestClass {

	private List<SoccerMatch> trainMatches;

	@Override public void runTest() {
		trainMatches = getTrainMatches();
		final League leagueInfo = getLeagueInfoById();

		final int betMarginHome = leagueInfo.getBetMarginHome();
		final int betMarginDraw = leagueInfo.getBetMarginDraw();
		final int betMarginAway = leagueInfo.getBetMarginAway();

		final int lookbackHome = leagueInfo.getLookbackHome();
		final int lookbackDraw = leagueInfo.getLookbackDraw();
		final int lookbackAway = leagueInfo.getLookbackAway();

		final int lookback = leagueInfo.getLookback();
		final int margin = leagueInfo.getBetMargin();

		final float betLevel = bettingLevel / 100f;
		float bank = startingBank;
		float prevBank = startingBank;
		float prevBetAmount = startingBank * betLevel;

		int betOnGameCount = 0;
		int prevBetOnGameCount = 0;

		float betAmount = startingBank * betLevel;
		int wins = 0;
		int prevWins = 0;

		final float betMarginDecimalHome = 1 + (leagueInfo.getBetMarginHome() / 100f);
		final float betMarginDecimalAway = 1 + (leagueInfo.getBetMarginAway() / 100f);
		final float betMarginDecimalDraw = 1 + (leagueInfo.getBetMarginDraw() / 100f);

		int betOnX = 0;
		int correctX = 0;
		float xBank = startingBank;

		for (final SoccerMatch soccerMatch : trainMatches) {
			final Team homeTeam = soccerMatch.getHomeTeam();
			final Team awayTeam = soccerMatch.getAwayTeam();

			final List<SoccerMatch> homeMatches = trainMatches.stream()
					.filter(p -> p.getGameDate().isBefore(soccerMatch.getGameDate())
							&& p.getHomeTeam().getTeamId() == homeTeam.getTeamId())
					.limit(lookbackHome).toList();
			final List<SoccerMatch> awayMatches = trainMatches.stream()
					.filter(p -> p.getGameDate().isBefore(soccerMatch.getGameDate())
							&& p.getAwayTeam().getTeamId() == awayTeam.getTeamId())
					.limit(lookbackAway).toList();

			final long hemmaVinster = homeMatches.stream().filter(p -> p.getHomeScore() > p.getAwayScore()).count();
			final long hemmaLika = homeMatches.stream().filter(p -> p.getHomeScore() == p.getAwayScore()).count();
			final long hemmaForluster = homeMatches.stream().filter(p -> p.getHomeScore() < p.getAwayScore()).count();

			final long bortaVinster = awayMatches.stream().filter(p -> p.getAwayScore() > p.getHomeScore()).count();
			final long bortaLika = awayMatches.stream().filter(p -> p.getAwayScore() == p.getHomeScore()).count();
			final long bortaForluster = awayMatches.stream().filter(p -> p.getAwayScore() < p.getHomeScore()).count();

			int homeGoals = 0;
			for (final SoccerMatch hm : homeMatches) {
				homeGoals += hm.getHomeScore() - hm.getAwayScore();
			}

			int awayGoals = 0;
			for (final SoccerMatch am : awayMatches) {
				awayGoals += am.getAwayScore() - am.getHomeScore();
			}

			final float homeWinPercent = (hemmaVinster + bortaForluster)
					/ Float.valueOf(homeMatches.size() + awayMatches.size()) * 100;
			final float awayWinPercent = (hemmaForluster + bortaVinster)
					/ Float.valueOf(homeMatches.size() + awayMatches.size()) * 100;
			final float drawWinPercent = (hemmaLika + bortaLika)
					/ Float.valueOf(homeMatches.size() + awayMatches.size()) * 100;

			prevBetAmount = prevBank * betLevel;
			// check if should bet on home Win before score added
			if ((100 / homeWinPercent) * betMarginDecimalHome <= soccerMatch.getOdds1()) {
				// Bet on Home Win
				prevBetOnGameCount++;
				prevBank = prevBank - prevBetAmount;
				if (soccerMatch.getHomeScore() > soccerMatch.getAwayScore()) {
					prevWins++;
					prevBank = prevBank + prevBetAmount * soccerMatch.getOdds1();
				}
			}

			// check if should bet on away Win
			if ((100 / awayWinPercent) * betMarginDecimalAway <= soccerMatch.getOdds2()) {
				// Bet on Away Win
				prevBetOnGameCount++;
				prevBank = prevBank - prevBetAmount;
				if (soccerMatch.getAwayScore() > soccerMatch.getHomeScore()) {
					prevWins++;
					prevBank = prevBank + prevBetAmount * soccerMatch.getOdds2();
				}
			}

			final int goalDiff = homeGoals - awayGoals;

			float homeWinPercentGoals = homeWinPercent + ((homeGoals / 2f) * 10);
			float awayWinPercentGoals = awayWinPercent + ((awayGoals / 2f) * 10);

			if (homeWinPercentGoals <= 0) {
				homeWinPercentGoals = 0.01f;
			}

			if (awayWinPercentGoals <= 0) {
				awayWinPercentGoals = 0.01f;
			}

			final float homeOdds = 100 / homeWinPercentGoals;
			final float awayOdds = 100 / awayWinPercentGoals;

			betAmount = bank * betLevel;

			// check if should bet on home Win
			if (homeOdds * betMarginDecimalHome <= soccerMatch.getOdds1()) {
				// Bet on Home Win
				betOnGameCount++;
				bank = bank - betAmount;
				if (soccerMatch.getHomeScore() > soccerMatch.getAwayScore()) {
					wins++;
					bank = bank + betAmount * soccerMatch.getOdds1();
				}
			}

			// check if should bet on away Win
			if (awayOdds * betMarginDecimalAway <= soccerMatch.getOdds2()) {
				// Bet on Home Win
				betOnGameCount++;
				bank = bank - betAmount;
				if (soccerMatch.getAwayScore() > soccerMatch.getHomeScore()) {
					wins++;
					bank = bank + betAmount * soccerMatch.getOdds2();
				}
			}

			if (goalDiff >= -1 && goalDiff <= 1) {
				System.out.println("Test betting on X odds " + soccerMatch.getOddsX() + " Actual result "
						+ soccerMatch.getHomeScore() + " - " + soccerMatch.getAwayScore());
				betOnX++;
				final float xBetAmount = xBank * betLevel;
				xBank -= xBetAmount;
				if (soccerMatch.getHomeScore() == soccerMatch.getAwayScore()) {
					correctX++;
					xBank += xBetAmount * soccerMatch.getOddsX();
				}

			}
		}

		System.out.println(
				"Test ended with a bank of with score added " + bank + " and with no score added it was " + prevBank);
		System.out.println("Betted on " + betOnGameCount + " won " + wins + " out of " + trainMatches.size()
				+ " prev bets " + prevBetOnGameCount + " and prev wins " + prevWins);

		System.out.println("Test with draw ended with betting on " + betOnX + " matches and " + correctX + " correct "
				+ (correctX / (float) betOnX) + " bank " + xBank);
	}

	private ArrayList<SoccerMatch> getMatches() {
		final ArrayList<SoccerMatch> result = new ArrayList<>();

		final String sql = "SELECT res.*, " + "hTeam.name as homeTeamName, aTeam.name as awayTeamName "
				+ "FROM SoccerResults as res " + "Join Team as hTeam ON res.homeTeam = hTeam.id "
				+ "Join Team as aTeam ON res.awayTeam = aTeam.id " + "WHERE "
				+ "DATE(gameDate) > DATE_SUB(NOW(), INTERVAL 1 YEAR) AND " + "res.leagueId = ? "
				+ "ORDER BY gameDate ASC";

		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
			stat.setInt(1, leagueId);

			result.addAll(super.getMatches(stat));
		} catch (final SQLException e) {
			e.printStackTrace();
		}

		return result;
	}

	private List<SoccerMatch> getTrainMatches() {
		final ArrayList<SoccerMatch> result = new ArrayList<>();

		final String sql = "SELECT res.*, " + "hTeam.name as homeTeamName, aTeam.name as awayTeamName "
				+ "FROM SoccerResults as res " + "Join Team as hTeam ON res.homeTeam = hTeam.id "
				+ "Join Team as aTeam ON res.awayTeam = aTeam.id " + "WHERE "
				+ "DATE(gameDate) < DATE_SUB(NOW(), INTERVAL 1 YEAR) AND " + "res.leagueId = ? "
				+ "ORDER BY gameDate DESC";

		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
			stat.setInt(1, leagueId);

			result.addAll(super.getMatches(stat));
		} catch (final SQLException e) {
			e.printStackTrace();
		}

		return result;
	}

}
