"""Tests for ICSProvider — iCalendar import/export (U8)."""

from __future__ import annotations

import asyncio
from pathlib import Path

import pytest
from icalendar import Calendar

from agentkit.calendar.db import init_calendar_db, list_events as db_list_events
from agentkit.calendar.service import CalendarService
from agentkit.calendar.sync.ics_provider import ICSProvider
from agentkit.server.auth.models import init_auth_db


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def calendar_db_path(tmp_path: Path) -> Path:
    path = tmp_path / "test_calendar.db"
    asyncio.run(init_calendar_db(path))
    return path


@pytest.fixture
def auth_db_path(tmp_path: Path) -> Path:
    path = tmp_path / "test_auth.db"
    asyncio.run(init_auth_db(path))
    return path


@pytest.fixture
def service(calendar_db_path: Path, auth_db_path: Path) -> CalendarService:
    return CalendarService(db_path=calendar_db_path, auth_db_path=auth_db_path)


@pytest.fixture
def provider(service: CalendarService) -> ICSProvider:
    return ICSProvider(service)


USER_ID = "user-1"


# ---------------------------------------------------------------------------
# ICS sample strings
# ---------------------------------------------------------------------------

SIMPLE_ICS = (
    b"BEGIN:VCALENDAR\n"
    b"VERSION:2.0\n"
    b"PRODID:-//Test//Test//EN\n"
    b"BEGIN:VEVENT\n"
    b"UID:simple-uid@test\n"
    b"SUMMARY:Test Event\n"
    b"DTSTART:20260701T100000Z\n"
    b"DTEND:20260701T110000Z\n"
    b"DESCRIPTION:Test description\n"
    b"LOCATION:Room A\n"
    b"END:VEVENT\n"
    b"END:VCALENDAR\n"
)

RECURRING_ICS = (
    b"BEGIN:VCALENDAR\n"
    b"VERSION:2.0\n"
    b"PRODID:-//Test//Test//EN\n"
    b"BEGIN:VEVENT\n"
    b"UID:recur-uid@test\n"
    b"SUMMARY:Weekly Meeting\n"
    b"DTSTART:20260706T100000Z\n"
    b"DTEND:20260706T110000Z\n"
    b"RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=4\n"
    b"END:VEVENT\n"
    b"END:VCALENDAR\n"
)

ALL_DAY_ICS = (
    b"BEGIN:VCALENDAR\n"
    b"VERSION:2.0\n"
    b"PRODID:-//Test//Test//EN\n"
    b"BEGIN:VEVENT\n"
    b"UID:allday-uid@test\n"
    b"SUMMARY:All Day Event\n"
    b"DTSTART;VALUE=DATE:20260701\n"
    b"DTEND;VALUE=DATE:20260702\n"
    b"END:VEVENT\n"
    b"END:VCALENDAR\n"
)


# ---------------------------------------------------------------------------
# Import tests
# ---------------------------------------------------------------------------


async def test_import_simple_ics_creates_event(
    provider: ICSProvider, calendar_db_path: Path
) -> None:
    """Single VEVENT ICS → event created with correct fields."""
    result = await provider.import_ics(SIMPLE_ICS, USER_ID)

    assert result["imported"] == 1
    assert result["skipped"] == 0
    assert result["errors"] == []

    events = await db_list_events(USER_ID, db_path=calendar_db_path)
    assert len(events) == 1
    event = events[0]
    assert event.title == "Test Event"
    assert event.description == "Test description"
    assert event.location == "Room A"
    assert event.start_time == "2026-07-01T10:00:00+00:00"
    assert event.end_time == "2026-07-01T11:00:00+00:00"
    assert event.is_all_day is False
    assert event.external_id == "simple-uid@test"
    assert event.external_provider == "ics"


async def test_import_recurring_ics_preserves_rrule(
    provider: ICSProvider, calendar_db_path: Path
) -> None:
    """VEVENT with RRULE → rrule field set on the created event."""
    result = await provider.import_ics(RECURRING_ICS, USER_ID)

    assert result["imported"] == 1
    assert result["errors"] == []

    events = await db_list_events(USER_ID, db_path=calendar_db_path)
    assert len(events) == 1
    event = events[0]
    assert event.rrule is not None
    assert "FREQ=WEEKLY" in event.rrule
    assert "BYDAY=MO" in event.rrule
    assert "COUNT=4" in event.rrule


async def test_import_all_day_event(provider: ICSProvider, calendar_db_path: Path) -> None:
    """DTSTART is date (not datetime) → is_all_day=True, ISO date stored."""
    result = await provider.import_ics(ALL_DAY_ICS, USER_ID)

    assert result["imported"] == 1
    assert result["errors"] == []

    events = await db_list_events(USER_ID, db_path=calendar_db_path)
    assert len(events) == 1
    event = events[0]
    assert event.is_all_day is True
    assert event.start_time == "2026-07-01"
    assert event.end_time == "2026-07-02"


async def test_import_skips_duplicate_uid(provider: ICSProvider, calendar_db_path: Path) -> None:
    """Importing the same ICS twice → second import skips the duplicate UID."""
    first = await provider.import_ics(SIMPLE_ICS, USER_ID)
    assert first["imported"] == 1
    assert first["skipped"] == 0

    second = await provider.import_ics(SIMPLE_ICS, USER_ID)
    assert second["imported"] == 0
    assert second["skipped"] == 1

    events = await db_list_events(USER_ID, db_path=calendar_db_path)
    assert len(events) == 1  # Still only one event


async def test_import_malformed_ics_raises_error(provider: ICSProvider) -> None:
    """Invalid ICS bytes → ValueError raised (graceful, not a crash)."""
    with pytest.raises(ValueError, match="Failed to parse ICS"):
        await provider.import_ics(b"this is definitely not valid ics at all", USER_ID)


# ---------------------------------------------------------------------------
# Export tests
# ---------------------------------------------------------------------------


async def test_export_produces_valid_ics(provider: ICSProvider, service: CalendarService) -> None:
    """Create event, export, parse result with icalendar → roundtrip."""
    await service.create_event(
        user_id=USER_ID,
        title="Export Test",
        start_time="2026-07-01T10:00:00+00:00",
        end_time="2026-07-01T11:00:00+00:00",
        description="Desc",
        location="Room B",
    )

    events = await service.list_events(USER_ID)
    ics_bytes = provider.export_ics(events)

    # Parse the exported ICS back
    cal = Calendar.from_ical(ics_bytes)
    vevents = list(cal.walk("VEVENT"))
    assert len(vevents) == 1
    vevent = vevents[0]
    assert str(vevent.get("SUMMARY")) == "Export Test"
    assert str(vevent.get("DESCRIPTION")) == "Desc"
    assert str(vevent.get("LOCATION")) == "Room B"
    # DTSTART should be a datetime (not all-day)
    dtstart = vevent.get("DTSTART").dt
    assert dtstart.year == 2026
    assert dtstart.month == 7
    assert dtstart.day == 1


async def test_export_includes_recurrence(provider: ICSProvider, service: CalendarService) -> None:
    """Event with rrule → exported ICS contains RRULE line."""
    await service.create_event(
        user_id=USER_ID,
        title="Recurring Export",
        start_time="2026-07-06T10:00:00+00:00",
        end_time="2026-07-06T11:00:00+00:00",
        rrule="FREQ=WEEKLY;BYDAY=MO;COUNT=4",
    )

    events = await service.list_events(USER_ID)
    ics_bytes = provider.export_ics(events)

    cal = Calendar.from_ical(ics_bytes)
    vevents = list(cal.walk("VEVENT"))
    assert len(vevents) == 1
    vevent = vevents[0]
    rrule = vevent.get("RRULE")
    assert rrule is not None
    rrule_str = rrule.to_ical().decode("utf-8")
    assert "FREQ=WEEKLY" in rrule_str
    assert "BYDAY=MO" in rrule_str
    assert "COUNT=4" in rrule_str


async def test_export_date_range_filter(provider: ICSProvider, service: CalendarService) -> None:
    """3 events, export range covering 2 → only 2 in ICS output."""
    # Event 1: July 1
    await service.create_event(
        user_id=USER_ID,
        title="Event 1",
        start_time="2026-07-01T10:00:00+00:00",
        end_time="2026-07-01T11:00:00+00:00",
    )
    # Event 2: July 5
    await service.create_event(
        user_id=USER_ID,
        title="Event 2",
        start_time="2026-07-05T10:00:00+00:00",
        end_time="2026-07-05T11:00:00+00:00",
    )
    # Event 3: July 20
    await service.create_event(
        user_id=USER_ID,
        title="Event 3",
        start_time="2026-07-20T10:00:00+00:00",
        end_time="2026-07-20T11:00:00+00:00",
    )

    # Export range: July 1 – July 10 (covers Event 1 and Event 2)
    events = await service.list_events(
        USER_ID,
        start="2026-07-01T00:00:00+00:00",
        end="2026-07-10T00:00:00+00:00",
    )
    ics_bytes = provider.export_ics(events)

    cal = Calendar.from_ical(ics_bytes)
    vevents = list(cal.walk("VEVENT"))
    assert len(vevents) == 2
    summaries = {str(v.get("SUMMARY")) for v in vevents}
    assert summaries == {"Event 1", "Event 2"}
