"""Calendar integration for smart memory surfacing."""

from __future__ import annotations

import logging
import platform
import subprocess
import time
from typing import Any, Callable

logger = logging.getLogger(__name__)


class CalendarIntegration:
    """Sync with calendar events and surface relevant memories."""

    def __init__(
        self,
        recall_fn: Callable[..., list[Any]],
        entity_lookup_fn: Callable[[str], Any],
    ):
        self._recall = recall_fn
        self._entity_lookup = entity_lookup_fn
        self._last_check = 0.0
        self._notified_events: set[str] = set()

    def get_upcoming_events(self, hours_ahead: int = 2) -> list[dict[str, Any]]:
        """Get upcoming macOS Calendar events within the given time horizon."""
        if platform.system() != "Darwin":
            return []
        script = f"""
        set now to current date
        set later to now + ({int(hours_ahead)} * 3600)
        tell application "Calendar"
            set allEvents to {{}}
            repeat with cal in calendars
                set evts to (every event of cal whose start date >= now and start date <= later)
                repeat with evt in evts
                    set evtTitle to summary of evt
                    set evtStart to start date of evt
                    set evtNotes to ""
                    try
                        set evtNotes to description of evt
                    end try
                    set attendeeNames to {{}}
                    try
                        repeat with att in attendees of evt
                            set end of attendeeNames to display name of att
                        end repeat
                    end try
                    set end of allEvents to evtTitle & "|" & (evtStart as string) & "|" & evtNotes & "|" & (attendeeNames as string)
                end repeat
            end repeat
            return allEvents
        end tell
        """
        try:
            result = subprocess.run(
                ["osascript", "-e", script],
                capture_output=True,
                text=True,
                timeout=10,
                check=False,
            )
        except Exception as exc:
            logger.debug("Calendar access failed: %s", exc)
            return []

        if result.returncode != 0:
            return []

        output = (result.stdout or "").strip()
        if not output:
            return []

        events: list[dict[str, Any]] = []
        for line in output.split(", "):
            parts = [part.strip() for part in line.split("|")]
            if len(parts) < 2:
                continue
            events.append(
                {
                    "title": parts[0],
                    "start": parts[1],
                    "notes": parts[2] if len(parts) > 2 else "",
                    "attendees": parts[3] if len(parts) > 3 else "",
                }
            )
        return events

    def surface_memories_for_event(self, event: dict[str, Any], limit: int = 10) -> list[dict[str, Any]]:
        """Find memories relevant to an upcoming calendar event."""
        queries: list[str] = []

        title = str(event.get("title", "")).strip()
        if title:
            queries.append(title)

        attendees_raw = str(event.get("attendees", "")).strip()
        if attendees_raw:
            for name in attendees_raw.split(","):
                attendee = name.strip()
                if not attendee or len(attendee) < 3:
                    continue
                queries.append(attendee)
                try:
                    lookup = self._entity_lookup(attendee)
                except Exception:
                    continue
                if isinstance(lookup, str) and lookup.strip():
                    queries.append(lookup.strip())
                elif isinstance(lookup, list):
                    for item in lookup[:2]:
                        if isinstance(item, str) and item.strip():
                            queries.append(item.strip())

        notes = str(event.get("notes", "")).strip()
        if notes:
            queries.append(notes[:200])

        all_results: list[dict[str, Any]] = []
        seen_ids: set[Any] = set()
        recall_limit = max(1, int(limit) // 2)

        for query in queries[:5]:
            try:
                results = self._recall(query, limit=recall_limit, mode="multi")
            except TypeError:
                results = self._recall(query, limit=recall_limit)
            except Exception:
                continue

            for item in results or []:
                memory = getattr(item, "memory", None)
                if memory is None and isinstance(item, dict):
                    memory = item.get("memory")
                if memory is None:
                    continue

                memory_id = getattr(memory, "id", None)
                if memory_id is None and isinstance(memory, dict):
                    memory_id = memory.get("id")
                if memory_id is None:
                    memory_id = id(memory)
                if memory_id in seen_ids:
                    continue
                seen_ids.add(memory_id)

                score = getattr(item, "combined_score", None)
                if score is None:
                    score = getattr(item, "score", None)
                if score is None and isinstance(item, dict):
                    score = item.get("combined_score", item.get("score", 0.0))
                if score is None:
                    score = 0.0

                all_results.append(
                    {
                        "memory": memory,
                        "score": float(score),
                        "query": query,
                        "reason": f"Related to: {query[:50]}",
                    }
                )

        all_results.sort(key=lambda row: -float(row.get("score", 0.0)))
        return all_results[: max(0, int(limit))]

    def check_and_notify(self) -> list[dict[str, Any]]:
        """Check upcoming events and prepare memory bundles."""
        now = time.time()
        if now - self._last_check < 300:
            return []
        self._last_check = now

        events = self.get_upcoming_events(hours_ahead=1)
        notifications: list[dict[str, Any]] = []

        for event in events:
            title = str(event.get("title", ""))
            start = str(event.get("start", ""))
            event_key = f"{title}:{start}"
            if event_key in self._notified_events:
                continue

            surfaced = self.surface_memories_for_event(event)
            if not surfaced:
                continue

            notifications.append(
                {
                    "event": event,
                    "surfaced_memories": surfaced,
                    "count": len(surfaced),
                }
            )
            self._notified_events.add(event_key)

        return notifications
