package com.lona.tictactoe.client;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main extends JFrame {
    private Socket socket;
    private PrintWriter out;
    private BufferedReader in;

    private CardLayout cardLayout = new CardLayout();
    private JPanel mainPanel = new JPanel(cardLayout);

    // Menu Components
    private JTextField joinCodeField = new JTextField(10);

    // Game Components
    private JButton[] buttons = new JButton[9];
    private JLabel statusLabel = new JLabel("Connecting...");
    private JLabel codeLabel = new JLabel("Code: -");
    private boolean myTurn = false;
    private char mySymbol = ' '; // assigned by server implied turn

    public static void main(String[] args) {
        String host = "38.242.130.81";
        int port = 1870;

        if (args.length > 0) {
            host = args[0];
        }
        if (args.length > 1) {
            try {
                port = Integer.parseInt(args[1]);
            } catch (NumberFormatException e) {
                System.err.println("Invalid port number provided, using default 1870");
            }
        }

        final String serverHost = host;
        final int serverPort = port;

        System.out.println("Starting TicTacToe Client...");
        System.out.println("Target Server: " + serverHost + ":" + serverPort);

        SwingUtilities.invokeLater(() -> {
            try {
                Main client = new Main(serverHost, serverPort);
                client.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("Failed to start GUI: " + e.getMessage());
            }
        });
    }

    // Instance variables
    private final String serverHost;
    private final int serverPort;

    public Main(String host, int port) {
        super("TicTacToe Client");
        this.serverHost = host;
        this.serverPort = port;

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 500);
        setLocationRelativeTo(null);

        try {
            initUI();
            setContentPane(mainPanel);
            connectToServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initUI() {
        // --- MENU PANEL ---
        JPanel menuPanel = new JPanel(new GridBagLayout());
        JButton createBtn = new JButton("Create Game");
        JButton joinBtn = new JButton("Join Game");

        createBtn.addActionListener(e -> send("CREATE"));
        joinBtn.addActionListener(e -> {
            String code = joinCodeField.getText().trim();
            if (!code.isEmpty())
                send("JOIN " + code);
        });

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.gridx = 0;
        gbc.gridy = 0;
        menuPanel.add(createBtn, gbc);

        gbc.gridy = 1;
        menuPanel.add(new JLabel("Enter Code:"), gbc);
        gbc.gridy = 2;
        menuPanel.add(joinCodeField, gbc);
        gbc.gridy = 3;
        menuPanel.add(joinBtn, gbc);

        // --- GAME PANEL ---
        JPanel gamePanel = new JPanel(new BorderLayout());

        JPanel topPanel = new JPanel(new GridLayout(2, 1));
        topPanel.add(statusLabel);
        topPanel.add(codeLabel);
        gamePanel.add(topPanel, BorderLayout.NORTH);

        JPanel boardPanel = new JPanel(new GridLayout(3, 3));
        for (int i = 0; i < 9; i++) {
            int finalI = i;
            buttons[i] = new JButton("");
            buttons[i].setFont(new Font("Arial", Font.BOLD, 40));
            buttons[i].setFocusPainted(false);
            buttons[i].addActionListener(e -> {
                int r = finalI / 3;
                int c = finalI % 3;
                send("MOVE " + r + " " + c);
            });
            boardPanel.add(buttons[i]);
        }
        gamePanel.add(boardPanel, BorderLayout.CENTER);

        // --- BUTTONS PANEL ---
        JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 10, 0));

        JButton leaveBtn = new JButton("Leave Lobby");
        leaveBtn.setBackground(Color.GRAY);
        leaveBtn.setForeground(Color.WHITE);
        leaveBtn.addActionListener(e -> {
            send("LEAVE");
            resetGame();
        });

        JButton surrenderBtn = new JButton("Surrender");
        surrenderBtn.setBackground(new Color(220, 53, 69)); // Bootstrap Danger Red
        surrenderBtn.setForeground(Color.WHITE);
        surrenderBtn.addActionListener(e -> send("SURRENDER"));

        buttonPanel.add(leaveBtn);
        buttonPanel.add(surrenderBtn);

        gamePanel.add(buttonPanel, BorderLayout.SOUTH);

        mainPanel.add(menuPanel, "MENU");
        mainPanel.add(gamePanel, "GAME");
    }

    // AI Configuration
    private static final String API_KEY = "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";
    private static final String MODEL = "arcee-ai/trinity-large-preview:free";
    private char[] boardState = new char[9]; // Keep track of board state

    private void connectToServer() {
        new Thread(() -> {
            try {
                System.out.println("Attempting to connect to " + serverHost + ":" + serverPort);
                socket = new Socket(serverHost, serverPort);
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                System.out.println("Connected successfully!");
                SwingUtilities.invokeLater(() -> statusLabel.setText("Connected to Server."));

                String line;
                while ((line = in.readLine()) != null) {
                    System.out.println("Received: " + line); // Log received messages
                    processMessage(line);
                }
            } catch (IOException e) {
                System.err.println("Connection Error: " + e.getMessage());
                SwingUtilities.invokeLater(() -> {
                    statusLabel.setText("Connection Failed.");
                    JOptionPane.showMessageDialog(this, "Connection Error: " + e.getMessage());
                    // Don't reset to menu if we can't connect, let user see error
                });
            }
        }).start();
    }

    private void send(String msg) {
        if (out != null)
            out.println(msg);
    }

    private void processMessage(String msg) {
        SwingUtilities.invokeLater(() -> handleMessage(msg));
    }

    private void handleMessage(String msg) {
        String[] parts = msg.split(" ");
        String cmd = parts[0];

        switch (cmd) {
            case "GAME_CREATED":
                cardLayout.show(mainPanel, "GAME");
                if (parts.length > 1) {
                    codeLabel.setText("Code: " + parts[1]);
                    mySymbol = 'X'; // Creator starts as X usually
                    statusLabel.setText("Game Created. Waiting for opponent...");
                }
                break;
            case "JOIN_SUCCESS":
                cardLayout.show(mainPanel, "GAME");
                mySymbol = 'O'; // Joiner is O
                statusLabel.setText("Joined Game.");
                codeLabel.setText("Code: " + joinCodeField.getText());
                break;
            case "START":
                statusLabel.setText("Game Started! X goes first.");
                break;
            case "RESTART":
                for (int i = 0; i < 9; i++) {
                    buttons[i].setText("");
                    boardState[i] = ' ';
                }
                statusLabel.setText("Game Restarted! X goes first.");
                break;
            case "BOARD": // BOARD X O X ...
                String boardStr = msg.substring(6); // remove "BOARD "
                for (int i = 0; i < 9 && i < boardStr.length(); i++) {
                    char c = boardStr.charAt(i);
                    boardState[i] = c;
                    buttons[i].setText(String.valueOf(c));
                }
                break;
            case "TURN":
                if (parts.length > 1) {
                    char turn = parts[1].charAt(0);
                    if (turn == mySymbol) {
                        statusLabel.setText("AI Turn (" + mySymbol + ")... Thinking...");
                        makeAiMove();
                    } else {
                        statusLabel.setText("Opponent's Turn (" + turn + ")");
                    }
                }
                break;
            case "WIN":
                String winner = msg.substring(4);
                statusLabel.setText("Winner: " + winner);
                // Auto RESTART after 3 seconds
                new Thread(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                    }
                    send("RESTART");
                }).start();
                break;
            case "DRAW":
                statusLabel.setText("Draw!");
                // Auto RESTART after 3 seconds
                new Thread(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                    }
                    send("RESTART");
                }).start();
                break;
            case "OPPONENT_LEFT":
                JOptionPane.showMessageDialog(this, "Opponent left the game.");
                resetGame();
                break;
            case "ERROR:":
                if (msg.contains("It is not your turn")) {
                    statusLabel.setText("Not your turn!");
                } else {
                    // Start thinking again if move was invalid?
                    if (msg.contains("Invalid") || msg.contains("occupied")) {
                        makeAiMove(); // Retry
                    }
                    System.err.println("Server Error: " + msg);
                }
                break;
        }
    }

    private void makeAiMove() {
        new Thread(() -> {
            try {
                Thread.sleep(1500); // Slightly faster

                // 1. Try Simple Rule-Based / Minimax first (guarantees better play)
                // If we want "AI" feel, we can use LLM, but for "winning", logic is better.
                // Let's mix them. Use LLM but with VERY specific instructions,
                // and if it fails or returns invalid, fall back to smart move.

                String prompt = buildPrompt();
                System.out.println("Sending Prompt to AI...");

                String response = callOpenRouter(prompt);
                System.out.println("AI Response: " + response);

                String[] coords = parseCoordinates(response);
                if (coords != null) {
                    int r = Integer.parseInt(coords[0]);
                    int c = Integer.parseInt(coords[1]);
                    // Validate move locally before sending
                    if (isValidMove(r, c)) {
                        send("MOVE " + r + " " + c);
                    } else {
                        System.err.println("AI suggested invalid move. Using Minimax fallback.");
                        makeSmartMove();
                    }
                } else {
                    System.err.println("Failed to parse AI move. Using Minimax fallback.");
                    makeSmartMove();
                }

            } catch (Exception e) {
                e.printStackTrace();
                makeSmartMove();
            }
        }).start();
    }

    private boolean isValidMove(int r, int c) {
        if (r < 0 || r > 2 || c < 0 || c > 2)
            return false;
        return boardState[r * 3 + c] == ' ';
    }

    private void makeSmartMove() {
        // Simple Minimax or Rule-based
        int bestScore = Integer.MIN_VALUE;
        int move = -1;

        // Convert boardState to something easier
        char[] board = boardState.clone();

        for (int i = 0; i < 9; i++) {
            if (board[i] == ' ') {
                board[i] = mySymbol;
                int score = minimax(board, 0, false);
                board[i] = ' ';
                if (score > bestScore) {
                    bestScore = score;
                    move = i;
                }
            }
        }

        if (move != -1) {
            int r = move / 3;
            int c = move % 3;
            send("MOVE " + r + " " + c);
        } else {
            makeRandomMove();
        }
    }

    private char getOpponentSymbol() {
        return (mySymbol == 'X') ? 'O' : 'X';
    }

    private int minimax(char[] board, int depth, boolean isMaximizing) {
        char result = checkWinner(board);
        if (result == mySymbol)
            return 10 - depth;
        if (result == getOpponentSymbol())
            return depth - 10;
        if (result == 'D')
            return 0; // Draw

        if (isMaximizing) {
            int bestScore = Integer.MIN_VALUE;
            for (int i = 0; i < 9; i++) {
                if (board[i] == ' ') {
                    board[i] = mySymbol;
                    int score = minimax(board, depth + 1, false);
                    board[i] = ' ';
                    bestScore = Math.max(score, bestScore);
                }
            }
            return bestScore;
        } else {
            int bestScore = Integer.MAX_VALUE;
            for (int i = 0; i < 9; i++) {
                if (board[i] == ' ') {
                    board[i] = getOpponentSymbol();
                    int score = minimax(board, depth + 1, true);
                    board[i] = ' ';
                    bestScore = Math.min(score, bestScore);
                }
            }
            return bestScore;
        }
    }

    private char checkWinner(char[] b) {
        int[][] wins = {
                { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, // rows
                { 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, // cols
                { 0, 4, 8 }, { 2, 4, 6 } // diags
        };
        for (int[] w : wins) {
            if (b[w[0]] != ' ' && b[w[0]] == b[w[1]] && b[w[1]] == b[w[2]]) {
                return b[w[0]];
            }
        }
        for (int i = 0; i < 9; i++)
            if (b[i] == ' ')
                return ' '; // Not finished
        return 'D'; // Draw
    }

    private void makeRandomMove() {
        for (int i = 0; i < 9; i++) {
            if (boardState[i] == ' ') {
                int r = i / 3;
                int c = i % 3;
                send("MOVE " + r + " " + c);
                return;
            }
        }
    }

    private String buildPrompt() {
        StringBuilder sb = new StringBuilder();
        sb.append("You are an unbeatable Tic-Tac-Toe AI. You are player '").append(mySymbol).append("'.\n");
        sb.append("Current Board State (row, col):\n");
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                char c = boardState[i * 3 + j];
                sb.append("[").append(i).append(",").append(j).append("]=").append(c == ' ' ? "EMPTY" : c).append("  ");
            }
            sb.append("\n");
        }

        // Threat Detection
        char opponent = (mySymbol == 'X') ? 'O' : 'X';
        String threats = findThreats(opponent);
        if (!threats.isEmpty()) {
            sb.append("\n!!! CRITICAL WARNING !!!\n");
            sb.append(threats);
            sb.append("You MUST block this threat immediately or you will lose.\n");
        }

        sb.append(
                "\nYour goal is to WIN. If you cannot win immediately, BLOCK the opponent. If neither, play optimally to force a draw.\n");
        sb.append("Analyze the board carefully. Don't make random moves.\n");
        sb.append("Respond with JSON ONLY: {\"row\": <row>, \"col\": <col>}");
        return sb.toString();
    }

    private String findThreats(char opponent) {
        int[][] wins = {
                { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, // rows
                { 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, // cols
                { 0, 4, 8 }, { 2, 4, 6 } // diags
        };
        StringBuilder threatMsg = new StringBuilder();
        for (int[] w : wins) {
            // Check if 2 cells are opponent and 1 is empty
            int opCount = 0;
            int emptyCount = 0;
            int emptyIndex = -1;

            for (int idx : w) {
                if (boardState[idx] == opponent)
                    opCount++;
                else if (boardState[idx] == ' ') {
                    emptyCount++;
                    emptyIndex = idx;
                }
            }

            if (opCount == 2 && emptyCount == 1) {
                int r = emptyIndex / 3;
                int c = emptyIndex % 3;
                threatMsg.append("Opponent '").append(opponent).append("' is about to win at row ").append(r)
                        .append(", col ").append(c).append(".\n");
            }
        }
        return threatMsg.toString();
    }

    private String callOpenRouter(String prompt) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        String jsonBody = "{"
                + "\"model\": \"" + MODEL + "\","
                + "\"messages\": [{\"role\": \"user\", \"content\": \""
                + prompt.replace("\n", "\\n").replace("\"", "\\\"") + "\"}],"
                + "\"temperature\": 0.2" // Lower temperature for more deterministic/structured output
                + "}";

        HttpRequest request = HttpRequest.newBuilder()
                .uri(java.net.URI.create("https://openrouter.ai/api/v1/chat/completions"))
                .header("Authorization", "Bearer " + API_KEY)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        String body = response.body();
        // System.out.println("DEBUG RAW BODY: " + body); // excessive logging, but
        // helpful if needed

        // Parse content
        int contentIndex = body.indexOf("\"content\":");
        if (contentIndex != -1) {
            int start = body.indexOf("\"", contentIndex + 10) + 1;
            // Find the end quote, handling escaped quotes
            int end = start;
            while (true) {
                end = body.indexOf("\"", end + 1);
                if (body.charAt(end - 1) != '\\') {
                    break;
                }
            }
            String content = body.substring(start, end);
            return content.replace("\\n", "\n").replace("\\\"", "\"");
        }
        return "";
    }

    private String[] parseCoordinates(String text) {
        // Look for JSON pattern first: "row": 1, "col": 2
        Pattern jsonPattern = Pattern.compile("\"row\"\\s*:\\s*(\\d)\\s*,\\s*\"col\"\\s*:\\s*(\\d)");
        Matcher m = jsonPattern.matcher(text);
        if (m.find()) {
            return new String[] { m.group(1), m.group(2) };
        }

        // Fallback: strictly row col numbers
        m = Pattern.compile("(\\d)\\s+(\\d)").matcher(text);
        if (m.find()) {
            return new String[] { m.group(1), m.group(2) };
        }

        // One last fallback: just find any two digits
        m = Pattern.compile("(\\d)[^\\d]+(\\d)").matcher(text);
        if (m.find()) {
            return new String[] { m.group(1), m.group(2) };
        }

        return null;
    }

    private void resetGame() {
        cardLayout.show(mainPanel, "MENU");
        for (JButton btn : buttons)
            btn.setText("");
        for (int i = 0; i < 9; i++)
            boardState[i] = ' ';
        statusLabel.setText("Connected.");
        codeLabel.setText("Code: -");
    }
}
