# Author : Niloy Chakraborty
# AWS Schedule Expression cron expression validator
import re

from voluptuous import Invalid

import datetime

minute_regex = r"^([*]|([0]?[0-5]?[0-9]?)|(([0]?[0-5]?[0-9]?)(\/|\-)([0]?[0-5]?[0-9]?))|" \
               "(([0]?[0-5]?[0-9]?)((\,)([0]?[0-5]?[0-9]?))*))$"
hour_regex = r"^([*]|[01]?[0-9]|2[0-3]|(([01]?[0-9]|2[0-3]?)(\/|\-)([01]?[0-9]|2[0-3]?))|" \
             "(([01]?[0-9]|2[0-3]?)((\,)([01]?[0-9]|2[0-3]?))*))$"
d_m_regex = r"^([*]|[?]|([0-2]?[0-9]|3[0-1])|(([0-2]?[0-9]|3[0-1])(\/|\-)([0-2]?[0-9]|3[0-1]))|" \
            "(([0-2]?[0-9]|3[0-1])((\,)([0-2]?[0-9]|3[0-1]))*))$"
month_regex = r"^([*]|([0]?[0-9]|1[0-2])|(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|" \
              "((([0]?[0-9]|1[0-2])|(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))(\/|\-)(([0]?[0-9]|1[0-2])|" \
              "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)))|((([0]?[0-9]|1[0-2])|" \
              "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))((\,)(([0]?[0-9]|1[0-2])|" \
              "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)))*))$"
d_w_regex = r"^([*]|[?]|([0]?[1-7])|(SUN|MON|TUE|WED|THU|FRI|SAT)|((([0]?[1-7])|(SUN|MON|TUE|WED|" \
            "THU|FRI|SAT))(\/|\-|\,|\#)(([0]?[1-7])|(SUN|MON|TUE|WED|THU|FRI|SAT)))|((([0]?[1-7])|(SUN|MON|TUE|WED|" \
            "THU|FRI|SAT))((\,)(([0]?[1-7])|(SUN|MON|TUE|WED|THU|FRI|SAT)))*))$"
year_regex = r"^([*]|([1-2][01][0-9][0-9])|(([1-2][01][0-9][0-9])(\/|\-)([1-2][01][0-9][0-9]))|" \
             "(([1-2][01][0-9][0-9])((\,)([1-2][01][0-9][0-9]))*))$"


def validate_aws_regex(regex_val):
    regex_splits = regex_val.split(" ")
    if len(regex_splits) < 6:
        raise Invalid(
            "Schedule parameter should have 6 field minute,hour,day_of_the_month,month,day_of_the_week,year.Please check the value")

    minute_val = regex_splits[0]
    hour_val = regex_splits[1]
    d_m_val = regex_splits[2]
    month_val = regex_splits[3]
    d_w_val = regex_splits[4]
    year_val = regex_splits[5]

    print(
        "Values of Min is %s , hour is %s , day_of_month is %s , month is %s ,day of the week is %s and year is %s" % (
            minute_val, hour_val, d_m_val, month_val, d_w_val, year_val))
    if not ((d_m_val == '?' and d_w_val != '?') or (d_m_val != '?' and d_w_val == '?')):
        raise Invalid("Either day-of-month or day-of-week values must be a question mark (?)")
    minute_match = re.fullmatch(minute_regex, minute_val)
    hour_match = re.fullmatch(hour_regex, hour_val)
    d_m_match = re.fullmatch(d_m_regex, d_m_val)
    month_match = re.fullmatch(month_regex, month_val)
    d_w_match = re.fullmatch(d_w_regex, d_w_val)
    year_match = re.fullmatch(year_regex, year_val)

    if not minute_match:
        raise Invalid("Schedule expression has an invalid minute column value")
    if not hour_match:
        raise Invalid("Schedule expression has an invalid hour column value")
    if not d_m_match:
        raise Invalid("Schedule expression has an invalid day of the month column value")
    if not month_match:
        raise Invalid("Schedule expression has an invalid month column value")
    if not d_w_match:
        raise Invalid("Schedule expression has an invalid day of the week column value")
    if not year_match:
        raise Invalid("Schedule expression has an invalid year column value")

    if '#' in d_w_val:
        nd = int(d_w_val.split('#')[1])
        if nd > 5:
            raise Invalid(
                "Schedule expression has an invalid day of the week column value. Nth day of week cannot be more than 5")

    current_year = datetime.datetime.today().year

    year_split_comma_val = year_val.split(",")
    year_split_dash_val = year_val.split("-")

    if "," in year_val:
        if any(yr_val for yr_val in year_split_comma_val if int(yr_val) < current_year):
            raise \
                Invalid("Schedule expression has an invalid year column value. " \
                        "Year value[s] should be greater or equal to %s and less than 2199 " % current_year)
    if "-" in year_val:
        if int(year_split_dash_val[1]) < current_year:
            raise \
                Invalid("Schedule expression has an invalid year column value. " \
                        "Year value[s] should be greater or equal to %s and less than 2199 " % current_year)

    return regex_val


# Valid
assert validate_aws_regex("0 18 ? * MON-FRI *") == "0 18 ? * MON-FRI *"
assert validate_aws_regex("0 10 * * ? *") == "0 10 * * ? *"
assert validate_aws_regex("15 12 * * ? *") == "15 12 * * ? *"
assert validate_aws_regex("0 8 1 * ? *") == "0 8 1 * ? *"
assert validate_aws_regex("0/5 8-17 ? * MON-FRI *") == "0/5 8-17 ? * MON-FRI *"
assert validate_aws_regex("0 9 ? * 2#1 *") == "0 9 ? * 2#1 *"
assert validate_aws_regex("0 07/12 ? * * *") == "0 07/12 ? * * *"
assert validate_aws_regex("10,20,30,40 07/12 ? * * *") == "10,20,30,40 07/12 ? * * *"
assert validate_aws_regex("10 10,15,20,23 ? * * *") == "10 10,15,20,23 ? * * *"
assert validate_aws_regex("10 10 15,30,31 * ? *") == "10 10 15,30,31 * ? *"
assert validate_aws_regex("10 10 15 JAN,JUL,DEC ? *") == "10 10 15 JAN,JUL,DEC ? *"
assert validate_aws_regex("10 10 31 04,09,12 ? *") == "10 10 31 04,09,12 ? *"
assert validate_aws_regex("0,5 07/12 ? * 01,05,7 *") == "0,5 07/12 ? * 01,05,7 *"
assert validate_aws_regex("0,5 07/12 ? * 01,05,7 2020,2021,2028,2199") == "0,5 07/12 ? * 01,05,7 2020,2021,2028,2199"
assert validate_aws_regex("0,5 07/12 ? * 01,05,7 2020,2021,2028,2199")


# Invalid
# validate_aws_regex("0 18 ? * MON-FRI")
# validate_aws_regex("0 18 * * * *")
# validate_aws_regex("0 65 * * ? *")
# validate_aws_regex("89 10 * * ? *")
# validate_aws_regex("15/65 10 * * ? *")
# validate_aws_regex("15/30 10 * * ? 2400")
# validate_aws_regex("0 9 ? * 2#6 *")
# validate_aws_regex("0 9 ? * ? *")
# validate_aws_regex("10 10 31 04,09,13 ? *")
# validate_aws_regex("0,5 07/12 ? * 01,05,8 *")
# validate_aws_regex("0,5 07/12 ? * 01,05,7 2020,2021,2028,1111")
#validate_aws_regex("0,5 07/12 ? * 01,05,7 2020,2021,2028,2017")
#validate_aws_regex("0,5 07/12 ? * 01,05,7 2017-2100")