package tests;

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

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

public class AnalysisBettTester extends TestClass {

    TestDatabase database;
    List<SoccerMatch> betsToPlace = new ArrayList<>();
    float betAmount;
    List<DifferenceBetDTO> bets = new ArrayList<>();

    int wins = 0;
    int losses = 0;

    float bank;
    private List<DifferenceBetDTO> betSeries = new ArrayList<>();
    private League leagueInfo;

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

        leagueInfo = database.getLeagueInfo(leagueId);

        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();

        for (SoccerMatch match : seasonMatches) {

            if (currentMatchDate.isBefore(match.getGameDate().toLocalDate())) {
                placeBets();
                checkBetResults();

                betAmount = bank * (bettingLevel / 100.0f);
            }
            /**
             * Matchen har en differens mellan hemma lagets mål och borta laget på minst X
             * (starta att testa med 1)
             */
            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(match);

            int homeWinsCount = analysis.winLossRatio(leagueInfo.getWinLossRatio(), true);
            int awayWinsCount = analysis.winLossRatio(leagueInfo.getWinLossRatio(), false);

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

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

            int scoringDiffLastGames = analysis.getScoringDiffLastGames(leagueInfo.getScoringDiffLastGame());
            int winsCountDiff = homeWinsCount - awayWinsCount;
            int winLossRatioDiff = homeWinLossRatioCount - awayWinLossRatioCount;
            int scoringTotalDiff = homeScoringTotal - awayScoringTotal;

            int testValue = 0;
            if (scoringDiffLastGames < 0 && winsCountDiff < 0 && winLossRatioDiff < 0 && scoringTotalDiff < 0) {
                addBet(match, scoringDiffLastGames, winsCountDiff, winLossRatioDiff, scoringTotalDiff);
            } else if (scoringDiffLastGames > 0 && winsCountDiff > 0 && winLossRatioDiff > 0 && scoringTotalDiff > 0) {
                addBet(match, scoringDiffLastGames, winsCountDiff, winLossRatioDiff, scoringTotalDiff);
            } else {
//                match.put("analysis", "NoBet");
            }

            currentMatchDate = match.getGameDate().toLocalDate();
        }
    }

    private void addBet(SoccerMatch match, int scoringDiffLastGames, int winsCountDiff, int winLossRatioDiff,
            int scoringTotalDiff) {
        int testValue;
        testValue = (scoringDiffLastGames + winsCountDiff + winLossRatioDiff + scoringTotalDiff) / 4;
        match.setGoalsDiff(BigDecimal.valueOf(testValue));// Temp use GoldsDiff for value
        betsToPlace.add(match);
    }

    private void placeBets() {

        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.getGoalsDiff().floatValue() > 0f) {
                if (currentBetAmount > 0) {
                    currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds1() - 1.0));
                }
                bets.add(new DifferenceBetDTO(soccerMatch, currentBetAmount + betAmount, true));
            } else if (soccerMatch.getGoalsDiff().floatValue() < 0f) {
                if (currentBetAmount > 0) {
                    currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds2() - 1.0));
                }
                bets.add(new DifferenceBetDTO(soccerMatch, currentBetAmount + betAmount, false));
            }

            bank -= currentBetAmount + betAmount;
        }

        betsToPlace.clear();
    }

    private void checkBetResults() {
        List<DifferenceBetDTO> activeBets = bets.stream().filter(p -> !p.resolved).toList();
        for (DifferenceBetDTO 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, float betAmount, boolean betOnHomeTeam) {
            super(0, match, betOnHomeTeam ? "1" : "2", 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(), getBetAmount(), super.getBetOdds(),
                    super.isBetOnHomeTeam() ? "1" : "2", getMatch().getHomeScore(),
                    getMatch().getAwayScore(), getMatch().getGoalsDiff());
        }
    }

    public class DifferenceAsianBetDTO extends AsianHandicap {
        boolean resolved = false;

        public DifferenceAsianBetDTO(SoccerMatch match, float betAmount, boolean betOnHomeTeam, float handicap) {
            super(match, betOnHomeTeam ? "1" : "2", 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(), getBetAmount(), super.getBetOdds(),
                    super.isBetOnHomeTeam() ? "1" : "2", getMatch().getHomeScore(),
                    getMatch().getAwayScore(), getMatch().getGoalsDiff(), getResult());
        }
    }

}
