from __future__ import annotations
import re
import sedate
from itertools import chain
from html import unescape
from onegov.core import mail
from onegov.feriennet import _
from onegov.feriennet.exports.const import ACTIVITY_STATES
from onegov.feriennet.exports.const import BOOKING_STATES
from onegov.feriennet.exports.const import FREQUENCIES
from onegov.feriennet.exports.const import GENDERS
from onegov.feriennet.exports.const import ROLES
from onegov.feriennet.exports.const import SALUTATIONS
from onegov.feriennet.exports.const import STATES
from onegov.feriennet.utils import decode_name
from onegov.org.models import Export
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
    from collections.abc import Iterable, Iterator
    from onegov.activity.models import (
        Activity, ActivityInvoiceItem, Attendee, Booking,
        Occasion, OccasionNeed, Volunteer)
    from onegov.user import User
[docs]
SPACES = re.compile(r'[ ]+') 
[docs]
STREET = re.compile(r"""
    # street name
    (?P<street>[\D\-\.\s]+)
    # punctuation between
    [\s,]*
    # street number
    (?P<number>[0-9]{1}[0-9]*\s?[\w]?)?
    """, re.UNICODE | re.VERBOSE) 
[docs]
class Street:
[docs]
    __slots__ = ('name', 'number') 
    def __init__(self, name: str | None, number: str | None) -> None:
[docs]
        self.name = name and name.strip(' \n,').title() 
[docs]
        self.number = number and number.strip(' \n,').lower().replace(' ', '') 
 
[docs]
def score_street_match(match: re.Match[str] | None) -> int:
    score = 0
    if match:
        if match.group('street'):
            score += 1
        if match.group('street') and 'strasse' in match.group('street'):
            score += 1
        if match.group('number'):
            score += 1
    return score 
[docs]
def remove_duplicate_spaces(text: str) -> str:
    return SPACES.sub(' ', text) 
[docs]
def html_to_text(html: str | None) -> str:
    if not html:
        return ''
    return remove_duplicate_spaces(mail.html_to_text(
        unescape(html),
        ul_item_mark='•',
        strong_mark='',
        emphasis_mark=''
    )) 
[docs]
class FeriennetExport(Export):
[docs]
    def activity_fields(
        self,
        activity: Activity
    ) -> Iterator[tuple[str, Any]]:
        yield _('Activity Title'), activity.title
        yield _('Activity Lead'), activity.lead
        yield _('Activity Text'), html_to_text(activity.text)
        yield _('Activity Text (HTML)'), activity.text
        yield _('Activity Status'), ACTIVITY_STATES[activity.state]
        yield _('Activity Location'), activity.location
        yield _('Activity Tags'), '\n'.join(sorted(activity.tags or [])) 
[docs]
    def booking_fields(
        self,
        booking: Booking
    ) -> Iterator[tuple[str, Any]]:
        yield _('Booking State'), BOOKING_STATES[booking.state]
        yield _('Booking Priority'), booking.priority
        yield _('Booking Cost'), booking.cost
        local_booking_time = sedate.to_timezone(
            booking.created, booking.period.timezone)
        yield _('Booking Date'), local_booking_time.date() 
[docs]
    def attendee_fields(
        self,
        attendee: Attendee
    ) -> Iterator[tuple[str, Any]]:
        first_name, last_name = decode_name(attendee.name)
        yield _('Attendee First Name'), first_name or ''
        yield _('Attendee Last Name'), last_name or ''
        yield _('Attendee Birth Date'), attendee.birth_date
        yield _('Attendee Gender'), GENDERS.get(attendee.gender, '')
        yield _('Attendee Notes'), attendee.notes
        yield _('Attendee SwissPass ID'), attendee.swisspass or ''
        yield _('Attendee Booking-Limit'), attendee.limit or '' 
[docs]
    def occasion_fields(
        self,
        occasion: Occasion
    ) -> Iterator[tuple[str, Any]]:
        dates = [
            (d.localized_start, d.localized_end)
            for d in occasion.dates
        ]
        yield _('Occasion Rescinded'), occasion.cancelled
        yield _('Occasion Dates'), dates
        yield _('Occasion Note'), occasion.note
        yield _('Occasion Age'), '{} - {}'.format(
            occasion.age.lower, occasion.age.upper - 1)
        yield _('Occasion Spots'), '{} - {}'.format(
            occasion.spots.lower, occasion.spots.upper - 1)
        yield _('Occasion Cost'), occasion.cost or 0
        yield _('Occasion Custom Booking Cost'), occasion.booking_cost or 0
        yield _('Occasion Meeting Point'), occasion.meeting_point
        yield _('Occasion May Overlap'), occasion.exclude_from_overlap_check 
[docs]
    def occasion_need_fields(
        self,
        need: OccasionNeed
    ) -> Iterator[tuple[str, Any]]:
        yield _('Need Number'), '{} - {}'.format(
            need.number.lower, need.number.upper - 1)
        yield _('Need Name'), need.name
        yield _('Need Description'), need.description 
[docs]
    def user_fields(self, user: User) -> Iterator[tuple[str, Any]]:
        user_data = user.data or {}
        salutation = user_data.get('salutation')
        first_name, last_name = decode_name(user.realname)
        status_email = user_data.get('ticket_statistics', 'never')
        street = extract_street(user_data.get('address', None))
        yield _('User Login'), user.username
        yield _('User Role'), ROLES[user.role]
        yield _('User Active'), user.active
        yield _('User Tags'), user_data.get('tags', ())
        yield _('User Salutation'), SALUTATIONS.get(salutation, '')
        yield _('User First Name'), first_name or ''
        yield _('User Last Name'), last_name or ''
        yield _('User Organisation'), user_data.get('organisation', '')
        yield _('User Address'), user_data.get('address', '')
        yield _('User Street'), street.name or ''
        yield _('User Street Number'), street.number or ''
        yield _('User Zipcode'), user_data.get('zip_code', '')
        yield _('User Location'), user_data.get('place', '')
        yield (
            _('User Political Municipality'),
            user_data.get('political_municipality', '')
        )
        yield _('User E-Mail'), user_data.get('email', '')
        yield _('User Phone'), user_data.get('phone', '')
        yield _('User Emergency'), user_data.get('emergency', '')
        yield _('User Website'), user_data.get('website', '')
        yield _('User Bank Account'), user_data.get('bank_account', '')
        yield _('User Beneficiary'), user_data.get('bank_beneficiary', '')
        yield _('User Status E-Mail'), FREQUENCIES.get(status_email, '')
        yield _('User TOS Accepted'), user_data.get('tos_accepted', False) 
[docs]
    def invoice_item_fields(
        self,
        item: ActivityInvoiceItem
    ) -> Iterator[tuple[str, Any]]:
        yield _('Invoice Item Group'), item.group
        yield _('Invoice Item Text'), item.text
        yield _('Invoice Item Paid'), item.paid
        yield _('Invoice Item Payment date'), item.payment_date
        yield _('Invoice Item Transaction ID'), item.tid or ''
        yield _('Invoice Item Source'), item.source or ''
        yield _('Invoice Item Unit'), item.unit
        yield _('Invoice Item Quantity'), item.quantity
        yield _('Invoice Item Amount'), item.amount
        yield _('Invoice Item References'), '\n'.join(
            r.readable for r in item.invoice.references
        ) 
[docs]
    def invoice_attendee_fields(
        self,
        attendee: Attendee
    ) -> Iterator[tuple[str, Any]]:
        yield _('Attendee Address'), attendee.address if attendee else ''
        yield _('Attendee Zipcode'), attendee.zip_code if attendee else ''
        yield _('Attendee Place'), attendee.place if attendee else ''
        yield _('Attendee Political Municipality'
                ), attendee.political_municipality if attendee else ''
        yield _('Attendee SwissPass ID'
                ), attendee.swisspass if attendee else '' 
[docs]
    def organiser_fields(
        self,
        organiser: User
    ) -> Iterator[tuple[str, Any]]:
        user_data = organiser.data or {}
        first_name, last_name = decode_name(organiser.realname)
        street = extract_street(user_data.get('address', None))
        yield _('Organiser Last Name'), last_name or ''
        yield _('Organiser First Name'), first_name or ''
        yield _('Organiser Organisation'), user_data.get('organisation', '')
        yield _('Organiser Address'), user_data.get('address', '')
        yield _('Organiser Street'), street.name or ''
        yield _('Organiser Street Number'), street.number or ''
        yield _('Organiser Zipcode'), user_data.get('zip_code', '')
        yield _('Organiser Location'), user_data.get('place', '')
        yield _('Organiser E-Mail'), user_data.get('email', '')
        yield _('Organiser Phone'), user_data.get('phone', '')
        yield _('Organiser Website'), user_data.get('website', '')
        yield _('Organiser Bank Account'), user_data.get('bank_account', '')
        yield _('Organiser Beneficiary'), user_data.get('bank_beneficiary', '') 
[docs]
    def volunteer_fields(
        self,
        volunteer: Volunteer
    ) -> Iterator[tuple[str, Any]]:
        need = volunteer.need
        occasion = need.occasion
        activity = occasion.activity
        dates = [
            (d.localized_start, d.localized_end)
            for d in occasion.dates
        ]
        yield _('Activity Title'), activity.title
        yield _('Occasion Dates'), dates
        yield _('Occasion Rescinded'), occasion.cancelled
        yield _('Need Name'), need.name
        yield _('Need Number'), '{} - {}'.format(
            need.number.lower, need.number.upper - 1)
        yield _('Confirmed Volunteers'), sum(
            v.state == 'confirmed' for v in need.volunteers)
        yield _('Volunteer State'), STATES[volunteer.state]
        yield _('First Name'), volunteer.first_name
        yield _('Last Name'), volunteer.last_name
        yield _('Birth Date'), volunteer.birth_date
        yield _('Organisation'), volunteer.organisation
        yield _('Place'), volunteer.place
        yield _('E-Mail'), volunteer.email
        yield _('Phone'), volunteer.phone
        yield _('Address'), volunteer.address