package tests;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import data.TestDatabase;
import objects.SoccerMatch;
import objects.bets.AsianHandicap;
import objects.bets.Bet;
import tests.objects.LeagueDTO;

public class BetOnDifference extends TestClass {

    private static final float MIN_DIFF_IN_GOALS = 0.5f;
    private static final float LOWER_LIMIT_COVER_BET = 1.3f;
    private static final float HIGHER_LIMIT_COVER_BET = 2.0f;
    TestDatabase database;
    List<SoccerMatch> betsToPlace = new ArrayList<>();
    float betAmount;
    List<DifferenceAsianBetDTO> bets = new ArrayList<>();

    int wins = 0;
    int losses = 0;

    float bank;
    private List<DifferenceAsianBetDTO> betSeries = new ArrayList<>();

    @Override
    public void runTest() {
        bank = startingBank;
        database = TestDatabase.getInstance();

        LeagueDTO leagueDTO = database.getLeagueDTO(leagueId);
        LinkedHashMap<String, List<SoccerMatch>> matches = getMatchesOrderedBySeason(leagueDTO);
        this.betAmount = startingBank * (bettingLevel / 100.0f);

        boolean skipFirst = true;
        for (Entry<String, List<SoccerMatch>> seasonMatches : matches.entrySet()) {
            if (skipFirst) {
                skipFirst = false;
                continue;
            }
            analyze(seasonMatches.getValue());
            System.out
                    .println(String.format("Wins %s Losses %s %s total bank: %s", wins, losses,
                            wins / (float) (wins + losses) + "%", bank));
            break;
        }

    }

    private void analyze(List<SoccerMatch> seasonMatches) {

        LocalDate currentMatchDate = seasonMatches.get(0).getGameDate().toLocalDate();

        int skipFirstXMatchDates = 10;
        for (SoccerMatch match : seasonMatches) {

            List<Float> avgHomeScoreThisSeason = database.getAvgHomeScoreThisSeason(match.getHomeTeam().getTeamId(),
                    countryId, leagueId, match.getGameDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
            List<Float> avgAwayScoreThisSeason = database.getAvgAwayScoreThisSeason(match.getAwayTeam().getTeamId(),
                    countryId, leagueId, match.getGameDate().format(DateTimeFormatter.ISO_LOCAL_DATE));

            if (currentMatchDate.isBefore(match.getGameDate().toLocalDate())) {
                if (skipFirstXMatchDates > 0) {
                    bets.clear();
                    betsToPlace.clear();
                    skipFirstXMatchDates--;
                    continue;
                }
                placeBets();
                checkBetResults();
            }
            /**
             * Matchen har en differens mellan hemma lagets mål och borta laget på minst X
             * (starta att testa med 1)
             */
            match.setAvgScoreHome((avgHomeScoreThisSeason.get(0) * avgAwayScoreThisSeason.get(1)));
            match.setAvgScoreAway((avgHomeScoreThisSeason.get(1) * avgAwayScoreThisSeason.get(0)));
            match.setGoalsDiff(
                    BigDecimal.valueOf((avgHomeScoreThisSeason.get(0) * avgAwayScoreThisSeason.get(1))
                            - (avgHomeScoreThisSeason.get(1) * avgAwayScoreThisSeason.get(0))));
            if (Math.abs((avgHomeScoreThisSeason.get(0) * avgAwayScoreThisSeason.get(1))
                    - (avgHomeScoreThisSeason.get(1) * avgAwayScoreThisSeason.get(0))) >= MIN_DIFF_IN_GOALS) {
                betsToPlace.add(match);
            }
            currentMatchDate = match.getGameDate().toLocalDate();
        }
    }

    private void placeBets() {

        betsToPlace.sort((b1, b2) -> Float.compare(Math.abs(b2.getGoalsDiff().floatValue()),
                Math.abs(b1.getGoalsDiff().floatValue())));
        for (SoccerMatch soccerMatch : betsToPlace) {
            float currentBetAmount = 0;
            betSeries.sort((b1, b2) -> Float.compare(b2.getBetAmount(), b1.getBetAmount()));
            if (!betSeries.isEmpty() && !betsToPlace.isEmpty()) {
                currentBetAmount += betSeries.get(0).getBetAmount();
                betSeries.remove(0);
            }
            if (soccerMatch.getAvgScoreHome() > soccerMatch.getAvgScoreAway()) {
                if (currentBetAmount > 0) {
                    currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds1() - 1.0));
                }
                bets.add(new DifferenceAsianBetDTO(soccerMatch, "1", currentBetAmount + betAmount, true, 0f));
            } else {
                if (currentBetAmount > 0) {
                    currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds2() - 1.0));
                }
                bets.add(new DifferenceAsianBetDTO(soccerMatch, "2", currentBetAmount + betAmount, false, 0f));
            }

            bank -= currentBetAmount + betAmount;
        }

        betsToPlace.clear();
    }

    private void checkBetResults() {
        List<DifferenceAsianBetDTO> activeBets = bets.stream().filter(p -> !p.resolved).toList();
        for (DifferenceAsianBetDTO bet : activeBets) {
            if (bet.getResult() > 0) {
                bank += bet.getResult(); // bet.getBetAmount() * bet.getBetOdds();
                String winText = bet.correctBet() ? bet.isBetOnHomeTeam() ? "HOME WIN " : "AWAY WIN" : "HALF WIN";
                System.out.println(String.format(winText + "\t" + "%s \t\tnew bank %s", bet, bank));
                wins++;
            } else {
                // LOSS
                System.out.println(String.format("LOSS\t\t%s \t\t bank: %s", bet, bank));
                losses++;
                betSeries.add(bet);
            }
            bet.resolved = true;
        }
        if (!betSeries.isEmpty()) {
            System.out.println("Outstanding bets:");
            betSeries.forEach(b -> System.out.println(b));
        }
    }

    public class DifferenceBetDTO extends Bet {
        boolean resolved = false;

        public DifferenceBetDTO(SoccerMatch match, String bet, float betAmount, boolean betOnHomeTeam) {
            super(0, match, bet, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2());
        }

        @Override
        public String toString() {
            return String.format("%s \t%s - %s \t\tbet amount %s \todds %s \twinner %s \t result %s-%s \tdiff %s",
                    getMatch().getGameDate().toLocalDate(),
                    getMatch().getHomeTeamName(),
                    getMatch().getAwayTeamName(), betAmount, super.getBetOdds(),
                    super.isBetOnHomeTeam() ? "1" : "2", getMatch().getHomeScore(),
                    getMatch().getAwayScore(), getMatch().getGoalsDiff());
        }
    }

    public class DifferenceAsianBetDTO extends AsianHandicap {
        boolean resolved = false;

        public DifferenceAsianBetDTO(SoccerMatch match, String bet, float betAmount, boolean betOnHomeTeam,
                float handicap) {
            super(match, bet, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2(), handicap);
        }

        @Override
        public String toString() {
            return String.format(
                    "%s \t%s - %s \t\tbet amount %s \todds %s \twinner %s \t result %s-%s \tdiff %s with results %s",
                    getMatch().getGameDate().toLocalDate(),
                    getMatch().getHomeTeamName(),
                    getMatch().getAwayTeamName(), betAmount, super.getBetOdds(),
                    super.isBetOnHomeTeam() ? "1" : "2", getMatch().getHomeScore(),
                    getMatch().getAwayScore(), getMatch().getGoalsDiff(), getResult());
        }
    }

}
