Разбей на отдельные классы не теряя функциональность и ничего не меняя package backend.academy;

import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Scanner;


public class HangmanGame {
    private final PrintStream printStream;
    private final WordList wordList;
    private final HangmanVisualizer visualizer;
    public Scanner scanner;
    public String secretWord;
    public String hint;
    public char[] guessedWord;
    public int attemptsLeft;
    public int maxAttempts;
    public String difficultyLevel;
    public String category;
    public List<Character> availableLetters;
    private boolean hintUsed = false;

    public HangmanGame(InputStream inputStream, PrintStream printStream) {
        this.printStream = printStream;
        this.wordList = new WordList();
        this.visualizer = new HangmanVisualizer();
        this.scanner = new Scanner(inputStream);
    }

    public void start() {
        printFramedMessage("Добро пожаловать в игру 'Виселица'!");

        selectCategory();
        selectDifficultyLevel();
        selectSecretWord();
        initializeAvailableLetters();

        this.maxAttempts = visualizer.getStagesCount() - 1;
        this.attemptsLeft = maxAttempts;
        this.guessedWord = new char[secretWord.length()];
        Arrays.fill(guessedWord, '_');

        gameLoop();
    }

    public void selectCategory() {
        String vivWord = "Случайная категория выбрана: ";
        printStream.println("Выберите категорию или нажмите Enter для случайной категории:");
        String[] categories = wordList.getCategories();
        for (int i = 0; i < categories.length; i++) {
            printStream.println((i + 1) + ". " + categories[i]);
        }
        String input = scanner.nextLine();
        if (input.isEmpty()) {
            Random rand = new Random();
            int index = rand.nextInt(categories.length);
            category = categories[index];
            printStream.println(vivWord + category);
        } else {
            try {
                int choice = Integer.parseInt(input);
                if (choice >= 1 && choice <= categories.length) {
                    category = categories[choice - 1];
                    printStream.println("Вы выбрали категорию: " + category);
                } else {
                    printStream.println("Неверный выбор. Будет выбрана случайная категория.");
                    Random rand = new Random();
                    int index = rand.nextInt(categories.length);
                    category = categories[index];
                    printStream.println(vivWord + category);
                }
            } catch (NumberFormatException e) {
                printStream.println("Неверный ввод. Будет выбрана случайная категория.");
                Random rand = new Random();
                int index = rand.nextInt(categories.length);
                category = categories[index];
                printStream.println(vivWord + category);
            }
        }
    }

    public void selectDifficultyLevel() {
        String vivWord = "Случайный уровень сложности выбран: ";
        printStream.println("Выберите уровень сложности или нажмите Enter для случайного уровня:");
        String[] levels = {"Легкий", "Средний", "Сложный"};
        for (int i = 0; i < levels.length; i++) {
            printStream.println((i + 1) + ". " + levels[i]);
        }
        String input = scanner.nextLine();
        if (input.isEmpty()) {
            Random rand = new Random();
            int index = rand.nextInt(levels.length);
            difficultyLevel = levels[index];
            printStream.println(vivWord + difficultyLevel);
        } else {
            try {
                int choice = Integer.parseInt(input);
                if (choice >= 1 && choice <= levels.length) {
                    difficultyLevel = levels[choice - 1];
                    printStream.println("Вы выбрали уровень сложности: " + difficultyLevel);
                } else {
                    printStream.println("Неверный выбор. Будет выбран случайный уровень сложности.");
                    Random rand = new Random();
                    int index = rand.nextInt(levels.length);
                    difficultyLevel = levels[index];
                    printStream.println(vivWord + difficultyLevel);
                }
            } catch (NumberFormatException e) {
                printStream.println("Неверный ввод. Будет выбран случайный уровень сложности.");
                Random rand = new Random();
                int index = rand.nextInt(levels.length);
                difficultyLevel = levels[index];
                printStream.println(vivWord + difficultyLevel);
            }
        }
    }

    public void selectSecretWord() {
        Word wordObj = wordList.getRandomWord(category, difficultyLevel);
        this.secretWord = wordObj.getWord().toLowerCase();
        this.hint = wordObj.getHint();

        if (this.secretWord.isEmpty()) {
            throw new IllegalArgumentException("Слово не может быть пустым");
        }
    }

    public void initializeAvailableLetters() {
        availableLetters = new ArrayList<>();
        for (char c = 'а'; c <= 'я'; c++) {
            availableLetters.add(c);
        }
    }

    public void displayAvailableLetters() {
        printStream.print("Доступные буквы: ");
        for (char c : availableLetters) {
            printStream.print(c + " ");
        }
        printStream.println();
    }

    public void updateAvailableLetters(char guess) {
        availableLetters.remove(Character.valueOf(guess));
    }

    public void gameLoop() {
        while (attemptsLeft > 0) {
            printFramedMessage("Слово: " + String.valueOf(guessedWord));
            displayAvailableLetters();
            printStream.println("Осталось попыток: " + attemptsLeft);
            printStream.println(visualizer.getHangmanDrawing(maxAttempts - attemptsLeft));

            if (!hintUsed) {
                printStream.print("Введите букву или слово 'подсказка' для получения подсказки: ");
            } else {
                printStream.print("Введите букву: ");
            }
            String input = scanner.nextLine().toLowerCase();

            if (input.equals("подсказка") && !hintUsed) {
                displayHint();
                hintUsed = true;
                continue;
            }

            if (input.isEmpty()) {
                printStream.println("Вы ничего не ввели. Попробуйте еще раз.");
                continue;
            }

            if (input.length() > 1) {
                printStream.println("Пожалуйста, введите одну букву.");
                continue;
            }

            char guess = input.charAt(0);
            if (!Character.isLetter(guess) || !availableLetters.contains(guess)) {
                printStream.println("Пожалуйста, введите доступную букву.");
                continue;
            }

            updateAvailableLetters(guess);

            if (updateGuessedWord(guess)) {
                printStream.println("Верно!");
            } else {
                printStream.println("Неверно!");
                attemptsLeft--;

                if (attemptsLeft == 1 && !hintUsed) {
                    printStream.println("У вас осталась последняя попытка. Хотите использовать подсказку? (да/нет)");
                    String hintInput = scanner.nextLine().toLowerCase();
                    if (hintInput.equals("да")) {
                        displayHint();
                        hintUsed = true;
                    }
                }
            }

            if (String.valueOf(guessedWord).equals(secretWord)) {
                printFramedMessage("Поздравляем! Вы угадали слово: " + secretWord.toUpperCase());
                return;
            }
        }
        printStream.println(visualizer.getHangmanDrawing(maxAttempts));
        printFramedMessage("Вы проиграли! Загаданное слово было: " + secretWord.toUpperCase());
    }

    public boolean updateGuessedWord(char guess) {
        boolean correct = false;
        for (int i = 0; i < secretWord.length(); i++) {
            if (secretWord.charAt(i) == guess) {
                guessedWord[i] = guess;
                correct = true;
            }
        }
        return correct;
    }

    public void displayHint() {
        printFramedMessage("Подсказка: " + hint);
    }

    private void printFramedMessage(String message) {
        String border = "╔" + "═".repeat(message.length() + 2) + "╗";
        String emptyLine = "║" + " ".repeat(message.length() + 2) + "║";
        String msgLine = "║ " + message + " ║";
        String bottom = "╚" + "═".repeat(message.length() + 2) + "╝";
        printStream.println(border);
        printStream.println(emptyLine);
        printStream.println(msgLine);
        printStream.println(emptyLine);
        printStream.println(bottom);
    }
}