from datetime import datetime, timedelta

from backend.calendar_runtime import _google_read_calendar, _outlook_read_calendar
from backend.config import _load_settings


def _parse_iso_field(block: str, label: str) -> datetime | None:
    for line in block.splitlines():
        if line.strip().startswith(label + ":"):
            raw = line.partition(label + ":")[2].strip()
            if not raw or raw == "-":
                return None
            try:
                return datetime.fromisoformat(raw.replace("Z", "+00:00").split("+")[0])
            except ValueError:
                return None
    return None


def _find_free_slots(
    date: str = "",
    days: int = 1,
    duration_minutes: int = 30,
    working_start: int = 9,
    working_end: int = 17,
) -> str:
    """Find available time slots by reading existing calendar events."""
    settings = _load_settings()
    use_outlook = bool(settings.get("outlook_enabled"))
    use_google = bool(settings.get("google_enabled"))
    if not use_outlook and not use_google:
        return "No calendar provider configured."

    now_local = datetime.now()
    if date:
        try:
            base = datetime.strptime(date[:10], "%Y-%m-%d")
        except ValueError:
            base = now_local
    else:
        base = now_local

    busy: list[tuple[datetime, datetime]] = []
    since_str = base.strftime("%Y-%m-%d")
    until_dt = base + timedelta(days=max(1, days))
    until_str = until_dt.strftime("%Y-%m-%d")

    if use_outlook:
        try:
            raw = _outlook_read_calendar(days_ahead=0, since=since_str, until=until_str)
            for block in raw.split("---"):
                start_value = _parse_iso_field(block, "Start ISO")
                end_value = _parse_iso_field(block, "End ISO")
                if start_value and end_value:
                    busy.append((start_value, end_value))
        except Exception:
            pass

    if use_google:
        try:
            raw = _google_read_calendar(days_ahead=0, since=since_str, until=until_str)
            for block in raw.split("---"):
                start_value = _parse_iso_field(block, "Start ISO")
                end_value = _parse_iso_field(block, "End ISO")
                if start_value and end_value:
                    busy.append((start_value, end_value))
        except Exception:
            pass

    busy.sort()
    lines: list[str] = []
    for day_offset in range(max(1, days)):
        day = base + timedelta(days=day_offset)
        day_start = day.replace(hour=working_start, minute=0, second=0, microsecond=0)
        day_end = day.replace(hour=working_end, minute=0, second=0, microsecond=0)
        if day.date() == now_local.date() and now_local > day_start:
            mins = now_local.minute
            rounded = mins + (15 - mins % 15) if mins % 15 else mins
            day_start = now_local.replace(minute=0, second=0, microsecond=0) + timedelta(minutes=rounded)
        cursor = day_start
        day_label = day.strftime("%A %d %B %Y")
        day_slots: list[str] = []
        for busy_start, busy_end in busy:
            if busy_end <= cursor or busy_start >= day_end:
                continue
            if busy_start > cursor:
                gap = (busy_start - cursor).total_seconds() / 60
                if gap >= duration_minutes:
                    day_slots.append(f"  {cursor.strftime('%H:%M')} - {busy_start.strftime('%H:%M')} ({int(gap)} min)")
            cursor = max(cursor, busy_end)
        if cursor < day_end:
            gap = (day_end - cursor).total_seconds() / 60
            if gap >= duration_minutes:
                day_slots.append(f"  {cursor.strftime('%H:%M')} - {day_end.strftime('%H:%M')} ({int(gap)} min)")
        if day_slots:
            lines.append(f"{day_label}:")
            lines.extend(day_slots)
        else:
            lines.append(f"{day_label}: geen vrije slots van {duration_minutes}+ min")

    return "\n".join(lines) if lines else "Geen vrije slots gevonden."