from datetime import datetime, timedelta

import pytest

from conftest import create_daily_schedule

pytestmark = pytest.mark.django_db


def url(include_virtual=False, start=None, end=None):
    if include_virtual and start and end:
        return f"/api/v1/program/calendar/?include_virtual=true&start={start}&end={end}"
    elif start and end:
        return f"/api/v1/program/calendar/?start={start}&end={end}"
    elif include_virtual:
        return "/api/v1/program/calendar/?include_virtual=true"
    else:
        return "/api/v1/program/calendar/"


def assert_episodes(episodes, one_week=False) -> None:
    """asserts the episodes are valid."""

    assert len(episodes) == 2 if not one_week else 7

    for episode in episodes:
        assert episode["id"]
        assert episode["timeslotId"]


def assert_program(program, show, fallback_show=None, one_week=False) -> None:
    """asserts the program are valid and correspond to the given show and fallback show."""

    assert len(program) == 2 if fallback_show is None and not one_week else (7 if one_week else 3)

    for entry in program:
        assert entry["end"]
        assert entry["id"]
        assert entry["start"]

    if fallback_show is None:
        for entry in program:
            assert entry["showId"] == show.id
            assert entry["timeslotId"]
    else:
        for entry in program:
            if entry["timeslotId"]:
                assert entry["showId"] == show.id
                assert entry["timeslotId"]
            else:
                assert entry["showId"] == fallback_show.id


def assert_shows(shows, show, fallback_show=None) -> None:
    """asserts the shows are valid correspond to the given show and fallback show."""

    assert len(shows) == 1 if fallback_show is None else 2

    if fallback_show is None:
        assert shows[0]["id"] == show.id
        assert shows[0]["name"] == show.name
    else:
        assert shows[0]["id"] == show.id
        assert shows[0]["name"] == show.name

        assert shows[1]["id"] == fallback_show.id
        assert shows[1]["name"] == fallback_show.name


def assert_timeslots(timeslots, show, one_week=False) -> None:
    """asserts the timeslots are valid and correspond to the given show."""

    assert len(timeslots) == 2 if not one_week else 7

    for timeslot in timeslots:
        assert timeslot["id"]
        assert timeslot["noteId"]
        assert timeslot["end"]
        assert timeslot["start"]
        assert timeslot["showId"] == show.id


def test_calendar(admin_api_client, api_client, daily_rrule, show):
    create_daily_schedule(admin_api_client, daily_rrule, show)

    response = api_client.get(url())

    assert response.status_code == 200

    for key, value in response.json().items():
        match key:
            case "episodes":
                assert_episodes(value)
            case "program":
                assert_program(value, show)
            case "shows":
                assert_shows(value, show)
            case "timeslots":
                assert_timeslots(value, show)


def test_calendar_one_week(admin_api_client, api_client, daily_rrule, show):
    create_daily_schedule(admin_api_client, daily_rrule, show)

    now = datetime.now()
    in_one_week = now + timedelta(days=7)

    response = api_client.get(url(start=now.isoformat(), end=in_one_week.isoformat()))

    assert response.status_code == 200

    for key, value in response.json().items():
        match key:
            case "episodes":
                assert_episodes(value, one_week=True)
            case "program":
                assert_program(value, show, one_week=True)
            case "shows":
                assert_shows(value, show)
            case "timeslots":
                assert_timeslots(value, show, one_week=True)


def test_calendar_include_virtual(
    admin_api_client,
    api_client,
    daily_rrule,
    show,
    fallback_show,
    radio_settings,
):
    create_daily_schedule(admin_api_client, daily_rrule, show)

    response = api_client.get(url(include_virtual=True))

    assert response.status_code == 200

    for key, value in response.json().items():
        match key:
            case "episodes":
                assert_episodes(value)
            case "program":
                assert_program(value, show, fallback_show)
            case "shows":
                assert_shows(value, show, fallback_show)
            case "timeslots":
                assert_timeslots(value, show)


def test_calendar_one_week_include_virtual(
    admin_api_client,
    api_client,
    daily_rrule,
    show,
    fallback_show,
    radio_settings,
):
    create_daily_schedule(admin_api_client, daily_rrule, show)

    now = datetime.now()
    in_one_week = now + timedelta(days=7)

    response = api_client.get(
        url(include_virtual=True, start=now.isoformat(), end=in_one_week.isoformat())
    )

    assert response.status_code == 200

    for key, value in response.json().items():
        match key:
            case "episodes":
                assert_episodes(value, one_week=True)
            case "program":
                assert_program(value, show=show, fallback_show=fallback_show, one_week=True)
            case "shows":
                assert_shows(value, show=show, fallback_show=fallback_show)
            case "timeslots":
                assert_timeslots(value, show=show, one_week=True)