from datetime import datetime

import pytest
from django.conf import settings
from django_celery_beat.models import CrontabSchedule

from core.utils import fmt_input, dtnow
from rotation.enums import Period, WeekDay, Month
from rotation.serializers import TimeRotationSettingsSerializer
from rotation.services import cron_utils

TIME_ZONE = getattr(settings, 'TIME_ZONE', 'UTC')


@pytest.mark.django_db
class TestCrontabFunctions(object):

    @pytest.mark.unit
    def test_can_convert_time(self):
        now = datetime(year=2020, month=1, day=1, hour=14, minute=30)
        data = {'rotation_type': '0',
                'size_rotation': '5242880',
                'schedule': {
                    'period': Period.DAY,
                    'time': fmt_input(now.time()),
                    'week_day': [],
                    'month': []
                }
                }
        serializer = TimeRotationSettingsSerializer(data=data)
        serializer.is_valid()
        schedule = cron_utils.serializer_to_model(serializer)

        assert schedule.minute == now.time().minute
        assert schedule.hour == now.time().hour
        assert schedule.day_of_week == '*'
        assert schedule.day_of_month == '*'
        assert schedule.month_of_year == '*'
        assert schedule.timezone == TIME_ZONE

    @pytest.mark.unit
    def test_can_convert_week_day_single(self):
        now = dtnow()
        data = {
            'rotation_type': '0',
            'size_rotation': '5242880',
            'schedule': {
                'period': Period.WEEK,
                'time': fmt_input(now.time()),
                'week_day': [WeekDay.SATURDAY],
                'month': []
            }
        }
        serializer = TimeRotationSettingsSerializer(data=data)
        serializer.is_valid()

        schedule = cron_utils.serializer_to_model(serializer)

        assert schedule.day_of_week == '5'
        assert schedule.minute == '0'
        assert schedule.hour == '0'

    @pytest.mark.unit
    def test_can_convert_week_day_multi(self):
        now = dtnow()
        data = {
            'rotation_type': '0',
            'size_rotation': '5242880',
            'schedule': {
                'period': Period.WEEK,
                'time': fmt_input(now.time()),
                'week_day': [WeekDay.SATURDAY, WeekDay.MONDAY],
                'month': []
            }
        }
        serializer = TimeRotationSettingsSerializer(data=data)
        serializer.is_valid()

        schedule = cron_utils.serializer_to_model(serializer)

        assert schedule.day_of_week == '1,6'
        assert TestCrontabFunctions.is_int(schedule.minute), 'Must be a single number, i.e. once'
        assert TestCrontabFunctions.is_int(schedule.hour), 'Must be a single number, i.e. once'

    @staticmethod
    def is_int(val):
        try:
            int(val)
        except:
            return False
        return True

    @pytest.mark.unit
    def test_can_convert_month_single(self):
        now = dtnow()
        data = {'rotation_type': '0',
                'size_rotation': '5242880',
                'schedule': {
                    'period': Period.MONTH,
                    'time': fmt_input(now.time()),
                    'week_day': [],
                    'month': [Month.APRIL]
                }
                }
        serializer = TimeRotationSettingsSerializer(data=data)
        serializer.is_valid()

        schedule = cron_utils.serializer_to_model(serializer)

        assert schedule.month_of_year == '4'
        assert schedule.day_of_week == '*'
        assert TestCrontabFunctions.is_int(schedule.day_of_month), 'Must be a single number, i.e. once'
        assert TestCrontabFunctions.is_int(schedule.minute), 'Must be a single number, i.e. once'
        assert TestCrontabFunctions.is_int(schedule.hour), 'Must be a single number, i.e. once'

        print(schedule)

    @pytest.mark.unit
    def test_can_convert_month_multi(self):
        now = dtnow()
        data = {'rotation_type': '0',
                'size_rotation': '5242880',
                'schedule': {
                    'period': Period.MONTH,
                    'time': fmt_input(now.time()),
                    'week_day': [],
                    'month': [Month.APRIL, Month.SEPTEMBER]
                }
                }
        serializer = TimeRotationSettingsSerializer(data=data)
        serializer.is_valid()

        schedule = cron_utils.serializer_to_model(serializer)

        assert schedule.month_of_year == '4,9'
        assert schedule.day_of_week == '*'
        assert TestCrontabFunctions.is_int(schedule.day_of_month), 'Must be a single number, i.e. once'
        assert TestCrontabFunctions.is_int(schedule.minute), 'Must be a single number, i.e. once'
        assert TestCrontabFunctions.is_int(schedule.hour), 'Must be a single number, i.e. once'

    @pytest.mark.unit
    def test_can_convert_from_month_single(self):
        schedule = CrontabSchedule(month_of_year='1')

        data = cron_utils.schedule_to_dict(schedule)

        assert data['period'] == Period.MONTH
        assert len(data['month']) == 1
        assert data['month'][0] == Month.JANUARY

    @pytest.mark.unit
    def test_can_convert_from_month_multi(self):
        schedule = CrontabSchedule(month_of_year='1,2')

        data = cron_utils.schedule_to_dict(schedule)

        assert data['period'] == Period.MONTH
        assert len(data['month']) == 2
        assert data['month'][0] == Month.JANUARY
        assert data['month'][1] == Month.FEBRUARY

    @pytest.mark.unit
    def test_can_convert_week_day_single(self):
        schedule = CrontabSchedule(day_of_week='0')

        data = cron_utils.schedule_to_dict(schedule)

        assert data['period'] == Period.WEEK
        assert len(data['week_day']) == 1
        assert data['week_day'][0] == WeekDay.SUNDAY

    @pytest.mark.unit
    def test_can_convert_week_day_multi2(self):
        schedule = CrontabSchedule(day_of_week='0,1,3')

        data = cron_utils.schedule_to_dict(schedule)

        assert data['period'] == Period.WEEK
        assert len(data['week_day']) == 3
        assert data['week_day'][0] == WeekDay.SUNDAY
        assert data['week_day'][1] == WeekDay.MONDAY
        assert data['week_day'][2] == WeekDay.WEDNESDAY

    @pytest.mark.unit
    def test_can_convert_time(self):
        date = datetime(year=2020, month=1, day=1, hour=15, minute=20)

        schedule = CrontabSchedule(hour='15', minute='20')

        data = cron_utils.schedule_to_dict(schedule)

        assert data['period'] == Period.DAY
        assert data['time'] == date.time()
