from plesk_migrator.sdk.utils import parse_key_value_line


class Cron(object):
    """
    :type _shell: str | unicode | None
    :type _mailto: str | unicode | None
    :type _path: str | unicode | None
    :type _expressions: list[plesk_migrator.sdk.parsers.cron.CronExpression]    
    """

    def __init__(self, shell=None, mailto=None, path=None, expressions=None):
        self._shell = shell
        self._mailto = mailto
        self._path = path
        self._expressions = expressions if expressions else []

    @property
    def shell(self):
        """
        :rtype: str | unicode | None
        """
        return self._shell

    @property
    def mailto(self):
        """
        :rtype: str | unicode | None
        """
        return self._mailto

    @property
    def path(self):
        """
        :rtype: str | unicode | None
        """
        return self._path

    @property
    def expressions(self):
        """
        :rtype: list[plesk_migrator.sdk.parsers.cron.CronExpression]
        """
        return self._expressions

    @staticmethod
    def parse_crontab_content(crontab_content):
        """Parse content of CRON file
        
        Please note, that crontab type with user specified before the command is not supported.
        User will be parsed as part of command.
        https://help.ubuntu.com/community/CronHowto

        :type crontab_content: str | unicode
        :rtype: plesk_migrator.sdk.parsers.cron.Cron
        """
        shell = None
        mailto = None
        path = None
        expressions = []
        if not crontab_content:
            return Cron()
        for line in crontab_content.split('\n'):
            # skip empty lines and commentaries
            if len(line) == 0 or line.startswith('#'):
                continue
            shell_value = parse_key_value_line(line, 'SHELL', strip_symbols='\'"')
            if shell_value:
                shell = shell_value
                continue
            mailto_value = parse_key_value_line(line, 'MAILTO', strip_symbols='\'"')
            if mailto_value:
                mailto = mailto_value
                continue
            path_value = parse_key_value_line(line, 'PATH', strip_symbols='\'"')
            if path_value:
                path = path_value
                continue
            expression = CronExpression.parse_cron_expression(line)
            if expression:
                expressions.append(expression)
        return Cron(shell, mailto, path, expressions)


class CronExpression(object):
    """
    :type _minutes: str | unicode
    :type _hours: str | unicode
    :type _day_of_month: str | unicode 
    :type _month: str | unicode
    :type _day_of_week: str | unicode
    :type _command: str | unicode
    :type _macros: str | unicode | None    
    """
    def __init__(self, minutes, hours, day_of_month, month, day_of_week, command, macros=None):
        self._minutes = minutes
        self._hours = hours
        self._day_of_month = day_of_month
        self._month = month
        self._day_of_week = day_of_week
        self._command = command
        self._macros = macros

    @property
    def minutes(self):
        """
        :rtype: str | unicode
        """
        return self._minutes

    @property
    def hours(self):
        """
        :rtype: str | unicode 
        """
        return self._hours

    @property
    def day_of_month(self):
        """
        :rtype: str | unicode 
        """
        return self._day_of_month

    @property
    def month(self):
        """
        :rtype: str | unicode 
        """
        return self._month

    @property
    def day_of_week(self):
        """
        :rtype: str | unicode 
        """
        return self._day_of_week

    @property
    def command(self):
        """
        :rtype: str | unicode 
        """
        return self._command

    @property
    def macros(self):
        """
        :rtype: str | unicode | None
        """
        return self._macros

    @staticmethod
    def parse_cron_expression(line):
        """Parse CRON expression

        :type line: str | unicode
        :rtype: plesk_migrator.sdk.parsers.cron.CronExpression | None
        """
        # macros in cron expression
        if line.startswith('@'):
            fline = line.lstrip('@').split(None, 1)
            if len(fline) == 2:
                return CronExpression._parse_cron_expression_with_macros(*fline)
        # regular cron expression
        elements = line.split(None, 5)
        if len(elements) == 6:
            return CronExpression(*elements)
        # unknown
        return None

    @staticmethod
    def _parse_cron_expression_with_macros(macros, command):
        """Return list of time variables equivalent to macros
        https://help.ubuntu.com/community/CronHowto

        :type macros: str | unicode
        :type command: str | unicode
        :rtype: plesk_migrator.sdk.parsers.cron.CronExpression | None
        """
        if macros == 'yearly' or macros == 'annually':
            return CronExpression('0', '0', '1', '1', '*', command, macros)
        elif macros == 'monthly':
            return CronExpression('0', '0', '1', '*', '*', command, macros)
        elif macros == 'weekly':
            return CronExpression('0', '0', '*', '*', '0', command, macros)
        elif macros == 'daily' or macros == 'midnight':
            return CronExpression('0', '0', '*', '*', '*', command, macros)
        elif macros == 'hourly':
            return CronExpression('0', '*', '*', '*', '*', command, macros)
        elif macros == 'reboot':
            # no time equivalent for '@reboot' macros, return February, 31, that will never run
            return CronExpression('0', '0', '31', '2', '0', command, macros)
        return None
