package objects.visual;

import data.BetStrategyMysql.BetStrategyMysql;
import data.DTO.FibonacciDTO.FibonacciDTO;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import objects.MatchViewDTO;
import objects.SoccerMatch;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class FibonacciBets extends VBox {
    BetStrategyMysql database;
    List<FibonacciDTO> allOpenFibonacciSequences;
    private final int maxActiveFibonacciSequences = 4;
    private static final List<Integer> fibonacciNumberSequence = Arrays.asList(1, 1, 2, 3, 5, 8, 13, 21, 34, 55); //,89,144);
    private SoccerMatch currentMatch;
    Label matchTeamLabel = new Label();
    VBox betPanel;

    TextField oddsField = new TextField();
    ComboBox<String> sequenceNumberComboBox = new ComboBox<>();

    public FibonacciBets() {
        database = BetStrategyMysql.getInstance();
        this.getChildren().add(buildCurrentBetsPanel());
    }

    private VBox buildCurrentBetsPanel() {
        VBox panel = new VBox();
        allOpenFibonacciSequences = database.getAllOpenFibonacciSequences();

        List<Integer> sequenceNumbers = allOpenFibonacciSequences.stream().map(FibonacciDTO::getSequenceNumber).distinct().collect(Collectors.toList());

        for (Integer sequenceNumber : sequenceNumbers) {
            List<FibonacciDTO> fibonacciDTOs = allOpenFibonacciSequences.stream()
                    .filter(fibonacciDTO -> fibonacciDTO.getSequenceNumber() == sequenceNumber)
                    .collect(Collectors.toList());

            FlowPane fibonacciSequencePanel = new FlowPane();
            fibonacciSequencePanel.setHgap(2);
            fibonacciSequencePanel.setVgap(2);
            HBox infoPanel = new HBox(3);
            infoPanel.getChildren().add(new Label("Sequence Number: " + sequenceNumber + " ; "));
            infoPanel.getChildren().add(new Label("                    "));
            int pos = 0;
            for (Integer i : fibonacciNumberSequence) {
                Label label = new Label(i.toString());
                if (pos++ == fibonacciDTOs.size()) {
                    label.getStyleClass().add("bold-label");
                }
                infoPanel.getChildren().add(label);
            }
            panel.getChildren().add(infoPanel);
            for (FibonacciDTO dto : fibonacciDTOs) {
                fibonacciSequencePanel.getChildren().add(new Label(dto.getHomeTeamName() + " - " + dto.getAwayTeamName() + " (" + dto.getOdds() + ")"));
                fibonacciSequencePanel.getChildren().add(new Label(";"));
            }
            Button removeButton = new Button("X");
            removeButton.setStyle("-fx-background-color: darkred; -fx-text-fill: white; -fx-font-weight: bold;");
            removeButton.setOnAction(event -> {
                database.closeFibonacciSequence(sequenceNumber);
                allOpenFibonacciSequences.removeIf(m -> m.getSequenceNumber() == sequenceNumber);
                panel.getChildren().remove(fibonacciSequencePanel);
            });
            fibonacciSequencePanel.getChildren().add(removeButton);

            panel.getChildren().add(fibonacciSequencePanel);
        }

        return panel;
    }

    public int getSequenceToBetOn(LocalDate date) {
        Optional<FibonacciDTO> sequenceToBetOn = allOpenFibonacciSequences.stream()
                .filter(p -> !LocalDate.parse(p.getGameDate()).isBefore(date))
                .min((p1, p2) -> Integer.compare(p1.getSequenceNumber(), p2.getSequenceNumber()));

        return sequenceToBetOn.map(FibonacciDTO::getSequenceNumber).orElse(-1);
    }

    public List<FibonacciDTO> getOpenFibonacciSequences() {
        return allOpenFibonacciSequences;
    }

    public int getMaxActiveFibonacciSequences() {
        return maxActiveFibonacciSequences;
    }

    public VBox getNewBetGui() {
        betPanel = new VBox();
        if (currentMatch == null) {
            return betPanel;
        }

        sequenceNumberComboBox = new ComboBox<>();
        sequenceNumberComboBox.getItems().addAll(allOpenFibonacciSequences.stream().map(FibonacciDTO::getSequenceNumber).distinct().map(Object::toString).toList());
        sequenceNumberComboBox.getItems().add("New Sequence");

        matchTeamLabel.setText(currentMatch.toString());
        oddsField = new TextField();
        oddsField.setPromptText("Odds");

        betPanel.getChildren().addAll(sequenceNumberComboBox, matchTeamLabel, oddsField);
        return betPanel;
    }

    public void setCurrentMatch(SoccerMatch match) {
        this.currentMatch = match;
        updateBetsPanel();
    }

    private void updateBetsPanel() {
        matchTeamLabel.setText(currentMatch.toString());
        if (betPanel != null) {
            betPanel.getChildren().clear();
            betPanel.getChildren().addAll(sequenceNumberComboBox, matchTeamLabel, oddsField);
        }
    }

    public String getOddsField() {
        return oddsField.getText();
    }

    public float getOddsFieldFloat() {
        return Float.parseFloat(oddsField.getText());
    }

    public int getSequenceNumber() {
        try {
            return Integer.parseInt(sequenceNumberComboBox.getValue());
        } catch (NumberFormatException e) {
            return database.getNextSequenceNumber();
        }
    }

    public boolean validBetSettings() {
        try {
            if (Float.parseFloat(oddsField.getText()) < 0.0f) {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
        if (sequenceNumberComboBox.getValue() == null || sequenceNumberComboBox.getValue().isEmpty()) {
            return false;
        }
        return true;
    }

    public void addBet(int sequenceNumber, float odds, MatchViewDTO match) {
        List<FibonacciDTO> fibonacciDTO = getOpenFibonacciSequences().stream().filter(p -> p.getSequenceNumber() == sequenceNumber).collect(Collectors.toList());
        int currentSequenceBetAmount;
        try {
            currentSequenceBetAmount = fibonacciNumberSequence.get(fibonacciDTO.size());
        } catch (IndexOutOfBoundsException e) {
            return;
        }
        FibonacciDTO dto = new FibonacciDTO();
        dto.setBetAmount(currentSequenceBetAmount);
        dto.setOdds(odds);
        dto.setMatchId(match.getMatchId());
        dto.setSequenceNumber(sequenceNumber);
        database.addFibonacciBet(dto);

        allOpenFibonacciSequences.add(dto);

        betPanel.getChildren().clear();
    }
}
