package client;

import Server.*;
import Server.Automaton.ActionResult;
import Server.Automaton.Game;
import Server.Cards.*;
import javafx.animation.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

public class DeckGridAppFX extends Application {

    private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
    private static final int IMG_HEIGHT=200;
    private PDDocument documentFront;
    private PDDocument documentBack;
    private PDFRenderer pdfRendererFront;
    private PDFRenderer pdfRendererBack;
    private int totalCards = 0;

    // Contenitori Layout
    private HBox topMenu;
    private HBox topRow;
    private HBox centerRow;
    private HBox bottomRow;
    private HBox playersArea; // NUOVO: Area per i giocatori
    private Button btnRefresh;
    private Button btnTop;
    private Button btnBottom;
    private Button btnChooseOffering;

    private Button btnMsg;
    // --------------------------------

    @Override
    public void start(Stage primaryStage) {

        List<Player> players = new ArrayList<>();
        //players.add(new Player("Yellow", TotemColor.YELLOW));
        players.add(new Player("Blue", TotemColor.BLUE));
        //players.add(new Player("Purple", TotemColor.PURPLE));
        players.add(new Player("Red", TotemColor.RED));
        //players.add(new Player("Green", TotemColor.GREEN));

        Game game = new Game(players);

        InputStream frontStream = getClass().getResourceAsStream("/files/Cards_total_front_PROMO.pdf");
        InputStream backStream = getClass().getResourceAsStream("/files/Cards_total_back_PROMO.pdf");
        InputStream csvStream = getClass().getResourceAsStream("/files/cards.csv");

        try {
            documentFront = PDDocument.load(frontStream);
            pdfRendererFront = new PDFRenderer(documentFront);

            documentBack = PDDocument.load(backStream);
            pdfRendererBack = new PDFRenderer(documentBack);

        } catch ( IOException e) {
            throw new RuntimeException(e);
        }
        //File fileCsv = new File(getClass().getResource("/files/cards.csv").toURI());
        game.newGame(csvStream);

        game.setEventListener(notification -> {
            Platform.runLater(() -> {
                drawGameState(game);
                switch (notification.event()) {
                    case TOTEM_PLACEMENT-> showPopup("TOTEM PLACEMENT\n" + (notification.message()!=null?notification.message():""));
                    case SETUP       -> showPopup("SETUP \n" + (notification.message()!=null?notification.message():""));
                    case ACTION_RESOLUTION -> showPopup("ACTION_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
                    case END_ACTION -> showPopup("END_ACTION \n" + (notification.message()!=null?notification.message():"" ));
                    case EVENT_RESOLUTION -> showPopup("EVENT_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
                    case EXTRA_DRAW -> showPopup("EXTRA_DRAW \n" + (notification.message()!=null?notification.message():"" ));
                    case GAME_OVER     -> showPopup("GAME OVER");

                }
            });
        });


        primaryStage.setTitle("Tavolo da Gioco - Board & Players");

        btnTop = new Button("Pick Top");
        btnBottom = new Button("Pick Bottom");
        btnMsg = new Button("Game State");
        btnRefresh= new Button("Refresh");
        btnChooseOffering= new Button("Choose Offering");
        //btnNext.setDisable(true);
        topMenu = new HBox(15,  btnRefresh, btnMsg);
        topMenu.setAlignment(Pos.CENTER);
        topMenu.setPadding(new Insets(10));

        Label stateLabel = new Label("Game State = " + game.getState() );
        stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
        stateLabel.setTextFill(Color.DARKBLUE);

        //showPopup(topMenu, "STARTING GAME");

        btnMsg.setOnAction(e -> {
            new Alert(Alert.AlertType.INFORMATION,
                    game.toString()
            ).showAndWait();
        });
        btnRefresh.setOnAction(e -> {
            drawGameState(game);
        });

        topRow = createRowContainer();
        centerRow = createRowContainer();
        bottomRow = createRowContainer();

        // NUOVO: Inizializza l'area giocatori
        playersArea = new HBox(30);
        playersArea.setAlignment(Pos.CENTER);
        playersArea.setPadding(new Insets(20));




        VBox tableArea = new VBox(20,
                new Label(" "), topRow,
                new Label("  "), centerRow,
                new Label(" "), bottomRow,
                new Label("--- ASSET PLAYERS ---"), playersArea
        );
        tableArea.setAlignment(Pos.CENTER);
        tableArea.setPadding(new Insets(20));

        ScrollPane scrollPane = new ScrollPane(tableArea);
        scrollPane.setFitToWidth(true);

        BorderPane root = new BorderPane();
        root.setTop(topMenu);
        root.setCenter(scrollPane);

        Scene scene = new Scene(root, 1300, 900);
        primaryStage.setScene(scene);
        primaryStage.show();

        drawGameState(game);
    }

    private HBox createRowContainer() {
        HBox row = new HBox(10);
        row.setAlignment(Pos.CENTER);
        return row;
    }


    private void drawGameState(Game game) {
        if (documentFront == null) return;


        topMenu.getChildren().clear();
        topRow.getChildren().clear();
        centerRow.getChildren().clear();
        bottomRow.getChildren().clear();
        playersArea.getChildren().clear();

        drawTopMenu(topMenu, game);
        drawTopRow(topRow, game);
        drawTurnTileAndOffering(centerRow,  game.getPlayers().size(), game);
        drawBottomRow(bottomRow, game );


        List<Player> players =game.getPlayers();
        renderPlayers(players, game);
        logger.info(game.getCurrentPlayer());

    }

    private void pickTopCardAction(Player player, GameBoard board, int cardid){
        Card card = board.getTopRow().stream()
                .filter(CharacterCard.class::isInstance)
                .map(CharacterCard.class::cast)
                .filter(c -> c.getCardId() == cardid)
                .findFirst()
                .orElse(null);

                if (card != null) {
                    board.getTopRow().remove(card);
                    CharacterCard charCard = (CharacterCard)card;
                    player.addCharacterToTribe((CharacterCard) card);
                }
    }

    private void pickBottomCardAction(Player player, GameBoard board, int cardid){
        Card card = board.getBottomRow().stream()
                .filter(CharacterCard.class::isInstance)
                .map(CharacterCard.class::cast)
                .filter(c -> c.getCardId() == cardid)
                .findFirst()
                .orElse(null);
                if (card != null) {
                    board.getBottomRow().remove(card);
                    player.addCharacterToTribe((CharacterCard) card);
                }
    }


    private void drawBottomRow(HBox row, Game game) {
        for (Card c : game.getGameBoard().getBottomRow()) {
            ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);

            if (cardImage != null) {
                // 1. Create a Label for your debugging text
                Label debugLabel = new Label("ID: " + c.getCardId());

                // 2. Style the label so it's readable over any card background
                // (White text with a semi-transparent black background)
                debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
                        "-fx-text-fill: white; " +
                        "-fx-padding: 3px; " +
                        "-fx-font-weight: bold;");

                // 3. Create a StackPane and add both the image and the text
                StackPane cardContainer = new StackPane();
                cardContainer.getChildren().addAll(cardImage, debugLabel);

                // 4. Align the text wherever you want (e.g., Top-Left corner of the card)
                StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
                // You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
                cardImage.setOnMouseClicked(event -> {
                    logger.info("Bottom Card clicked");
                    Player p = game.getCurrentPlayer();
                    //pickBottomCardAction(p, game.getGameBoard(), c.getCardId());
                    ActionResult result = game.resolveCardAction(p, false, c.getCardId());
                    if (!result.isSuccess()) {
                        new Alert(Alert.AlertType.ERROR,
                                result.getErrorMessage()
                        ).showAndWait();
                    }
                    drawGameState(game);
                });
                // 5. Add the StackPane (which now holds both) to the row
                row.getChildren().add(cardContainer);
            }
        }
    }
    private void drawTopMenu(HBox row, Game game) {
        //topMenu = new HBox(15, btnChooseOffering, btnAction, btnTop, btnBottom, btnMsg);
        //topMenu.setAlignment(Pos.CENTER);
        //topMenu.setPadding(new Insets(10));

        Label stateLabel = new Label("Game State = " + game.getState() );
        stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
        stateLabel.setTextFill(Color.DARKBLUE);
        row.getChildren().add(stateLabel);
        row.getChildren().add(btnRefresh);
        row.getChildren().add(btnMsg);



    }
    private void drawTopRow(HBox row, Game game) {
        for (Card c : game.getGameBoard().getTopRow()) {
            ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);

            if (cardImage != null) {
                // 1. Create a Label for your debugging text
                Label debugLabel = new Label("ID: " + c.getCardId());

                // 2. Style the label so it's readable over any card background
                // (White text with a semi-transparent black background)
                debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
                        "-fx-text-fill: white; " +
                        "-fx-padding: 3px; " +
                        "-fx-font-weight: bold;");

                // 3. Create a StackPane and add both the image and the text
                StackPane cardContainer = new StackPane();
                cardContainer.getChildren().addAll(cardImage, debugLabel);

                // 4. Align the text wherever you want (e.g., Top-Left corner of the card)
                StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
                // You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
                cardImage.setOnMouseClicked(event -> {
                    logger.info("Card clicked");
                    Player p = game.getCurrentPlayer();
                    //pickTopCardAction(p, game.getGameBoard(), c.getCardId());
                    //game.resolveCardAction(p, true, c.getCardId());

                    ActionResult result = game.resolveCardAction(p, true, c.getCardId());
                    if (!result.isSuccess()) {
                        new Alert(Alert.AlertType.ERROR,
                                result.getErrorMessage()
                        ).showAndWait();
                    }

                    drawGameState(game);
                });
                // 5. Add the StackPane (which now holds both) to the row
                row.getChildren().add(cardContainer);
            }
        }
    }

    private StackPane drawTurnTileCardImageWithPlayers(InputStream imgStream, int height, Player[]  players) {
        try {
            Image fxImage = new Image(imgStream);

            ImageView imageView = new ImageView(fxImage);
            imageView.setFitHeight(height);
            imageView.setPreserveRatio(true);
            imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");

            StackPane stack = new StackPane(imageView);

            VBox playerBox = new VBox(5);
            playerBox.setPadding(new Insets(5));
            playerBox.setAlignment(Pos.CENTER_RIGHT);

            // 🔥 DEBUG background (to SEE the box)
            playerBox.setStyle("-fx-background-color: rgba(255,0,0,0.3);");

            for (Player p : players) {
                if (p==null) continue;
                Rectangle rect = new Rectangle(15,15);
                rect.setFill(p.getTotemColor().getFxColor());
                rect.setStroke(Color.WHITE);
                rect.setStrokeWidth(1);

                playerBox.getChildren().add(rect);
            }
            playerBox.setPadding(new Insets(22));
            // 🔥 IMPORTANT: bring to front
            playerBox.toFront();

            // position it clearly
            StackPane.setAlignment(playerBox, Pos.CENTER);

            stack.getChildren().add(playerBox);

            return stack;

        } catch (Exception e) {
            logger.error("Errore caricamento immagine {}", imgStream, e);
            return null;
        }
    }


    private void drawTurnTileAndOffering(HBox row, int n, Game game) {

        // Clear row if needed (optional but recommended)
        row.getChildren().clear();

        // --- LEFT BLOCK (Turn + initial card) ---
        VBox turnBox = new VBox(5);
        turnBox.setAlignment(Pos.CENTER);

        // Turn label
        int round = game.getRound(); // adjust if different
        Label turnLabel = new Label("Round = " + round);
        turnLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
        turnLabel.setTextFill(Color.DARKBLUE);

        Card first = game.getGameBoard().getCardDeck().getFirstCardForCover();
        if (first!=null){
            ImageView imgCover = createCardImageFromPdf(first.getCardId(), 120, false);
            turnBox.getChildren().addAll(turnLabel, imgCover);
        } else turnBox.getChildren().addAll(turnLabel);

        // Add FIRST
        row.getChildren().add(turnBox);

        // --- TURN TILE ---

        StackPane pane = drawTurnTileCardImageWithPlayers(
                getClass().getResourceAsStream("/files/Start_" + n + "P.png"),
                IMG_HEIGHT,
                game.getGameBoard().getTurnTile().getPositions()
        );

        row.getChildren().add(pane);

        // --- OFFERINGS ---
        for (OfferingTile offering : game.getGameBoard().getOfferingTiles()) {
            Player occupant = offering.getOccupant();
            StackPane offPane =drawOfferingCardImageWithTotem(
                    getClass().getResourceAsStream("/files/offering" + offering.getLetter() + ".png"),
                    IMG_HEIGHT,
                    occupant, game, offering);
            if (offPane != null) {
                row.getChildren().add(offPane);
            }
        }
    }

    // --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
    private void renderPlayers(List<Player> players, Game game) {
        playersArea.getChildren().clear();
        for (Player player : players) {
            boolean active = player.equals(game.getCurrentPlayer());

            VBox playerBox = new VBox(10);
            playerBox.setPadding(new Insets(15));
            playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");

            if (active) {
                // Strong highlight (gold border + soft background)
                playerBox.setStyle(
                        "-fx-border-color: gold;" +
                                "-fx-border-width: 3;" +
                                "-fx-border-radius: 10;" +
                                "-fx-background-color: linear-gradient(to bottom, #fffbe6, #f9f9f9);" +
                                "-fx-background-radius: 10;"
                );

                // Slight scale-up
                playerBox.setScaleX(1.05);
                playerBox.setScaleY(1.05);

                // Glow effect
                DropShadow glow = new DropShadow();
                glow.setColor(Color.GOLD);
                glow.setRadius(20);
                playerBox.setEffect(glow);

                // Smooth pulse animation (NOT blinking)
                Timeline pulse = new Timeline(
                        new KeyFrame(Duration.ZERO,
                                new KeyValue(glow.radiusProperty(), 10)
                        ),
                        new KeyFrame(Duration.seconds(1.2),
                                new KeyValue(glow.radiusProperty(), 25)
                        )
                );
                pulse.setAutoReverse(true);
                pulse.setCycleCount(Animation.INDEFINITE);
                pulse.play();
            }

            HBox nameRow = new HBox(8);
            nameRow.setAlignment(Pos.CENTER_LEFT);

            Rectangle rect = new Rectangle(12, 12);

            TotemColor color = player.getTotemColor();
            rect.setFill(color.getFxColor());


            rect.setStroke(Color.BLACK);
            rect.setStrokeWidth(1);

            Label nameLbl = new Label(player.getNickname());
            nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));

            if (active) {
                Label turnLbl = new Label(" ▶ Your Turn");
                turnLbl.setTextFill(Color.GOLDENROD);
                turnLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
                nameRow.getChildren().addAll(rect, nameLbl, turnLbl);
            } else {
                nameRow.getChildren().addAll(rect, nameLbl);
            }

            // Risorse Cibo e Soldi
            Label statsLbl = new Label("🍖 Food: " + player.getFoodTokens() + "   |   💰 Points: " + player.getPrestigePoints());
            statsLbl.setTextFill(Color.DARKRED);
            statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));

            playerBox.getChildren().addAll(nameRow, statsLbl);

            Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
                    .collect(Collectors.groupingBy(CharacterCard::getCharacterType));

            // Itera sui gruppi creati e genera la grafica
            groupedCards.forEach((type, cardsOfType) -> {
                Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
                typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));

                HBox cardImagesRow = new HBox(5);
                for (Card c : cardsOfType) {
                    // Carte più piccole (90px) per l'area giocatore
                    ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
                    if (img != null) cardImagesRow.getChildren().add(img);
                }

                playerBox.getChildren().addAll(typeLbl, cardImagesRow);
            });

            Map<Era, List<BuildingCard>> groupedBuildCards = player.getPlayerTribe().getBuildingCard().stream()
                    .collect(Collectors.groupingBy(BuildingCard::getEra));

            groupedBuildCards.forEach((type, cardsOfType) -> {
                Label typeLbl = new Label("Build Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
                typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));

                HBox cardImagesRow = new HBox(5);
                for (Card c : cardsOfType) {
                    // Carte più piccole (90px) per l'area giocatore
                    ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
                    if (img != null) cardImagesRow.getChildren().add(img);
                }

                playerBox.getChildren().addAll(typeLbl, cardImagesRow);
                //playerBox.getChildren().addAll(nameRow, statsLbl);
            });
            playersArea.getChildren().add(playerBox);
        }
    }

    private ImageView createCardImageFromPdf(int pageIndex, int height, boolean front) {
        try {
            BufferedImage bim =null;
            if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex-1, 100);
            else bim = pdfRendererBack.renderImageWithDPI(pageIndex-1, 100);

            Image fxImage = SwingFXUtils.toFXImage(bim, null);
            ImageView imageView = new ImageView(fxImage);
            imageView.setFitHeight(height);
            imageView.setPreserveRatio(true);
            imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
            return imageView;
        } catch (IOException e) {
            logger.error("Errore rendering pagina {}", pageIndex, e);
            return null;
        }
    }

    private StackPane drawOfferingCardImageWithTotem(InputStream imagePath, int height, Player occupant, Game game, OfferingTile offering) {
        try {
            Image fxImage = new Image(imagePath);

            ImageView imageView = new ImageView(fxImage);
            imageView.setFitHeight(height);
            imageView.setPreserveRatio(true);
            imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");

            imageView.setOnMouseClicked(event -> {
                int idx = game.getGameBoard().getOfferingTiles().indexOf(offering);
                game.placeTotem(game.getCurrentPlayer(), idx);
                logger.info(" PLAYER {} choose {} ", game.getCurrentPlayer().getNickname() , offering );
                drawGameState(game);
            });

            StackPane stack = new StackPane(imageView);
            if(occupant!=null){
                Rectangle rect = new Rectangle(25, 25);
                rect.setFill(occupant.getTotemColor().getFxColor());
                rect.setStroke(Color.WHITE);
                rect.setStrokeWidth(1);

                StackPane.setAlignment(rect, Pos.TOP_CENTER);
                rect.setTranslateY(20);
                StackPane.setMargin(rect, new Insets(5));

                stack.getChildren().add(rect);
            }

            return stack;
        } catch (Exception e) {
            logger.error("Errore caricamento immagine {}", imagePath, e);
            return null;
        }
    }


    private ImageView createCardImage(InputStream imagePath, int height) {
        try {
            Image fxImage = new Image(imagePath);

            ImageView imageView = new ImageView(fxImage);
            imageView.setFitHeight(height);
            imageView.setPreserveRatio(true);
            imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");

            return imageView;
        } catch (Exception e) {
            logger.error("Errore caricamento immagine {}", imagePath, e);
            return null;
        }
    }

    private void showPopup(String message) {
        Platform.runLater(() -> {

            Label label = new Label("⚡ " + message); // icon improves readability
            label.setWrapText(true);
            label.setMaxWidth(400);

            label.setStyle(
                    "-fx-background-color: rgba(20,20,20,0.9);" +
                            "-fx-text-fill: white;" +
                            "-fx-font-size: 20px;" +
                            "-fx-font-weight: bold;" +
                            "-fx-padding: 15 25 15 25;" +
                            "-fx-background-radius: 12;" +
                            "-fx-border-radius: 12;" +
                            "-fx-border-color: #00c3ff;"
            );

            label.setOpacity(0); // start invisible

            topMenu.getChildren().add(label);

            // Fade IN
            FadeTransition fadeIn = new FadeTransition(Duration.millis(250), label);
            fadeIn.setFromValue(0);
            fadeIn.setToValue(1);

            // Stay visible
            PauseTransition pause = new PauseTransition(Duration.seconds(4));

            // Fade OUT
            FadeTransition fadeOut = new FadeTransition(Duration.millis(400), label);
            fadeOut.setFromValue(1.0);
            fadeOut.setToValue(0.0);

            fadeOut.setOnFinished(f -> topMenu.getChildren().remove(label));

            fadeIn.play();
            fadeIn.setOnFinished(e -> pause.play());
            pause.setOnFinished(e -> fadeOut.play());
        });
    }
    private void showPopupNo(String message) {
        Platform.runLater(() -> {
            Label label = new Label(message);
            label.setStyle(
                    "-fx-background-color: rgba(0,0,0,0.75);" +
                            "-fx-text-fill: white;" +
                            "-fx-font-size: 28px;" +
                            "-fx-font-weight: bold;" +
                            "-fx-padding: 20 40 20 40;" +
                            "-fx-background-radius: 12;"
            );
            label.setMouseTransparent(true);

            topMenu.getChildren().add(label);

            PauseTransition pause = new PauseTransition(Duration.seconds(5));
            pause.setOnFinished(e -> {
                FadeTransition fade = new FadeTransition(Duration.millis(400), label);
                fade.setFromValue(1.0);
                fade.setToValue(0.0);
                fade.setOnFinished(f -> topMenu.getChildren().remove(label));
                fade.play();
            });
            pause.play();
        });
    }
    @Override
    public void stop() throws Exception {
        if (documentFront != null) documentFront.close();
        super.stop();
    }

    public static void main(String[] args) {
        launch(args);
    }
}