'''Tests checking that parsing works'''
import pytest
import base64
from icalendar import Calendar, vRecur, vBinary, Event
from datetime import datetime
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
    '''
    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"
