from datetime import datetime, timedelta

import pytest

from conftest import assert_data
from program.models import TimeSlot
from program.tests.factories import RRuleFactory, ScheduleFactory

pytestmark = pytest.mark.django_db


def url(show=None, schedule=None) -> str:
    if show and schedule:
        return f"/api/v1/shows/{show.id}/schedules/{schedule.id}/"
    elif show and not schedule:
        return f"/api/v1/shows/{show.id}/schedules/"
    elif not show and schedule:
        return f"/api/v1/schedules/{schedule.id}/"
    else:
        return f"/api/v1/schedules/"


def schedule_data(rrule) -> dict[str, dict[str | int]]:
    now = datetime.now()
    in_an_hour = now + timedelta(hours=1)
    in_a_year = now + timedelta(days=365)

    return {
        "schedule": {
            "end_time": in_an_hour.strftime("%H:%M:%S"),
            "first_date": now.strftime("%Y-%m-%d"),
            "last_date": in_a_year.strftime("%Y-%m-%d"),
            "rrule_id": rrule.id,
            "start_time": now.strftime("%H:%M:%S"),
        },
    }


def test_create_once_schedule(admin_api_client, once_rrule, show):
    data = schedule_data(once_rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 1

    assert_data(response, data)


def test_create_daily_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=3)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 365 or 366

    assert_data(response, data)


def test_create_business_days_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=2, by_weekdays="0,1,2,3,4")
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 261 or 262

    assert_data(response, data)


def test_create_weekends_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=2, by_weekdays="5,6")
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 104 or 105

    assert_data(response, data)


def test_create_weekly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=2)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 52 or 53

    assert_data(response, data)


def test_create_2weekly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=2, interval=2)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 26 or 27

    assert_data(response, data)


def test_create_4weekly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=2, interval=4)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 13 or 14

    assert_data(response, data)


def test_create_monthly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=1)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 12

    assert_data(response, data)


def test_create_2monthly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=1, interval=2)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 6

    assert_data(response, data)


def test_create_3monthly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=1, interval=3)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 4

    assert_data(response, data)


def test_create_4monthly_schedule(admin_api_client, show):
    rrule = RRuleFactory(freq=1, interval=4)
    data = schedule_data(rrule)

    response = admin_api_client.post(url(show=show), data=data, format="json")

    assert response.status_code == 201
    assert TimeSlot.objects.all().count() == 3

    assert_data(response, data)


def test_create_schedule_forbidden_for_common_user(common_api_client1, once_rrule, show):
    data = schedule_data(once_rrule)

    response = common_api_client1.post(url(show=show), data=data, format="json")

    assert response.status_code == 403


def test_delete_schedule(admin_api_client, once_schedule):
    response = admin_api_client.delete(url(schedule=once_schedule))

    assert response.status_code == 204


def test_delete_schedule_forbidden_for_common_user(common_api_client1, once_schedule):
    response = common_api_client1.delete(url(schedule=once_schedule))

    assert response.status_code == 403


def test_list_schedules(api_client, once_rrule, show):
    SCHEDULES = 3
    ScheduleFactory.create_batch(size=SCHEDULES, show=show, rrule=once_rrule)

    response = api_client.get(url(show=show))

    assert response.status_code == 200
    assert len(response.data) == 3


def test_retrieve_schedule(api_client, once_schedule):
    response = api_client.get(url(schedule=once_schedule))

    assert response.status_code == 200


def test_update_schedule(admin_api_client, once_schedule):
    update = {
        "schedule": {
            "first_date": once_schedule.first_date,
            "start_time": once_schedule.start_time,
            "end_time": once_schedule.end_time,
            "rrule_id": once_schedule.rrule.id,
        },
    }

    response = admin_api_client.put(url(schedule=once_schedule), data=update, format="json")

    assert response.status_code == 200

    assert_data(response, update)