package tests;

import java.time.LocalDate;
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.stream.Collectors;

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 AnalysisBettDrawTester extends TestClass {

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

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

    class LocalSoccerMatch extends SoccerMatch {
        String decider;
        SoccerMatch match;

        public LocalSoccerMatch(SoccerMatch match, String decider) {
            this.decider = decider;
            this.match = match;
        }
    }

    Map<String, List<Bet>> placedBets = new HashMap<>();
    TestDatabase database;

    List<LocalSoccerMatch> betsToPlace = new ArrayList<>();
    float betAmount;

    List<Bet> bets = new ArrayList<>();
    int wins = 0;
    int losses = 0;

    float bank;

    private List<Bet> betSeries = new ArrayList<>();

    private League leagueInfo;
    private int betsLost = 0;

    @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 = false;
        for (Entry<String, List<SoccerMatch>> seasonMatches : matches.entrySet()) {
            wins = 0;
            losses = 0;
            bank = startingBank;
            placedBets.clear();
            if (skipFirst) {
                skipFirst = false;
                continue;
            }
            analyze(seasonMatches.getValue());
            printResults();
            System.out
                    .println(
                            String.format(seasonMatches.getKey() + " Wins %s Losses %s %s total bank: %s", wins, losses,
                                    wins / (float) (wins + losses) + "%", bank));
//            break;
        }

    }

    private void addBet(SoccerMatch match, String deciders) {
        betsToPlace.add(new LocalSoccerMatch(match, deciders));
    }

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

            SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(match);

            int homeTotalGoals = analysis.scoringTotal(leagueInfo.getDrawTotalGoals(), true);
            int awayTotalGoals = analysis.scoringTotal(leagueInfo.getDrawTotalGoals(), false);
            int totalGoalsDecider = homeTotalGoals - awayTotalGoals;

            int homeTotalGoalsHomeAway = analysis.goalsScoredHomeAndAway(true, leagueInfo.getDrawDiffHomeAway());
            int awayTotalGoalsHomeAway = analysis.goalsScoredHomeAndAway(false, leagueInfo.getDrawDiffHomeAway());
            int totalGoalsHomeAwayDecider = homeTotalGoalsHomeAway - awayTotalGoalsHomeAway;

            int homeFormHomeAndAway = analysis.winLossRatioHomeAndAway(true,
                    leagueInfo.getDrawWinningFormHomeAway());
            int awayFormHomeAndAway = analysis.winLossRatioHomeAndAway(false,
                    leagueInfo.getDrawWinningFormHomeAway());
            int formHomeAwayDecider = homeFormHomeAndAway - awayFormHomeAndAway;

            int homeWiningForm = analysis.winLossRatio(leagueInfo.getDrawWinningForm(), true);
            int awayWiningForm = analysis.winLossRatio(leagueInfo.getDrawWinningForm(), false);
            int winningFormDecider = homeWiningForm - awayWiningForm;

//            if (totalGoalsDecider == 0 && totalGoalsHomeAwayDecider == 0 && formHomeAwayDecider == 0
//                    && winningFormDecider == 0) {
//                addBet(match, "ALL");
//            } else if (totalGoalsDecider == 0 && totalGoalsHomeAwayDecider == 0 && formHomeAwayDecider == 0) {
//                addBet(match, "NOT winningFormDecider");
//            } else if (totalGoalsDecider == 0 && totalGoalsHomeAwayDecider == 0 && winningFormDecider == 0) {
//                addBet(match, "NOT formHomeAwayDecider");
//            } else if (totalGoalsDecider == 0 && formHomeAwayDecider == 0 && winningFormDecider == 0) {
//                addBet(match, "NOT totalGoalsHomeAwayDecider");
//            } else if (totalGoalsHomeAwayDecider == 0 && formHomeAwayDecider == 0 && winningFormDecider == 0) {
//                addBet(match, "NOT totalGoalsDecider");
//            } else
            if (totalGoalsDecider == 0 && totalGoalsHomeAwayDecider == 0) {
                addBet(match, "totalGoalsDecider, totalGoalsHomeAwayDecider");
            } else if (totalGoalsDecider == 0 && formHomeAwayDecider == 0) {
                addBet(match, "totalGoalsDecider, formHomeAwayDecider");
            } else if (totalGoalsDecider == 0 && winningFormDecider == 0) {
                addBet(match, "totalGoalsDecider, winningFormDecider");
            } else if (totalGoalsHomeAwayDecider == 0 && formHomeAwayDecider == 0) {
                addBet(match, "totalGoalsHomeAwayDecider, formHomeAwayDecider");
            } else if (totalGoalsHomeAwayDecider == 0 && winningFormDecider == 0) {
                addBet(match, "totalGoalsHomeAwayDecider, winningFormDecider");
            } else if (formHomeAwayDecider == 0 && winningFormDecider == 0) {
                addBet(match, "formHomeAwayDecider, winningFormDecider");
//            } else if (totalGoalsDecider == 0) {
//                addBet(match, "totalGoalsDecider");
//            } else if (totalGoalsHomeAwayDecider == 0) {
//                addBet(match, "totalGoalsHomeAwayDecider");
//            } else if (formHomeAwayDecider == 0) {
//                addBet(match, "formHomeAwayDecider");
//            } else if (winningFormDecider == 0) {
//                addBet(match, "winningFormDecider");
            }

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

    private void checkBetResults() {

        placedBets.forEach((decider, betList) -> {
            List<Bet> activeBets = betList.stream().filter(b -> !b.resolved).toList();
            for (Bet bet : activeBets) {
                if (bet.getHomeScore() == bet.getAwayScore()) {
                    bank += bet.getBetAmount() * bet.getBetOdds();
                    wins++;
                } else {
                    losses++;
                    betSeries.add(bet);
                }

                bet.setResolved(true);
            }
        });

        /*-  OLD checkBetResults left for inspiration
        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));
        }
        */
    }

    private void placeBets() {
        for (LocalSoccerMatch soccerMatch : betsToPlace) {
            Bet bet = new Bet(-1, soccerMatch.match, "X", betAmount, soccerMatch.match.getOddsX());

            if (!betSeries.isEmpty()) {
                betSeries.sort((b1, b2) -> Float.compare(b2.getBetAmount(), b1.getBetAmount()));
                bet.setBetAmount(bet.getBetAmount() + betSeries.get(0).getBetAmount());
                betSeries.remove(0);
            }

            bank -= bet.getBetAmount();
            List<Bet> betsList = placedBets.get(soccerMatch.decider);

            if (betsList == null) {
                betsList = new ArrayList<>();
            }
            betsList.add(bet);
            placedBets.put(soccerMatch.decider, betsList);

            // F�r att t�cka upp f�reg�ende bets

//            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 printResults() {

        placedBets.forEach((decider, bets) -> {
            List<Bet> correctBets = bets.stream().filter(p -> p.getHomeScore() == p.getAwayScore()).toList();
            Double winnings = correctBets.stream()
                    .collect(Collectors.summingDouble(d -> (double) d.getBetAmount() * (double) d.getBetOdds()));
            Double totalBetAmount = bets.stream().collect(Collectors.summingDouble(Bet::getBetAmount));

            System.out.println(decider + " total matches betted " + bets.size() + " correct " + correctBets.size() + " "
                    + (correctBets.size() / (float) bets.size()) * 100 + "% total cost " + totalBetAmount
                    + " totalWinnings "
                    + winnings + " sum " + (winnings - totalBetAmount) + System.lineSeparator());

        });
    }
}
