"""Tests for calendar platform of local calendar."""

import datetime
import textwrap

import pytest

from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers.template import DATE_STR_FORMAT
import homeassistant.util.dt as dt_util

from .conftest import (
    FRIENDLY_NAME,
    TEST_ENTITY,
    ClientFixture,
    GetEventsFn,
    event_fields,
)

from tests.common import MockConfigEntry


async def test_empty_calendar(
    hass: HomeAssistant, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test querying the API and fetching events."""
    events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
    assert len(events) == 0

    state = hass.states.get(TEST_ENTITY)
    assert state
    assert state.name == FRIENDLY_NAME
    assert state.state == STATE_OFF
    assert dict(state.attributes) == {
        "friendly_name": FRIENDLY_NAME,
        "supported_features": 7,
    }


@pytest.mark.parametrize(
    ("dtstart", "dtend"),
    [
        ("1997-07-14T18:00:00+01:00", "1997-07-15T05:00:00+01:00"),
        ("1997-07-14T17:00:00+00:00", "1997-07-15T04:00:00+00:00"),
        ("1997-07-14T11:00:00-06:00", "1997-07-14T22:00:00-06:00"),
        ("1997-07-14T10:00:00-07:00", "1997-07-14T21:00:00-07:00"),
    ],
)
async def test_api_date_time_event(
    ws_client: ClientFixture,
    setup_integration: None,
    get_events: GetEventsFn,
    dtstart: str,
    dtend: str,
) -> None:
    """Test an event with a start/end date time.

    Events created in various timezones are ultimately returned relative
    to local home assistant timezone.
    """
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                "dtstart": dtstart,
                "dtend": dtend,
            },
        },
    )

    events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
        }
    ]

    # Query events in UTC

    # Time range before event
    events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T16:00:00Z")
    assert len(events) == 0
    # Time range after event
    events = await get_events("1997-07-15T05:00:00Z", "1997-07-15T06:00:00Z")
    assert len(events) == 0

    # Overlap with event start
    events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T18:00:00Z")
    assert len(events) == 1
    # Overlap with event end
    events = await get_events("1997-07-15T03:00:00Z", "1997-07-15T06:00:00Z")
    assert len(events) == 1

    # Query events overlapping with start and end but in another timezone
    events = await get_events("1997-07-12T23:00:00-01:00", "1997-07-14T17:00:00-01:00")
    assert len(events) == 1
    events = await get_events("1997-07-15T02:00:00-01:00", "1997-07-15T05:00:00-01:00")
    assert len(events) == 1


async def test_api_date_event(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test an event with a start/end date all day event."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Festival International de Jazz de Montreal",
                "dtstart": "2007-06-28",
                "dtend": "2007-07-09",
            },
        },
    )

    events = await get_events("2007-06-20T00:00:00", "2007-07-20T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Festival International de Jazz de Montreal",
            "start": {"date": "2007-06-28"},
            "end": {"date": "2007-07-09"},
        }
    ]

    # Time range before event (timezone is -6)
    events = await get_events("2007-06-26T00:00:00Z", "2007-06-28T01:00:00Z")
    assert len(events) == 0
    # Time range after event
    events = await get_events("2007-07-10T00:00:00Z", "2007-07-11T00:00:00Z")
    assert len(events) == 0

    # Overlap with event start (timezone is -6)
    events = await get_events("2007-06-26T00:00:00Z", "2007-06-28T08:00:00Z")
    assert len(events) == 1
    # Overlap with event end
    events = await get_events("2007-07-09T00:00:00Z", "2007-07-11T00:00:00Z")
    assert len(events) == 1


async def test_active_event(
    hass: HomeAssistant,
    ws_client: ClientFixture,
    setup_integration: None,
) -> None:
    """Test an event with a start/end date time."""
    start = dt_util.now() - datetime.timedelta(minutes=30)
    end = dt_util.now() + datetime.timedelta(minutes=30)
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Evening lights",
                "dtstart": start.isoformat(),
                "dtend": end.isoformat(),
            },
        },
    )

    state = hass.states.get(TEST_ENTITY)
    assert state
    assert state.name == FRIENDLY_NAME
    assert state.state == STATE_ON
    assert dict(state.attributes) == {
        "friendly_name": FRIENDLY_NAME,
        "message": "Evening lights",
        "all_day": False,
        "description": "",
        "location": "",
        "start_time": start.strftime(DATE_STR_FORMAT),
        "end_time": end.strftime(DATE_STR_FORMAT),
        "supported_features": 7,
    }


async def test_upcoming_event(
    hass: HomeAssistant,
    ws_client: ClientFixture,
    setup_integration: None,
) -> None:
    """Test an event with a start/end date time."""
    start = dt_util.now() + datetime.timedelta(days=1)
    end = dt_util.now() + datetime.timedelta(days=1, hours=1)
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Evening lights",
                "dtstart": start.isoformat(),
                "dtend": end.isoformat(),
            },
        },
    )

    state = hass.states.get(TEST_ENTITY)
    assert state
    assert state.name == FRIENDLY_NAME
    assert state.state == STATE_OFF
    assert dict(state.attributes) == {
        "friendly_name": FRIENDLY_NAME,
        "message": "Evening lights",
        "all_day": False,
        "description": "",
        "location": "",
        "start_time": start.strftime(DATE_STR_FORMAT),
        "end_time": end.strftime(DATE_STR_FORMAT),
        "supported_features": 7,
    }


async def test_recurring_event(
    ws_client: ClientFixture,
    setup_integration: None,
    hass: HomeAssistant,
    get_events: GetEventsFn,
) -> None:
    """Test an event with a recurrence rule."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Monday meeting",
                "dtstart": "2022-08-29T09:00:00",
                "dtend": "2022-08-29T10:00:00",
                "rrule": "FREQ=WEEKLY",
            },
        },
    )

    events = await get_events("2022-08-20T00:00:00", "2022-09-20T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Monday meeting",
            "start": {"dateTime": "2022-08-29T09:00:00-06:00"},
            "end": {"dateTime": "2022-08-29T10:00:00-06:00"},
            "recurrence_id": "20220829T090000",
        },
        {
            "summary": "Monday meeting",
            "start": {"dateTime": "2022-09-05T09:00:00-06:00"},
            "end": {"dateTime": "2022-09-05T10:00:00-06:00"},
            "recurrence_id": "20220905T090000",
        },
        {
            "summary": "Monday meeting",
            "start": {"dateTime": "2022-09-12T09:00:00-06:00"},
            "end": {"dateTime": "2022-09-12T10:00:00-06:00"},
            "recurrence_id": "20220912T090000",
        },
        {
            "summary": "Monday meeting",
            "start": {"dateTime": "2022-09-19T09:00:00-06:00"},
            "end": {"dateTime": "2022-09-19T10:00:00-06:00"},
            "recurrence_id": "20220919T090000",
        },
    ]


async def test_websocket_delete(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test websocket delete command."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                "dtstart": "1997-07-14T17:00:00+00:00",
                "dtend": "1997-07-15T04:00:00+00:00",
            },
        },
    )

    events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
        }
    ]
    uid = events[0]["uid"]

    # Delete the event
    await client.cmd_result(
        "delete",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
        },
    )
    events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
    assert list(map(event_fields, events)) == []


async def test_websocket_delete_recurring(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test deleting a recurring event."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Morning Routine",
                "dtstart": "2022-08-22T08:30:00",
                "dtend": "2022-08-22T09:00:00",
                "rrule": "FREQ=DAILY",
            },
        },
    )

    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-24T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-24T09:00:00-06:00"},
            "recurrence_id": "20220824T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-25T09:00:00-06:00"},
            "recurrence_id": "20220825T083000",
        },
    ]
    uid = events[0]["uid"]
    assert [event["uid"] for event in events] == [uid] * 4

    # Cancel a single instance and confirm it was removed
    await client.cmd_result(
        "delete",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
            "recurrence_id": "20220824T083000",
        },
    )
    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-25T09:00:00-06:00"},
            "recurrence_id": "20220825T083000",
        },
    ]

    # Delete all and future and confirm multiple were removed
    await client.cmd_result(
        "delete",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
            "recurrence_id": "20220823T083000",
            "recurrence_range": "THISANDFUTURE",
        },
    )
    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
    ]


async def test_websocket_update(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test websocket update command."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                "dtstart": "1997-07-14T17:00:00+00:00",
                "dtend": "1997-07-15T04:00:00+00:00",
            },
        },
    )

    events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
        }
    ]
    uid = events[0]["uid"]

    # Update the event
    await client.cmd_result(
        "update",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
            "event": {
                "summary": "Bastille Day Party [To be rescheduled]",
                "dtstart": "1997-07-14",
                "dtend": "1997-07-15",
            },
        },
    )
    events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party [To be rescheduled]",
            "start": {"date": "1997-07-14"},
            "end": {"date": "1997-07-15"},
        }
    ]


async def test_websocket_update_recurring_this_and_future(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test updating a recurring event."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Morning Routine",
                "dtstart": "2022-08-22T08:30:00",
                "dtend": "2022-08-22T09:00:00",
                "rrule": "FREQ=DAILY",
            },
        },
    )

    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-24T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-24T09:00:00-06:00"},
            "recurrence_id": "20220824T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-25T09:00:00-06:00"},
            "recurrence_id": "20220825T083000",
        },
    ]
    uid = events[0]["uid"]
    assert [event["uid"] for event in events] == [uid] * 4

    # Update a single instance and confirm the change is reflected
    await client.cmd_result(
        "update",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
            "recurrence_id": "20220824T083000",
            "recurrence_range": "THISANDFUTURE",
            "event": {
                "summary": "Morning Routine [Adjusted]",
                "dtstart": "2022-08-24T08:00:00",
                "dtend": "2022-08-24T08:30:00",
            },
        },
    )
    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine [Adjusted]",
            "start": {"dateTime": "2022-08-24T08:00:00-06:00"},
            "end": {"dateTime": "2022-08-24T08:30:00-06:00"},
            "recurrence_id": "20220824T080000",
        },
        {
            "summary": "Morning Routine [Adjusted]",
            "start": {"dateTime": "2022-08-25T08:00:00-06:00"},
            "end": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "recurrence_id": "20220825T080000",
        },
    ]


async def test_websocket_update_recurring(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Test updating a recurring event."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Morning Routine",
                "dtstart": "2022-08-22T08:30:00",
                "dtend": "2022-08-22T09:00:00",
                "rrule": "FREQ=DAILY",
            },
        },
    )

    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-24T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-24T09:00:00-06:00"},
            "recurrence_id": "20220824T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-25T09:00:00-06:00"},
            "recurrence_id": "20220825T083000",
        },
    ]
    uid = events[0]["uid"]
    assert [event["uid"] for event in events] == [uid] * 4

    # Update a single instance and confirm the change is reflected
    await client.cmd_result(
        "update",
        {
            "entity_id": TEST_ENTITY,
            "uid": uid,
            "recurrence_id": "20220824T083000",
            "event": {
                "summary": "Morning Routine [Adjusted]",
                "dtstart": "2022-08-24T08:00:00",
                "dtend": "2022-08-24T08:30:00",
            },
        },
    )
    events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-22T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-22T09:00:00-06:00"},
            "recurrence_id": "20220822T083000",
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-23T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-23T09:00:00-06:00"},
            "recurrence_id": "20220823T083000",
        },
        {
            "summary": "Morning Routine [Adjusted]",
            "start": {"dateTime": "2022-08-24T08:00:00-06:00"},
            "end": {"dateTime": "2022-08-24T08:30:00-06:00"},
        },
        {
            "summary": "Morning Routine",
            "start": {"dateTime": "2022-08-25T08:30:00-06:00"},
            "end": {"dateTime": "2022-08-25T09:00:00-06:00"},
            "recurrence_id": "20220825T083000",
        },
    ]


@pytest.mark.parametrize(
    "rrule",
    [
        "FREQ=SECONDLY",
        "FREQ=MINUTELY",
        "FREQ=HOURLY",
        "invalid",
        "",
    ],
)
async def test_invalid_rrule(
    ws_client: ClientFixture,
    setup_integration: None,
    hass: HomeAssistant,
    get_events: GetEventsFn,
    rrule: str,
) -> None:
    """Test an event with a recurrence rule."""
    client = await ws_client()
    resp = await client.cmd(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Monday meeting",
                "dtstart": "2022-08-29T09:00:00",
                "dtend": "2022-08-29T10:00:00",
                "rrule": rrule,
            },
        },
    )
    assert resp
    assert not resp.get("success")
    assert "error" in resp
    assert resp["error"].get("code") == "invalid_format"


@pytest.mark.parametrize(
    ("time_zone", "event_order"),
    [
        ("America/Los_Angeles", ["One", "Two", "All Day Event"]),
        ("America/Regina", ["One", "Two", "All Day Event"]),
        ("UTC", ["One", "All Day Event", "Two"]),
        ("Asia/Tokyo", ["All Day Event", "One", "Two"]),
    ],
)
async def test_all_day_iter_order(
    hass: HomeAssistant,
    ws_client: ClientFixture,
    setup_integration: None,
    get_events: GetEventsFn,
    event_order: list[str],
):
    """Test the sort order of an all day events depending on the time zone."""
    client = await ws_client()
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "All Day Event",
                "dtstart": "2022-10-08",
                "dtend": "2022-10-09",
            },
        },
    )
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "One",
                "dtstart": "2022-10-07T23:00:00+00:00",
                "dtend": "2022-10-07T23:30:00+00:00",
            },
        },
    )
    await client.cmd_result(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Two",
                "dtstart": "2022-10-08T01:00:00+00:00",
                "dtend": "2022-10-08T02:00:00+00:00",
            },
        },
    )

    events = await get_events("2022-10-06T00:00:00Z", "2022-10-09T00:00:00Z")
    assert [event["summary"] for event in events] == event_order


async def test_start_end_types(
    ws_client: ClientFixture,
    setup_integration: None,
) -> None:
    """Test a start and end with different date and date time types."""
    client = await ws_client()
    result = await client.cmd(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                "dtstart": "1997-07-15",
                "dtend": "1997-07-14T17:00:00+00:00",
            },
        },
    )
    assert result
    assert not result.get("success")
    assert "error" in result
    assert "code" in result["error"]
    assert result["error"]["code"] == "invalid_format"


async def test_end_before_start(
    ws_client: ClientFixture,
    setup_integration: None,
) -> None:
    """Test an event with a start/end date time."""
    client = await ws_client()
    result = await client.cmd(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                "dtstart": "1997-07-15T04:00:00+00:00",
                "dtend": "1997-07-14T17:00:00+00:00",
            },
        },
    )
    assert result
    assert not result.get("success")
    assert "error" in result
    assert "code" in result["error"]
    assert result["error"]["code"] == "invalid_format"


async def test_invalid_recurrence_rule(
    ws_client: ClientFixture,
    setup_integration: None,
) -> None:
    """Test an event with a recurrence rule."""
    client = await ws_client()
    result = await client.cmd(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Monday meeting",
                "dtstart": "2022-08-29T09:00:00",
                "dtend": "2022-08-29T10:00:00",
                "rrule": "FREQ=invalid;'",
            },
        },
    )
    assert result
    assert not result.get("success")
    assert "error" in result
    assert "code" in result["error"]
    assert result["error"]["code"] == "invalid_format"


async def test_invalid_date_formats(
    ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
) -> None:
    """Exercises a validation error within rfc5545 parsing in ical."""
    client = await ws_client()
    result = await client.cmd(
        "create",
        {
            "entity_id": TEST_ENTITY,
            "event": {
                "summary": "Bastille Day Party",
                # Can't mix offset aware and floating dates
                "dtstart": "1997-07-15T04:00:00+08:00",
                "dtend": "1997-07-14T17:00:00",
            },
        },
    )
    assert result
    assert not result.get("success")
    assert "error" in result
    assert "code" in result["error"]
    assert result["error"]["code"] == "invalid_format"


async def test_update_invalid_event_id(
    ws_client: ClientFixture,
    setup_integration: None,
    hass: HomeAssistant,
) -> None:
    """Test updating an event with an invalid event uid."""
    client = await ws_client()
    resp = await client.cmd(
        "update",
        {
            "entity_id": TEST_ENTITY,
            "uid": "uid-does-not-exist",
            "event": {
                "summary": "Bastille Day Party [To be rescheduled]",
                "dtstart": "1997-07-14",
                "dtend": "1997-07-15",
            },
        },
    )
    assert resp
    assert not resp.get("success")
    assert "error" in resp
    assert resp["error"].get("code") == "failed"


async def test_delete_invalid_event_id(
    ws_client: ClientFixture,
    setup_integration: None,
    hass: HomeAssistant,
) -> None:
    """Test deleting an event with an invalid event uid."""
    client = await ws_client()
    resp = await client.cmd(
        "delete",
        {
            "entity_id": TEST_ENTITY,
            "uid": "uid-does-not-exist",
        },
    )
    assert resp
    assert not resp.get("success")
    assert "error" in resp
    assert resp["error"].get("code") == "failed"


@pytest.mark.parametrize(
    ("start_date_time", "end_date_time"),
    [
        ("1997-07-14T17:00:00+00:00", "1997-07-15T04:00:00+00:00"),
        ("1997-07-14T11:00:00-06:00", "1997-07-14T22:00:00-06:00"),
    ],
)
async def test_create_event_service(
    hass: HomeAssistant,
    setup_integration: None,
    get_events: GetEventsFn,
    start_date_time: str,
    end_date_time: str,
    config_entry: MockConfigEntry,
) -> None:
    """Test creating an event using the create_event service."""

    await hass.services.async_call(
        "calendar",
        "create_event",
        {
            "start_date_time": start_date_time,
            "end_date_time": end_date_time,
            "summary": "Bastille Day Party",
            "location": "Test Location",
        },
        target={"entity_id": TEST_ENTITY},
        blocking=True,
    )
    # Ensure data is written to disk
    await hass.async_block_till_done()

    events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
            "location": "Test Location",
        }
    ]

    events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T18:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
            "location": "Test Location",
        }
    ]

    # Reload the config entry, which reloads the content from the store and
    # verifies that the persisted data can be parsed correctly.
    await hass.config_entries.async_reload(config_entry.entry_id)
    await hass.async_block_till_done()

    events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T18:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T22:00:00-06:00"},
            "location": "Test Location",
        }
    ]


@pytest.mark.parametrize(
    "ics_content",
    [
        textwrap.dedent(
            """\
            BEGIN:VCALENDAR
            BEGIN:VEVENT
            SUMMARY:Bastille Day Party
            DTSTART:19970714
            DTEND:19970714
            END:VEVENT
            END:VCALENDAR
        """
        ),
        textwrap.dedent(
            """\
            BEGIN:VCALENDAR
            BEGIN:VEVENT
            SUMMARY:Bastille Day Party
            DTSTART:19970714
            DTEND:19970710
            END:VEVENT
            END:VCALENDAR
        """
        ),
    ],
    ids=["no_duration", "negative"],
)
async def test_invalid_all_day_event(
    ws_client: ClientFixture,
    setup_integration: None,
    get_events: GetEventsFn,
) -> None:
    """Test all day events with invalid durations, which are coerced to be valid."""
    events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"date": "1997-07-14"},
            "end": {"date": "1997-07-15"},
        }
    ]


@pytest.mark.parametrize(
    "ics_content",
    [
        textwrap.dedent(
            """\
            BEGIN:VCALENDAR
            BEGIN:VEVENT
            SUMMARY:Bastille Day Party
            DTSTART:19970714T110000
            DTEND:19970714T110000
            END:VEVENT
            END:VCALENDAR
        """
        ),
        textwrap.dedent(
            """\
            BEGIN:VCALENDAR
            BEGIN:VEVENT
            SUMMARY:Bastille Day Party
            DTSTART:19970714T110000
            DTEND:19970710T100000
            END:VEVENT
            END:VCALENDAR
        """
        ),
    ],
    ids=["no_duration", "negative"],
)
async def test_invalid_event_duration(
    ws_client: ClientFixture,
    setup_integration: None,
    get_events: GetEventsFn,
) -> None:
    """Test events with invalid durations, which are coerced to be valid."""
    events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
    assert list(map(event_fields, events)) == [
        {
            "summary": "Bastille Day Party",
            "start": {"dateTime": "1997-07-14T11:00:00-06:00"},
            "end": {"dateTime": "1997-07-14T11:30:00-06:00"},
        }
    ]