"""Hilfsfunktionen für client-seitige Zeitstempel und Öffnungszeiten."""
import re
from datetime import datetime, date, timedelta


def safe_client_time(client_time: str | None) -> str:
    """Gibt client_time zurück falls valides ISO-Datetime, sonst UTC-Now."""
    if client_time and re.match(
        r'^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?$', client_time
    ):
        return client_time.replace('T', ' ')[:19]
    return datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")


# OSM-Wochentag-Kürzel → Python-weekday (Mo=0 … So=6)
_DAY = {'Mo': 0, 'Di': 1, 'Mi': 2, 'Do': 3, 'Fr': 4, 'Sa': 5, 'So': 6}
_DAY_EN = {'Mo': 0, 'Tu': 1, 'We': 2, 'Th': 3, 'Fr': 4, 'Sa': 5, 'Su': 6}


def _parse_oh_segments(oh: str) -> list[tuple[set[int], str, str]]:
    """Parst OSM-opening_hours in [(weekdays, open_time, close_time), …].

    Unterstützt: "Mo-Fr 08:00-18:00; Sa 09:00-13:00", "24/7", "Mo,Mi 09:00-17:00"
    """
    if not oh:
        return []
    oh = oh.strip()
    if oh.lower() in ('24/7', '24/7 open'):
        return [({0, 1, 2, 3, 4, 5, 6}, '09:00', '18:00')]

    segments = []
    for part in oh.split(';'):
        part = part.strip()
        m = re.match(
            r'^([A-Za-z,\-]+)\s+(\d{2}:\d{2})-(\d{2}:\d{2})$', part
        )
        if not m:
            continue
        day_spec, open_t, close_t = m.group(1), m.group(2), m.group(3)
        days: set[int] = set()
        for chunk in day_spec.split(','):
            chunk = chunk.strip()
            if '-' in chunk:
                parts = chunk.split('-')
                if len(parts) == 2:
                    d1 = _DAY.get(parts[0], _DAY_EN.get(parts[0]))
                    d2 = _DAY.get(parts[1], _DAY_EN.get(parts[1]))
                    if d1 is not None and d2 is not None:
                        days.update(range(d1, d2 + 1))
            else:
                d = _DAY.get(chunk, _DAY_EN.get(chunk))
                if d is not None:
                    days.add(d)
        if days:
            segments.append((days, open_t, close_t))
    return segments


def next_appointment_slot(
    opening_hours: str | None,
    start_from: date | None = None,
    prefer_morning: bool = True,
) -> tuple[str, str]:
    """Gibt (datum_str, uhrzeit_str) für den nächsten Slot zurück.

    Sucht ab morgen (oder start_from) innerhalb der Öffnungszeiten.
    Fallback: nächster Werktag 09:00 wenn keine Öffnungszeiten vorhanden.
    Uhrzeit = Öffnungszeit + 1h (Puffer), min. 09:00, max. 11:00 (Vormittag).
    """
    base = (start_from or date.today()) + timedelta(days=1)
    segments = _parse_oh_segments(opening_hours or '')

    if not segments:
        # Fallback: nächster Mo–Fr 09:00
        d = base
        for _ in range(14):
            if d.weekday() < 5:  # Mo–Fr
                return d.isoformat(), '09:00'
            d += timedelta(days=1)
        return base.isoformat(), '09:00'

    for _ in range(14):
        wd = base.weekday()
        for days, open_t, close_t in segments:
            if wd in days:
                # Slot: Öffnungszeit + 1h, mindestens 09:00, höchstens 11:00
                h, m = map(int, open_t.split(':'))
                slot_h = max(9, h + 1)
                slot_h = min(slot_h, 11)
                return base.isoformat(), f'{slot_h:02d}:00'
        base += timedelta(days=1)

    return base.isoformat(), '09:00'
