import random


class Mastermind:
    """
    Class to represent a Mastermind game.
    Mastermind is a game where the player has to guess a secret code consisting of 4 numbers between 1 and 6.
    The player has 10 chances to guess the code and wins if he can guess the code within 10 chances.
    Each time the player makes a guess, the game will return a tuple of 2 numbers between 0 and 4.
    The hints are as follows:
    - The first digit indicates the number of correct numbers in the correct position
    - The second digit indicates the number of correct numbers in the wrong position
    Note : If there are duplicate colors in the guess, they cannot all get a hint unless
    they correspond to the same number of duplicate colors in the hidden code.
    """

    def __init__(self, chances: int = 10, nb_digits: int = 4, nb_colors: int = 6):
        """
        Initialize the Mastermind game with a secret code.
        :param chances: int, the number of chances the player has to guess the code. Default is 10.
        :param nb_digits: int, the number of digits in the code. Default is 4.
        :param nb_colors: int, the number of colors (or digits) that can be used in the code. Default is 6.
        """
        self.__nb_digits__ = nb_digits
        self.__nb_colors__ = nb_colors
        self.__code__ = [random.randint(1, nb_colors) for _ in range(nb_digits)]
        self.__chances__ = chances

    def set_code(self, code: list[int]):
        """
        Set the secret code. This method is useful for testing purposes.
        It calls the check_code method to validate the code.
        :param code: list[int], the secret code
        :raise ValueError: if the code is invalid
        """
        if self.check_code(code):
            self.__code__ = code
        else:
            raise ValueError(
                f"Invalid code. Please enter {self.__nb_digits__} numbers between 1 and {game.__nb_colors__}.")

    def check_code(self, code: list[int]) -> bool:
        """
        Check if the code is valid.
        :param code: list[int], the code to be checked
        :return: bool, True if the code is valid, False otherwise
        """
        return True

    @staticmethod
    def provide_hints(code: list[int], guess: list[int]) -> tuple[int, int]:
        """
        Each time the player makes a guess, the game will return a couple of numbers between 0 and 4.
        The hints are as follows:
        - The first digit indicates the number of correct numbers in the correct position
        - The second digit indicates the number of correct numbers in the wrong position
        Note : If there are duplicate colors in the guess, they cannot all get a hint unless
        they correspond to the same number of duplicate colors in the hidden code.
        :param code: list[int], the secret code
        :param guess: list[int], the player's guess
        :return: tuple[int, int], the hints
        """
        return 2, 0

    def guess(self, guess: list[int]) -> tuple[bool, int, int]:
        """
        Make a guess and return the hints.
        :param guess: list[int], the guess
        :return: tuple[bool, int, int], the result of the guess and the hints
        """
        if not self.has_remaining_attempts():
            print("Game over! You have run out of chances.")
            return False, 0, 0
        if not self.check_code(guess):
            print(f"Invalid guess. Please enter {game.__nb_digits__} numbers between 1 and {game.__nb_colors__}.")
            return False, 0, 0
        self.__chances__ -= 1
        nb_correct, nb_misplaced = self.provide_hints(self.__code__, guess)
        success = self.is_right_answer((nb_correct, nb_misplaced))
        return success, nb_correct, nb_misplaced

    def is_right_answer(self, hints: tuple[int, int]) -> bool:
        """
        Check if the guess is the right answer.
        Instead of checking the guess, the hints are provided as input.
        :return: bool, True if the hints are (4,0), False otherwise
        """
        return True

    def has_remaining_attempts(self) -> bool:
        """
        Check if the player has remaining attempts.
        :return: bool, True if the player has remaining attempts, False otherwise
        """
        return True


def convert(word: str) -> list[int]:
    """
    Convert a string to a list of integers.
    In the string, each integer is separated by a space.
    :param word: str, the string to be converted
    :return: list[int], the list of integers
    """
    try:
        return list(map(int, word.split()))
    except ValueError:
        raise ValueError(
            f"Invalid input. Please enter {game.__nb_digits__} numbers between 1 and {game.__nb_colors__}.")


if __name__ == '__main__':
    game = Mastermind()
    # Print a welcome message and instructions
    print("Welcome to Mastermind!")
    print(f"You have {game.__chances__} chances to guess the secret code.")
    print(f"The code consists of {game.__nb_digits__} numbers between 1 and {game.__nb_colors__}.")
    print("After each guess, you will receive hints:")
    print("- The first digit indicates the number of correct numbers in the correct position.")
    print("- The second digit indicates the number of correct numbers in the wrong position.")
    print("Enter your guess as a sequence of numbers separated by spaces.\n")
    print(f"Let's start!")

    # read the guess from the player
    while game.has_remaining_attempts():
        user_guess = input("Please enter your guess: ")
        # convert the guess to a list of integers
        user_guess = convert(user_guess)
        # call the guess method and print the hints
        won, correct, misplaced = game.guess(user_guess)
        if won:
            print("Congratulations! You have guessed the code.")
            break
        else:
            print(f"Hints: {correct, misplaced}")
            print(f"You have {game.__chances__} chances left.")
    else:
        print("Game over! You have run out of chances.")
        print(f"The secret code was: {game.__code__}")
