"""Tests checking that parsing works"""

import base64
from datetime import datetime

import pytest

from icalendar import vBinary, vRecur
from icalendar.cal.calendar import Calendar
from icalendar.cal.event import Event
from icalendar.parser import Contentline, Parameters, unescape_char


@pytest.mark.parametrize(
    "calendar_name",
    [
        # Issue #178 - A component with an unknown/invalid name is represented
        # as one of the known components, the information about the original
        # component name is lost.
        # https://github.com/collective/icalendar/issues/178 https://github.com/collective/icalendar/pull/180
        # Parsing of a nonstandard component
        "issue_178_component_with_invalid_name_represented",
        # Nonstandard component inside other components, also has properties
        "issue_178_custom_component_inside_other",
        # Nonstandard component is able to contain other components
        "issue_178_custom_component_contains_other",
    ],
)
def test_calendar_to_ical_is_inverse_of_from_ical(calendars, calendar_name):
    calendar = getattr(calendars, calendar_name)
    assert calendar.to_ical().splitlines() == calendar.raw_ics.splitlines()
    assert calendar.to_ical() == calendar.raw_ics


@pytest.mark.parametrize(
    ("raw_content_line", "expected_output"),
    [
        # Issue #142 - Multivalued parameters. This is needed for VCard 3.0.
        # see https://github.com/collective/icalendar/pull/142
        (
            "TEL;TYPE=HOME,VOICE:000000000",
            ("TEL", Parameters({"TYPE": ["HOME", "VOICE"]}), "000000000"),
        ),
        # Issue #143 - Allow dots in property names. Another vCard related issue.
        # see https://github.com/collective/icalendar/pull/143
        (
            "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany",
            (
                "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR",
                Parameters(),
                ";;This is the Adress 08; Some City;;12345;Germany",
            ),
        ),
        (
            "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL:",
            (
                "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL",
                Parameters(),
                "",
            ),
        ),
    ],
)
def test_content_lines_parsed_properly(raw_content_line, expected_output):
    assert Contentline.from_ical(raw_content_line).parts() == expected_output


@pytest.mark.parametrize(
    "timezone_info",
    [
        # General timezone aware dates in ical string
        (b"DTSTART;TZID=America/New_York:20130907T120000"),
        (b"DTEND;TZID=America/New_York:20130907T170000"),
        # Specific timezone aware exdates in ical string
        (b"EXDATE;TZID=America/New_York:20131012T120000"),
        (b"EXDATE;TZID=America/New_York:20131011T120000"),
    ],
)
def test_timezone_info_present_in_ical_issue_112(events, timezone_info):
    """Issue #112 - No timezone info on EXDATE

    https://github.com/collective/icalendar/issues/112
    """
    assert timezone_info in events.issue_112_missing_tzinfo_on_exdate.to_ical()


def test_timezone_name_parsed_issue_112(events):
    """Issue #112 - No timezone info on EXDATE

    https://github.com/collective/icalendar/issues/112
    """
    assert (
        events.issue_112_missing_tzinfo_on_exdate["exdate"][0].dts[0].dt.tzname()
        == "EDT"
    )


def test_issue_157_removes_trailing_semicolon(events):
    """Issue #157 - Recurring rules and trailing semicolons

    https://github.com/collective/icalendar/pull/157
    """
    recur = events.issue_157_removes_trailing_semicolon.decoded("RRULE")
    assert isinstance(recur, vRecur)
    assert recur.to_ical() == b"FREQ=YEARLY;BYDAY=1SU;BYMONTH=11"


@pytest.mark.parametrize(
    "event_name",
    [
        # https://github.com/collective/icalendar/pull/100
        ("issue_100_transformed_doctests_into_unittests"),
        ("issue_184_broken_representation_of_period"),
        # PERIOD should be put back into shape
        "issue_156_RDATE_with_PERIOD",
        "issue_156_RDATE_with_PERIOD_list",
        "event_with_unicode_organizer",
    ],
)
def test_event_to_ical_is_inverse_of_from_ical(events, event_name):
    """Make sure that an event's ICS is equal to the ICS it was made from."""
    event = events[event_name]
    assert event.to_ical().splitlines() == event.raw_ics.splitlines()
    assert event.to_ical() == event.raw_ics


def test_decode_rrule_attribute_error_issue_70(events):
    # Issue #70 - e.decode("RRULE") causes Attribute Error
    # see https://github.com/collective/icalendar/issues/70
    recur = events.issue_70_rrule_causes_attribute_error.decoded("RRULE")
    assert isinstance(recur, vRecur)
    assert recur.to_ical() == b"FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1"


def test_description_parsed_properly_issue_53(events):
    """Issue #53 - Parsing failure on some descriptions?

    https://github.com/collective/icalendar/issues/53
    """
    assert (
        b"July 12 at 6:30 PM"
        in events.issue_53_description_parsed_properly["DESCRIPTION"].to_ical()
    )


def test_raises_value_error_for_properties_without_parent_pull_179():
    """Found an issue where from_ical() would raise IndexError for
    properties without parent components.

    https://github.com/collective/icalendar/pull/179
    """
    with pytest.raises(ValueError):
        Calendar.from_ical("VERSION:2.0")


def test_tzid_parsed_properly_issue_53(timezones):
    """Issue #53 - Parsing failure on some descriptions?

    https://github.com/collective/icalendar/issues/53
    """
    assert (
        timezones.issue_53_tzid_parsed_properly["tzid"].to_ical() == b"America/New_York"
    )


def test_timezones_to_ical_is_inverse_of_from_ical(timezones):
    """Issue #55 - Parse error on utc-offset with seconds value
    see https://github.com/collective/icalendar/issues/55"""
    timezone = timezones["issue_55_parse_error_on_utc_offset_with_seconds"]
    assert timezone.to_ical() == timezone.raw_ics


@pytest.mark.parametrize(
    ("date", "expected_output"),
    [
        (datetime(2012, 7, 16, 0, 0, 0), b"DTSTART:20120716T000000Z"),
        (datetime(2021, 11, 17, 15, 9, 15), b"DTSTART:20211117T150915Z"),
    ],
)
def test_no_tzid_when_utc(utc, date, expected_output):
    """Issue #58  - TZID on UTC DATE-TIMEs
       Issue #335 - UTC timezone identification is broken

    https://github.com/collective/icalendar/issues/58
    https://github.com/collective/icalendar/issues/335
    """
    # According to RFC 5545: "The TZID property parameter MUST NOT be
    # applied to DATE-TIME or TIME properties whose time values are
    # specified in UTC.
    date = date.replace(tzinfo=utc)
    event = Event()
    event.add("dtstart", date)
    assert expected_output in event.to_ical()


def test_vBinary_base64_encoded_issue_82():
    """Issue #82 - vBinary __repr__ called rather than to_ical from
                   container types
    https://github.com/collective/icalendar/issues/82
    """
    b = vBinary("text")
    b.params["FMTTYPE"] = "text/plain"
    assert b.to_ical() == base64.b64encode(b"text")


def test_creates_event_with_base64_encoded_attachment_issue_82(events):
    """Issue #82 - vBinary __repr__ called rather than to_ical from
                   container types
    https://github.com/collective/icalendar/issues/82
    """
    b = vBinary("text")
    b.params["FMTTYPE"] = "text/plain"
    event = Event()
    event.add("ATTACH", b)
    assert event.to_ical() == events.issue_82_expected_output.raw_ics


@pytest.mark.parametrize(
    "calendar_name",
    [
        # Issue #466 - [BUG] TZID timezone is ignored when forward-slash is used
        # https://github.com/collective/icalendar/issues/466
        "issue_466_respect_unique_timezone",
        "issue_466_convert_tzid_with_slash",
    ],
)
def test_handles_unique_tzid(calendars, in_timezone, calendar_name):
    calendar = calendars[calendar_name]
    event = calendar.walk("VEVENT")[0]
    print(vars(event))
    start_dt = event["dtstart"].dt
    end_dt = event["dtend"].dt
    assert start_dt == in_timezone(datetime(2022, 10, 21, 20, 0, 0), "Europe/Stockholm")
    assert end_dt == in_timezone(datetime(2022, 10, 21, 21, 0, 0), "Europe/Stockholm")


@pytest.mark.parametrize(
    ("event_name", "expected_cn", "expected_ics"),
    [
        (
            "event_with_escaped_characters",
            r"that, that; %th%%at%\ that:",
            "это, то; that\\ %th%%at%:",
        ),
        ("event_with_escaped_character1", r"Society, 2014", "that"),
        ("event_with_escaped_character2", r"Society\ 2014", "that"),
        ("event_with_escaped_character3", r"Society; 2014", "that"),
        ("event_with_escaped_character4", r"Society: 2014", "that"),
    ],
)
def test_escaped_characters_read(event_name, expected_cn, expected_ics, events):
    event = events[event_name]
    assert event["ORGANIZER"].params["CN"] == expected_cn
    assert event["ORGANIZER"].to_ical() == expected_ics.encode("utf-8")


def test_unescape_char():
    assert unescape_char(b"123") == b"123"
    assert unescape_char(b"\\n") == b"\n"
