from datetime import datetime, timedelta, timezone

from backend.config import (
    GOOGLE_API_SUPPORT,
    OUTLOOK_SUPPORT,
    _google_has_calendar_write_scope,
    _google_service,
    _outlook_session,
)
from backend.mail_helpers_runtime import (
    _is_date_only,
    _outlook_build_calendar_restriction,
    _outlook_format_dt,
    _outlook_parse_datetime,
)
from backend.local_time import (
    get_windows_localzone,
    get_windows_localzone_name,
    get_windows_now,
    localize_windows_datetime,
    to_windows_naive_datetime,
    windows_utc_offset,
)


def _local_tz_offset(value: datetime | None = None) -> str:
    """Return the current local UTC offset as '+02:00' / '-05:00'."""
    return windows_utc_offset(value)


def _google_build_datetime_payload(raw_value: str) -> tuple[str, str]:
    parsed = _outlook_parse_datetime(raw_value)
    if parsed is None:
        raise ValueError("invalid datetime")
    local_dt = localize_windows_datetime(parsed)
    return local_dt.isoformat(timespec="seconds"), get_windows_localzone_name()


_DAY_NAMES_NL = ["maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag", "zondag"]
_MONTH_NAMES_NL = ["", "januari", "februari", "maart", "april", "mei", "juni",
                   "juli", "augustus", "september", "oktober", "november", "december"]


def _fmt_dt_nl(dt: datetime, include_time: bool = True) -> str:
    """Format a datetime as 'vrijdag 13 maart 2026, 14:00'."""
    day_name = _DAY_NAMES_NL[dt.weekday()]
    month_name = _MONTH_NAMES_NL[dt.month]
    result = f"{day_name} {dt.day} {month_name} {dt.year}"
    if include_time:
        result += f", {dt.hour:02d}:{dt.minute:02d}"
    return result


def _format_google_event_time(event_part: dict) -> str:
    value = dict(event_part or {})
    date_time = str(value.get("dateTime") or "").strip()
    if date_time:
        try:
            return _fmt_dt_nl(datetime.fromisoformat(date_time.replace("Z", "+00:00")).astimezone(get_windows_localzone()))
        except Exception:
            return date_time
    date_only = str(value.get("date") or "").strip()
    if date_only:
        try:
            return _fmt_dt_nl(datetime.strptime(date_only, "%Y-%m-%d"), include_time=False) + " (hele dag)"
        except Exception:
            return date_only
    return "-"


def _outlook_get_tasks(count: int = 20, include_completed: bool = False) -> str:
    """Fetch tasks from Outlook / Microsoft To-Do."""
    if not OUTLOOK_SUPPORT:
        return "Outlook not available: pywin32 not installed."
    try:
        status_labels = {0: "Not Started", 1: "In Progress", 2: "Complete", 3: "Waiting", 4: "Deferred"}
        importance_labels = {0: "Low", 1: "Normal", 2: "High"}
        with _outlook_session() as (_, ns):
            tasks_folder = ns.GetDefaultFolder(13)
            items = tasks_folder.Items
            items.Sort("[DueDate]", False)
            results: list[str] = []
            total = int(items.Count)
            for idx in range(1, total + 1):
                if len(results) >= count:
                    break
                try:
                    task = items.Item(idx)
                    status = int(getattr(task, "Status", 0))
                    if not include_completed and status == 2:
                        continue
                    subject = str(getattr(task, "Subject", "") or "").strip()
                    if not subject:
                        continue
                    due = getattr(task, "DueDate", "")
                    due_str = _outlook_format_dt(due) if due and str(due) != "01/01/4501" else ""
                    importance = int(getattr(task, "Importance", 1))
                    body = str(getattr(task, "Body", "") or "").strip()[:200]
                    results.append(
                        f"Subject: {subject}\n"
                        f"Status: {status_labels.get(status, 'Unknown')}\n"
                        f"Due: {due_str or 'No due date'}\n"
                        f"Importance: {importance_labels.get(importance, 'Normal')}\n"
                        f"Notes: {body or '(none)'}"
                    )
                except Exception:
                    pass
        if not results:
            return "No tasks found."
        return "\n\n---\n\n".join(results)
    except Exception as e:
        return f"Outlook tasks error: {e}"


def _outlook_read_calendar(days_ahead: int = 7, since: str = "", until: str = "") -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available: pywin32 not installed."
    try:
        with _outlook_session() as (_, ns):
            cal = ns.GetDefaultFolder(9)
            items = cal.Items
            items.IncludeRecurrences = True
            items.Sort("[Start]")
            range_start, _, restriction = _outlook_build_calendar_restriction(
                days_ahead=days_ahead,
                since=since,
                until=until,
            )
            restricted = items.Restrict(restriction)
            results = []
            for item in restricted:
                try:
                    results.append(_outlook_calendar_item_summary(item))
                except Exception:
                    pass
        header = f"Vandaag is {_fmt_dt_nl(range_start, include_time=False)}\n\n"
        empty_result = "No events found for the requested period." if since or until else "No upcoming events."
        return (header + "\n\n---\n\n".join(results)) if results else empty_result
    except Exception as e:
        return f"Outlook calendar error: {e}"


def _outlook_calendar_display_range(item) -> tuple[str, str]:
    import datetime as _dt

    start_value = getattr(item, "Start", "")
    end_value = getattr(item, "End", "")
    all_day = bool(getattr(item, "AllDayEvent", False))
    if isinstance(start_value, _dt.datetime):
        start_value = localize_windows_datetime(start_value)
    if isinstance(end_value, _dt.datetime):
        end_value = localize_windows_datetime(end_value)
    if all_day and isinstance(start_value, _dt.datetime) and isinstance(end_value, _dt.datetime):
        display_end = end_value - _dt.timedelta(days=1)
        start_str = _fmt_dt_nl(start_value, include_time=False) + " (hele dag)"
        end_str = _fmt_dt_nl(display_end, include_time=False) + " (hele dag)"
        return start_str, end_str
    start_str = _fmt_dt_nl(start_value) if isinstance(start_value, _dt.datetime) else str(start_value)
    end_str = _fmt_dt_nl(end_value) if isinstance(end_value, _dt.datetime) else str(end_value)
    return start_str, end_str


def _outlook_calendar_item_summary(item, include_notes: bool = True) -> str:
    import datetime as _dt

    start_str, end_str = _outlook_calendar_display_range(item)
    start_value = getattr(item, "Start", None)
    end_value = getattr(item, "End", None)
    if isinstance(start_value, _dt.datetime):
        start_value = localize_windows_datetime(start_value)
    if isinstance(end_value, _dt.datetime):
        end_value = localize_windows_datetime(end_value)
    all_day = bool(getattr(item, "AllDayEvent", False))
    if all_day:
        start_iso = start_value.strftime("%Y-%m-%d") if isinstance(start_value, _dt.datetime) else ""
        end_iso = end_value.strftime("%Y-%m-%d") if isinstance(end_value, _dt.datetime) else ""
    else:
        start_iso = start_value.strftime("%Y-%m-%dT%H:%M:%S") if isinstance(start_value, _dt.datetime) else ""
        end_iso = end_value.strftime("%Y-%m-%dT%H:%M:%S") if isinstance(end_value, _dt.datetime) else ""
    parts = [
        f"Subject: {getattr(item, 'Subject', '') or '(no title)'}",
        f"Start: {start_str}",
        f"End: {end_str}",
        f"Start ISO: {start_iso}",
        f"End ISO: {end_iso}",
        f"Location: {getattr(item, 'Location', '') or '-'}",
    ]
    if include_notes:
        parts.append(f"Notes: {(getattr(item, 'Body', '') or '')[:2000]}")
    parts.append(f"Event ID: {getattr(item, 'EntryID', '') or '?'}")
    return "\n".join(parts)


def _outlook_default_calendar_end(start_dt: datetime, *, all_day: bool) -> datetime:
    return start_dt + (timedelta(days=1) if all_day else timedelta(hours=1))


def _outlook_coerce_calendar_range(
    start: str,
    end: str,
    *,
    current_start: datetime | None = None,
    current_end: datetime | None = None,
    current_all_day: bool = False,
) -> tuple[datetime, datetime, bool] | tuple[None, None, None]:
    start_raw = str(start or "").strip()
    end_raw = str(end or "").strip()

    if not start_raw and current_start is None:
        return None, None, None

    inferred_all_day = current_all_day
    if start_raw:
        inferred_all_day = _is_date_only(start_raw)
    elif end_raw:
        inferred_all_day = current_all_day or _is_date_only(end_raw)

    start_dt = _outlook_parse_datetime(start_raw) if start_raw else current_start
    if start_dt is None:
        return None, None, None
    if inferred_all_day:
        start_dt = datetime(start_dt.year, start_dt.month, start_dt.day, 0, 0, 0)

    if end_raw:
        end_dt = _outlook_parse_datetime(end_raw)
        if end_dt is None:
            return None, None, None
        if inferred_all_day and _is_date_only(end_raw):
            end_dt = datetime(end_dt.year, end_dt.month, end_dt.day, 0, 0, 0) + timedelta(days=1)
    elif current_end is not None:
        if start_raw and current_start is not None:
            duration = current_end - current_start
            if duration.total_seconds() <= 0:
                duration = timedelta(days=1) if inferred_all_day else timedelta(hours=1)
            end_dt = start_dt + duration
        else:
            end_dt = current_end
    else:
        end_dt = _outlook_default_calendar_end(start_dt, all_day=inferred_all_day)

    start_dt = to_windows_naive_datetime(start_dt)
    end_dt = to_windows_naive_datetime(end_dt)
    if end_dt <= start_dt:
        return None, None, None
    return start_dt, end_dt, inferred_all_day


def _outlook_get_calendar_item(ns, event_id: str):
    if not event_id or not str(event_id).strip():
        return None
    try:
        return ns.GetItemFromID(str(event_id).strip())
    except Exception:
        return None


def _outlook_check_conflicts(ns, start_dt: datetime, end_dt: datetime, all_day: bool = False) -> list[str]:
    """Return summaries of Outlook calendar events overlapping the given range."""
    try:
        cal = ns.GetDefaultFolder(9)
        items = cal.Items
        items.IncludeRecurrences = True
        items.Sort("[Start]")
        fmt = "%m/%d/%Y %H:%M %p"
        restriction = f"[Start] < '{end_dt.strftime(fmt)}' AND [End] > '{start_dt.strftime(fmt)}'"
        restricted = items.Restrict(restriction)
        conflicts = []
        for item in restricted:
            try:
                subject = getattr(item, "Subject", "(no title)") or "(no title)"
                start_str, end_str = _outlook_calendar_display_range(item)
                is_all_day = bool(getattr(item, "AllDayEvent", False))
                if all_day and is_all_day:
                    conflicts.append(f"  - {subject} (hele dag)")
                else:
                    conflicts.append(f"  - {subject} ({start_str} – {end_str})")
            except Exception:
                pass
        return conflicts
    except Exception:
        return []


def _google_check_conflicts(service, start_dt: datetime, end_dt: datetime, all_day: bool = False) -> list[str]:
    """Return summaries of Google Calendar events overlapping the given range."""
    try:
        local_tz = get_windows_localzone()
        if all_day:
            time_min = start_dt.strftime("%Y-%m-%dT00:00:00")
            time_max = end_dt.strftime("%Y-%m-%dT00:00:00")
        else:
            time_min = localize_windows_datetime(start_dt).isoformat(timespec="seconds")
            time_max = localize_windows_datetime(end_dt).isoformat(timespec="seconds")
        events_result = service.events().list(
            calendarId="primary", timeMin=time_min, timeMax=time_max,
            maxResults=20, singleEvents=True, orderBy="startTime",
        ).execute()
        conflicts = []
        for event in events_result.get("items", []):
            subject = event.get("summary", "(no title)")
            start_raw = event["start"].get("dateTime", event["start"].get("date", "?"))
            end_raw = event["end"].get("dateTime", event["end"].get("date", "?"))
            try:
                if "T" in start_raw:
                    start_local = datetime.fromisoformat(start_raw.replace("Z", "+00:00")).astimezone(local_tz)
                    start_str = _fmt_dt_nl(start_local)
                else:
                    start_str = start_raw + " (hele dag)"
            except Exception:
                start_str = start_raw
            try:
                if "T" in end_raw:
                    end_local = datetime.fromisoformat(end_raw.replace("Z", "+00:00")).astimezone(local_tz)
                    end_str = _fmt_dt_nl(end_local)
                else:
                    end_str = end_raw
            except Exception:
                end_str = end_raw
            conflicts.append(f"  - {subject} ({start_str} – {end_str})")
        return conflicts
    except Exception:
        return []


def _outlook_create_calendar_event(
    summary: str, start: str, end: str = "",
    description: str = "", location: str = "", attendees: str = "",
) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available: pywin32 not installed."
    summary = str(summary or "").strip()
    if not summary:
        return "ERROR: summary is required to create an Outlook calendar event."
    start_dt, end_dt, all_day = _outlook_coerce_calendar_range(start, end)
    if start_dt is None or end_dt is None:
        return "Outlook calendar create error: invalid start/end. Use 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS'."
    try:
        with _outlook_session() as (outlook, ns):
            item = outlook.CreateItem(1)
            item.Subject = summary
            item.Start = start_dt
            item.End = end_dt
            item.AllDayEvent = all_day
            if location:
                item.Location = location
            if description:
                item.Body = description
            if attendees:
                item.RequiredAttendees = attendees
            item.Save()
            start_str, end_str = _outlook_calendar_display_range(item)
            result = (
                "Calendar event created successfully.\n"
                f"Title: {item.Subject}\n"
                f"Start: {start_str}\n"
                f"End: {end_str}\n"
                f"Location: {item.Location or '-'}\n"
                f"Event ID: {item.EntryID or '?'}"
            )
            conflicts = _outlook_check_conflicts(ns, start_dt, end_dt, all_day)
            conflicts = [conflict for conflict in conflicts if item.Subject not in conflict]
            if conflicts:
                result += "\n\n⚠️ Conflicts detected with existing events:\n" + "\n".join(conflicts)
            return result
    except Exception as e:
        return f"Outlook calendar create error: {e}"


def _outlook_update_calendar_event(
    event_id: str, summary: str = "", start: str = "", end: str = "",
    description: str = "", location: str = "",
) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available: pywin32 not installed."
    if not event_id or not str(event_id).strip():
        return (
            "ERROR: event_id is required. You MUST call outlook_read_calendar first "
            "to obtain the Event ID of the event you want to update."
        )
    try:
        with _outlook_session() as (_, ns):
            item = _outlook_get_calendar_item(ns, event_id)
            if item is None:
                return (
                    f"Outlook calendar update error: Event ID '{event_id}' not found. "
                    "Call outlook_read_calendar first to get valid Event IDs."
                )
            current_start = getattr(item, "Start", None)
            current_end = getattr(item, "End", None)
            current_all_day = bool(getattr(item, "AllDayEvent", False))
            if summary:
                item.Subject = summary
            if description:
                item.Body = description
            if location:
                item.Location = location
            if start or end:
                start_dt, end_dt, all_day = _outlook_coerce_calendar_range(
                    start,
                    end,
                    current_start=current_start,
                    current_end=current_end,
                    current_all_day=current_all_day,
                )
                if start_dt is None or end_dt is None:
                    return "Outlook calendar update error: invalid start/end. Use 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS'."
                item.Start = start_dt
                item.End = end_dt
                item.AllDayEvent = all_day
            item.Save()
            start_str, end_str = _outlook_calendar_display_range(item)
            return (
                "Calendar event updated successfully.\n"
                f"Title: {item.Subject}\n"
                f"Start: {start_str}\n"
                f"End: {end_str}\n"
                f"Location: {item.Location or '-'}\n"
                f"Event ID: {item.EntryID or event_id}"
            )
    except Exception as e:
        return f"Outlook calendar update error: {e}"


def _outlook_delete_calendar_event(event_id: str) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available: pywin32 not installed."
    if not event_id or not str(event_id).strip():
        return (
            "ERROR: event_id is required. You MUST call outlook_read_calendar first "
            "to obtain the Event ID of the event you want to delete."
        )
    try:
        with _outlook_session() as (_, ns):
            item = _outlook_get_calendar_item(ns, event_id)
            if item is None:
                return (
                    f"Outlook calendar delete error: Event ID '{event_id}' not found. "
                    "Call outlook_read_calendar first to get valid Event IDs."
                )
            title = str(getattr(item, "Subject", "") or "")
            item.Delete()
            return (
                "Calendar event deleted successfully.\n"
                f"Title: {title or '-'}\n"
                f"Event ID: {str(event_id).strip()}"
            )
    except Exception as e:
        return f"Outlook calendar delete error: {e}"


def _google_read_calendar(days_ahead: int = 7, since: str = "", until: str = "") -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    service = _google_service("calendar", "v3")
    if not service:
        return "Google not authorized. Ask the user to click 'Authorize Google' in Settings → Integrations."
    try:
        local_tz = get_windows_localzone()
        tz_label = _local_tz_offset()
        now_local = get_windows_now()
        start_dt = now_local.astimezone(timezone.utc)
        end_dt = (now_local + timedelta(days=days_ahead)).astimezone(timezone.utc)
        if since:
            parsed_since = _outlook_parse_datetime(since)
            if parsed_since:
                if _is_date_only(since):
                    parsed_since = datetime(parsed_since.year, parsed_since.month, parsed_since.day, 0, 0, 0)
                start_dt = localize_windows_datetime(parsed_since).astimezone(timezone.utc)
        if until:
            parsed_until = _outlook_parse_datetime(until)
            if parsed_until:
                if _is_date_only(until):
                    parsed_until = datetime(parsed_until.year, parsed_until.month, parsed_until.day, 23, 59, 59)
                end_dt = localize_windows_datetime(parsed_until).astimezone(timezone.utc)
        now = start_dt.isoformat()
        end = end_dt.isoformat()
        events_result = service.events().list(
            calendarId="primary", timeMin=now, timeMax=end,
            maxResults=50, singleEvents=True, orderBy="startTime"
        ).execute()
        events = events_result.get("items", [])
        if not events:
            return "No events found for the requested period." if since or until else f"No upcoming events in the next {days_ahead} day(s)."
        results = []
        for event in events:
            start_raw = event["start"].get("dateTime", "")
            end_raw = event["end"].get("dateTime", "")
            if start_raw:
                try:
                    start_dt_local = datetime.fromisoformat(start_raw.replace("Z", "+00:00")).astimezone(local_tz)
                    start_text = _fmt_dt_nl(start_dt_local)
                except Exception:
                    start_text = start_raw
            else:
                all_day_str = event["start"].get("date", "?")
                try:
                    all_day_dt = datetime.strptime(all_day_str, "%Y-%m-%d")
                    start_text = _fmt_dt_nl(all_day_dt, include_time=False) + " (hele dag)"
                except Exception:
                    start_text = all_day_str + " (hele dag)"
            if end_raw:
                try:
                    end_dt_local = datetime.fromisoformat(end_raw.replace("Z", "+00:00")).astimezone(local_tz)
                    end_text = _fmt_dt_nl(end_dt_local)
                except Exception:
                    end_text = end_raw
            else:
                all_day_str = event["end"].get("date", "?")
                try:
                    all_day_dt = datetime.strptime(all_day_str, "%Y-%m-%d")
                    end_text = _fmt_dt_nl(all_day_dt, include_time=False) + " (hele dag)"
                except Exception:
                    end_text = all_day_str + " (hele dag)"
            start_iso = start_raw or event["start"].get("date", "")
            end_iso = end_raw or event["end"].get("date", "")
            results.append(
                f"Summary: {event.get('summary', '(no title)')}\n"
                f"Start: {start_text}\n"
                f"End: {end_text}\n"
                f"Start ISO: {start_iso}\n"
                f"End ISO: {end_iso}\n"
                f"Location: {event.get('location', '-')}\n"
                f"Event ID: {event.get('id', '?')}"
            )
        header = f"Vandaag is {_fmt_dt_nl(now_local, include_time=False)} (UTC{tz_label})\n\n"
        return header + "\n\n---\n\n".join(results)
    except Exception as e:
        return f"Google Calendar error: {e}"


def _google_create_calendar_event(
    summary: str, start: str, end: str,
    description: str = "", location: str = "", attendees: str = "",
) -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    if not _google_has_calendar_write_scope():
        return (
            "Google Calendar write access not granted. The current token only has read-only calendar scope. "
            "Please re-authorize Google in Settings → Integrations to grant full calendar access."
        )
    service = _google_service("calendar", "v3")
    if not service:
        return "Google not authorized. Ask the user to click 'Authorize Google' in Settings → Integrations."
    try:
        is_all_day = len(start) <= 10
        if is_all_day:
            start_body = {"date": start[:10]}
            end_body = {"date": end[:10]}
        else:
            start_value, tz_name = _google_build_datetime_payload(start)
            end_value, _ = _google_build_datetime_payload(end)
            start_body = {"dateTime": start_value, "timeZone": tz_name}
            end_body = {"dateTime": end_value, "timeZone": tz_name}
        event_body: dict = {
            "summary": summary,
            "start": start_body,
            "end": end_body,
        }
        if description:
            event_body["description"] = description
        if location:
            event_body["location"] = location
        if attendees:
            event_body["attendees"] = [{"email": addr.strip()} for addr in attendees.split(",") if addr.strip()]
        created = service.events().insert(calendarId="primary", body=event_body).execute()
        result = (
            "Calendar event created successfully.\n"
            f"Title: {created.get('summary')}\n"
            f"Start: {_format_google_event_time(created.get('start', {}))}\n"
            f"End: {_format_google_event_time(created.get('end', {}))}\n"
            f"Link: {created.get('htmlLink', '-')}\n"
            f"Event ID: {created.get('id', '?')}"
        )
        try:
            if is_all_day:
                conflict_start = datetime.strptime(start[:10], "%Y-%m-%d")
                conflict_end = datetime.strptime(end[:10], "%Y-%m-%d")
            else:
                parsed_conflict_start = _outlook_parse_datetime(start)
                parsed_conflict_end = _outlook_parse_datetime(end)
                if parsed_conflict_start is None or parsed_conflict_end is None:
                    raise ValueError("invalid conflict datetime")
                conflict_start = localize_windows_datetime(parsed_conflict_start)
                conflict_end = localize_windows_datetime(parsed_conflict_end)
            conflicts = _google_check_conflicts(service, conflict_start, conflict_end, is_all_day)
            created_title = created.get("summary", "")
            conflicts = [conflict for conflict in conflicts if created_title not in conflict]
            if conflicts:
                result += "\n\n⚠️ Conflicts detected with existing events:\n" + "\n".join(conflicts)
        except Exception:
            pass
        return result
    except Exception as e:
        return f"Google Calendar create error: {e}"


def _google_update_calendar_event(
    event_id: str, summary: str = "", start: str = "", end: str = "",
    description: str = "", location: str = "",
) -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    if not event_id or not event_id.strip():
        return (
            "ERROR: event_id is required. You MUST call google_read_calendar first "
            "to obtain the Event ID of the event you want to update."
        )
    if not _google_has_calendar_write_scope():
        return (
            "Google Calendar write access not granted. The current token only has read-only calendar scope. "
            "Please re-authorize Google in Settings → Integrations to grant full calendar access."
        )
    service = _google_service("calendar", "v3")
    if not service:
        return "Google not authorized. Ask the user to click 'Authorize Google' in Settings → Integrations."
    try:
        event = service.events().get(calendarId="primary", eventId=event_id).execute()
        if summary:
            event["summary"] = summary
        if description:
            event["description"] = description
        if location:
            event["location"] = location
        if start:
            is_all_day = len(start) <= 10
            if is_all_day:
                event["start"] = {"date": start[:10]}
            else:
                start_value, tz_name = _google_build_datetime_payload(start)
                event["start"] = {"dateTime": start_value, "timeZone": tz_name}
        if end:
            is_all_day = len(end) <= 10
            if is_all_day:
                event["end"] = {"date": end[:10]}
            else:
                end_value, tz_name = _google_build_datetime_payload(end)
                event["end"] = {"dateTime": end_value, "timeZone": tz_name}
        updated = service.events().update(calendarId="primary", eventId=event_id, body=event).execute()
        return (
            "Calendar event updated successfully.\n"
            f"Title: {updated.get('summary')}\n"
            f"Start: {_format_google_event_time(updated.get('start', {}))}\n"
            f"End: {_format_google_event_time(updated.get('end', {}))}\n"
            f"Link: {updated.get('htmlLink', '-')}\n"
            f"Event ID: {updated.get('id', '?')}"
        )
    except Exception as e:
        err = str(e)
        if "404" in err or "Not Found" in err:
            return (
                f"Google Calendar update error: Event ID '{event_id}' not found. "
                "Call google_read_calendar first to get valid Event IDs."
            )
        return f"Google Calendar update error: {e}"


def _google_delete_calendar_event(event_id: str) -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    if not event_id or not event_id.strip():
        return (
            "ERROR: event_id is required. You MUST call google_read_calendar first "
            "to obtain the Event ID of the event you want to delete."
        )
    if not _google_has_calendar_write_scope():
        return (
            "Google Calendar write access not granted. The current token only has read-only calendar scope. "
            "Please re-authorize Google in Settings → Integrations to grant full calendar access."
        )
    service = _google_service("calendar", "v3")
    if not service:
        return "Google not authorized. Ask the user to click 'Authorize Google' in Settings → Integrations."
    try:
        service.events().delete(calendarId="primary", eventId=event_id).execute()
        return f"Calendar event {event_id} deleted successfully."
    except Exception as e:
        err = str(e)
        if "404" in err or "Not Found" in err:
            return (
                f"Google Calendar delete error: Event ID '{event_id}' not found. "
                "Call google_read_calendar first to get valid Event IDs."
            )
        return f"Google Calendar delete error: {e}"