from plesk_mail_migrator.utils.cmd_args.cmd_args_parser import CommandLineArgumentException, CmdArgsParser


class PlainCmdArgsParser(CmdArgsParser):
    """Simple class to parse command-line arguments"""

    def __init__(self, args):
        self._args = args
        self._used_args = set()
        self._args_dict = dict()
        for arg in args:
            if '=' in arg:
                name, value = arg.split('=', 1)
            else:
                name = arg
                value = None

            # Command-line parser does not see difference between, to simplify usage:
            # 1) name=value
            # 2) -name=value
            # 3) --name=value
            if name.startswith('--'):
                name = name[2:]
            elif name.startswith('-'):
                name = name[1:]

            if name in self._args_dict:
                raise CommandLineArgumentException(
                    'Command-line parameter "%s" was specified multiple times' % name
                )

            self._args_dict[name] = value

    def get(self, arg_name):
        """Get value of string command-line argument

        :type arg_name: str | unicode
        :rtype: str | unicode | None
        :raises: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CommandLineArgumentException
        """
        value = self._get_argument_value(arg_name)
        return value

    def getboolean(self, arg_name):
        """Get value of boolean command-line argument

        :type arg_name: str | unicode
        :rtype: bool
        :raises: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CommandLineArgumentException
        """
        value = self._get_argument_value(arg_name)

        true_values = ('true', '1')
        false_values = ('false', '0')
        all_possible_values = true_values + false_values

        if value is None or value.lower() not in all_possible_values:
            raise CommandLineArgumentException(
                'Invalid value specified for command-line parameter "%s". Expected one of: %s' % (
                    arg_name, ", ".join(all_possible_values)
                )
            )

        value = value.lower()

        return value in true_values

    def getinteger(self, arg_name):
        """Get value of integer command-line argument

        :type arg_name: str | unicode
        :rtype: int
        :raises: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CommandLineArgumentException
        """
        value = self._get_argument_value(arg_name)

        try:
            value = int(value)
        except ValueError:
            raise CommandLineArgumentException(
                'Invalid value specified for command-line parameter "%s": expected integer' % arg_name
            )

        return value

    def getenum(self, arg_name, allowed_values):
        """Get value of enumerated (which has fixed set of valid values) command-line argument

        Validity checks are case-insensitive. Returned value is converted to lowercase.

        :type arg_name: str | unicode
        :type allowed_values: list[str | unicode]
        :rtype: str | unicode
        :raises: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CommandLineArgumentException
        """
        value = self._get_argument_value(arg_name)
        allowed_values_lower = [v.lower() for v in allowed_values]

        if value is None or value.lower() not in allowed_values_lower:
            raise CommandLineArgumentException(
                'Invalid value specified for command-line parameter "%s". Expected one of: %s' % (
                    arg_name, ", ".join(allowed_values)
                )
            )

        return value.lower()

    def contains(self, arg_name):
        """
        :type arg_name: str | unicode
        :rtype: bool
        """
        return arg_name in self._args_dict

    def get_full_param_name(self, short_name):
        """
        :type short_name: str | unicode
        :rtype: str | unicode
        """
        return "--%s" % short_name

    def check_unused_arguments(self):
        """Check arguments that were not used. Usually that means that customer specified wrong argument.
        Cheap alternative to explicit validation of arguments set.
        Call that function only once you requested all arguments.

        :rtype: None
        :raises: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CommandLineArgumentException
        """
        for arg in self._args_dict.keys():
            if arg not in self._used_args:
                raise CommandLineArgumentException(
                    'Unknown command-line parameter "%s"' % arg
                )

    def _get_argument_value(self, arg_name):
        """Get value of command-line argument with specified name. If argument was not passed - throw and exception.

        :type arg_name: str | unicode
        :rtype: str | unicode
        """
        if arg_name not in self._args_dict:
            raise CommandLineArgumentException(
                'Missing expected command-line parameter "%s"' % arg_name
            )

        self._used_args.add(arg_name)
        return self._args_dict[arg_name]
