from .apikey import apikey_generator
from .exception import config_exception
from .exception import not_ready_exception
from .exception import validator_exception
from .validator import nick_validator
from .validator import apikey_validator
from .validator import password_validator

class user:
    """
    This represents user in the system. It would be stored in the config 
    file. Creating new must be done by builders, which checks content with
    validators. User class is immutable.
    """

    def __init__(self, nick: str, password: str, apikey: str) -> None:
        """
        This create new user, and require all parameters of it.

        Parameters:
            nick (str): Nick of the user
            password (str): Password of the user
            apikey (str): ApiKey of the user
        """

        self.__nick = nick
        self.__password = password
        self.__apikey = apikey

    @property
    def nick(self) -> str:
        """ It returns nick of the user. """

        return self.__nick

    @property
    def password(self) -> str:
        """ It returns password of the user. """

        return self.__password

    @property
    def apikey(self) -> str:
        """ It returns apikey of the user. """

        return self.__apikey

    @property
    def flat(self) -> dict:
        return {
            "nick": self.nick,
            "password": self.password,
            "apikey": self.apikey
        }

    def __str__(self) -> str:
        """ 
        This create dump of the user.

        Returns:
            (str): Dump of the user
        """

        target = "User:\n"
        target = target + "Nick: \"" + str(self.nick) + "\"\n"
        target = target + "Password: \"" + str(self.password) + "\"\n"
        target = target + "ApiKey: \"" + str(self.apikey) + "\"\n"

        return target

class user_factory:
    """ 
    This class is responsible for generating new users. It make avairable
    creating user step by step, and it is responsible for generating ApiKey.
    """

    def __init__(self, target: user | None = None) -> None:
        """
        This create new user factory. When user had been provided, then it is
        used to initialize nick, ApiKey and password of the factory.

        Parameters
            target (user | None): User to initialize factory (default = None)
        """

        self.__nick = None
        self.__password = None
        self.__apikey = None
        self.__result = None

        if target is not None:
            self.__nick = target.nick
            self.__password = target.password
            self.__apikey = target.apikey

    @property
    def apikey(self) -> str:
        """ This return factory ApiKey. """

        if self.__apikey is None:
            self.__apikey = apikey_generator()

        return self.__apikey

    def refresh_apikey(self) -> object:
        """
        This function drop current ApiKey to logout user.
        
        Returns:
            (object): Instance itself
        """

        self.__apikey = None
        return self

    @property
    def nick(self) -> str | None:
        """ This return nick of the factory. """

        return self.__nick

    @property
    def password(self) -> str | None:
        """ This return password of the factory. """

        return self.__password

    @nick.setter
    def nick(self, target: str) -> None:
        """ This set new nick, and validate it. """

        if nick_validator(target).invalid:
            raise validator_exception("user.nick")

        self.__nick = target

    @password.setter
    def password(self, target: str) -> None:
        """ This set new password, and validate it. """

        if password_validator(target).invalid:
            raise validator_exception("user.password")

        self.__password = target

    @property
    def ready(self) -> bool:
        """ This check that factory is ready or not. """

        if self.__nick is None:
            return False

        if self.__password is None:
            return False

        return True

    @property
    def result(self) -> user:
        """ This create new user. Factory must be ready, to do it. """

        if not self.ready:
            raise not_ready_exception(self)

        if self.__result is not None:
            return self.__result

        self.__result = user(
            self.nick,
            self.password,
            self.apikey
        )

        return self.__result

class user_builder:
    """
    This create new user builder. User builder is responsble for loading users
    from dicts, which would be provided from config file.
    """

    def __init__(self, target: dict):
        """
        This create new user builder.

        Parameters:
            target (dict): Target dict to load user from
        """

        self.__target = target
        self.__result = None

    @property
    def nick(self) -> str:
        """ This return nick from dict. """

        if not "nick" in self.__target:
            return ""

        return self.__target["nick"]

    @property
    def password(self) -> str:
        """ This return password from dict. """

        if not "password" in self.__target:
            return ""
        
        return self.__target["password"]

    @property
    def apikey(self) -> str:
        """ This return ApiKey from dict. """

        if not "apikey" in self.__target:
            return ""

        return self.__target["apikey"]

    def check(self) -> None | str:
        """
        This validate all properties, nick, password and ApiKey. All must 
        be valid, if any is not valid or is not provided, then it return
        error. When all is valid, return None, when anything is wrong, 
        then return it as string.
        
        Returns:
            (None): When all is valid
            (str): Content of the error
        """

        if not "nick" in self.__target:
            return "User not contain nick."

        if nick_validator(self.nick).invalid:
            return "User nick \"" + self.nick + "\" is invalid."

        if not "password" in self.__target:
            return self.nick + " not contain password."

        if password_validator(self.password).invalid:
            return self.nick + " has invalid password."

        if not "apikey" in self.__target:
            return self.nick + " not contain apikey."

        if apikey_validator(self.apikey).invalid:
            return self.nick + " has invalid apikey."

        return None

    @property
    def result(self) -> user:
        """ Ready user form dict. """

        if self.__result is not None:
            return self.__result

        check = self.check()

        if check is not None:
            raise config_exception("User config contain error. " + check)

        self.__result = user(
            self.nick,
            self.password,
            self.apikey
        )

        return self.__result

class user_exporter:
    """
    This export user to dict, which could be safe into config file.
    """

    def __init__(self, target: user) -> None:
        """
        This initialize new exporter.
        
        Parameters:
            target (user): Target user to export
        """

        self.__target = target

    @property
    def target(self) -> user:
        """ Target user to export. """

        return self.__target

    @property
    def result(self) -> dict:
        """ Exported user as dict. """
        
        return {
            "nick": self.target.nick,
            "password": self.target.password,
            "apikey": self.target.apikey
        }
