from .apikey import apikey_generator

class validator:
    """
    This is base validator class. It implements base validator mechanism, 
    and other validators must implements it.
    """

    def __init__(self, target: str) -> None:
        """
        This create new validator, from string. 
        
        Parameters:
            target (str): Content to validate
        """

        self.__target = target
        self._failed()

    @property
    def target(self) -> str:
        """ This return content to validate. """

        return self.__target

    def _failed(self) -> None:
        """
        This would mark content as invalid. 
        """

        self.__result = False

    def _check_all(self) -> None:
        """
        This would check all marks of the content.
        It must be overwrite by target validator.
        """

        pass

    def _contain(self, *args) -> bool:
        """
        This check that target contain any given phrase or letter.

        Args:
            (str): Phrase to check that content contain it

        Returns:
            (bool): True when any of given phrase is in the target
        """

        for letter in args:
            if self.__target.find(letter) != -1:
                return True
        
        return False

    @property
    def result(self) -> bool:
        """ Validate content, True when valid or False when not. """

        self.__result = True
        self._check_all()

        return self.__result

    @property
    def invalid(self) -> bool:
        """ Validate, and return True when target is invalid. """
        
        return not self.result

    @property
    def valid(self) -> bool:
        """ It validate target, and return True when valid. """

        return self.result

class nick_validator(validator):
    """
    This validate nick. It is slow, and best to use only when loading
    users from file.
    """

    def _check_all(self) -> None:
        
        # Minimum 4 characters
        if len(self.target) < 4:
            return self._failed()

        # Maximum 30 characters
        if len(self.target) > 30:
            return self._failed()

        # Could contain only 
        for letter in self.target:
            
            # Letters
            if letter.isalpha():
                continue

            # Numbers
            if letter.isnumeric():
                continue

            return self._failed()

class password_validator(validator):
    """
    This validate password. It is slow, and best to use only when
    loading users from file.
    """

    def _check_all(self) -> None:

        # Minimum 8 characters
        if len(self.target) < 8:
            return self._failed()

        # Maximum 40 characters
        if len(self.target) > 40:
            return self._failed()

        # Can not contain white chars
        for letter in self.target:
            if letter.isspace():
                return self._failed()

class apikey_validator(validator):
    """ This is simple ApiKey validator """

    def _check_all(self) -> None:
        
        # ApiKey must contain proof size 
        if len(self.target) != apikey_generator.size() * 2:
            return self._failed()

class name_validator(validator):
    """
    This is validator for product name. It check that it is not blank,
    and it not contain white chars before and after content. It must have 
    more or qeual of 4 and less than 40 characters.
    """

    def _check_all(self) -> None:

        # Minimum 4 characters
        if len(self.target) < 4:
            return self._failed()

        # Maximum 40 characters
        if len(self.target) > 40:
            return self._failed()

        # Must be trimed
        if len(self.target.strip()) != len(self.target):
            return self._failed()

class description_validator(validator):
    """
    This is validator for product description. It could be blank, but can not
    have white chars before and after content (must be trimmed), and coud has 
    only 500 chars.
    """

    def _check_all(self) -> None:

        # Could be blank
        if len(self.target) == 0:
            return

        # Must be trimmed
        if len(self.target.strip()) != len(self.target):
            return self._failed()

        # Must has max 500 characters.
        if len(self.target) > 500:
            return self._failed()

class barcode_validator(validator):
    """
    This is barcode validator. Barcode must be string, which contain number
    with 8, 12 or 13 digits. That is standard EAN.
    """

    def _check_all(self) -> None:

        # Must has only digits
        if not self.target.isdigit():
            return self._failed()

        lenght = len(self.target)

        # Must has 8, 12 or 13 chars
        if lenght != 8 and lenght != 12 and lenght != 13:
            return self._failed()

class author_validator(validator):
    """
    This validate author name. Author can not have less than 3 characters,
    and can not have more than 40 characters. It also must be trimmed.
    """

    def _check_all(self) -> None:

        # Must be trimmed
        if len(self.target) != len(self.target.strip()):
            return self._failed()

        # Must have more than 3 characters
        if len(self.target) < 3:
            return self._failed()

        # Must has max 40 characters
        if len(self.target) > 40:
            return self._failed()

class phone_number_validator(validator):
    """
    This is phone number validator. Phone number must be in format
    like +CC XXXXXXXXX. This is standard of E.123 and E.164.
    """

    def _check_all(self) -> None:
        
        # Separate country code and number
        splited = self.target.split(" ")

        if len(splited) != 2:
            return self._failed()

        # Get it
        country_code = splited[0]
        number = splited[1]
        
        # Must start with "+"
        if country_code[0] != "+":
            return self._failed()

        country_code = country_code[1:]
        
        # Country code must be numeric
        if not country_code.isnumeric():
            return self._failed()
        
        # Number of course must be numeric
        if not number.isnumeric():
            return self._failed()

        # Country code length must be <1;3>
        if len(country_code) == 0 or len(country_code) > 3:
            return self._failed()

        # All number must have less than 16
        if len(country_code) + len(number) > 15:
            return self._failed()

        # And number mast has minimum 7 length
        if len(number) < 7:
            return self._failed()

class email_validator(validator):
    """
    This is email validator. This validate email address in the standard
    format.
    """

    def _check_all(self) -> None:
        
        # Split name and domain
        splited = self.target.split("@")
        
        if len(splited) != 2:
            return self._failed()

        # Gat it
        name = splited[0]
        domain = splited[1]
        
        # Name must be in <1;128>
        if len(name) < 1 or len(name) > 128:
            return self._failed()

        # Domain can not has ".."
        if domain.find("..") != -1:
            return self._failed()

        splited = domain.split(".")
        
        # Must has more or equal 2 parts in domain
        if len(splited) < 2:
            return self._failed()

        # All parts must be in <1;128>
        for part in splited:
            if len(part) < 1 or len(part) > 128:
                return self._failed()
