"""PIM tool factory — closures over UserContext directories.

``create_pim_tools(ctx)`` returns a list of plain functions that the SDK
wraps as ``StructuredTool``. Each function captures the relevant PIM
directory from UserContext and returns formatted strings for the LLM.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections.abc import Callable

    from icarus.runtime.context import UserContext


def create_pim_tools(ctx: UserContext) -> list[Callable]:
    """Create PIM tool closures scoped to a UserContext.

    Returns:
        List of tool functions ready to be wrapped by the SDK.
    """
    contacts_dir = ctx.contacts_dir
    calendar_dir = ctx.calendar_dir
    todos_dir = ctx.todos_dir

    # ── Contacts ─────────────────────────────────────────────

    def search_contacts(query: str = "") -> str:
        """Search your contacts by name, email, or organization.

        Args:
            query: Search term to match against contact names, emails,
                   and organizations. Leave empty to list all contacts.

        Returns:
            Formatted list of matching contacts.
        """
        from icarus.pim.contacts import search_contacts as _search

        try:
            results = _search(contacts_dir, query)
        except Exception as e:  # noqa: BLE001
            return f"Error searching contacts: {e}"

        if not results:
            if query:
                return f"No contacts found matching '{query}'."
            return "No contacts found."

        lines = []
        for c in results:
            fn = c.get("fn", "Unknown")
            emails = ", ".join(c.get("email", []))
            org = c.get("org", "")

            parts = [fn]
            if emails:
                parts.append(f"<{emails}>")
            if org:
                parts.append(f"({org})")
            lines.append("  ".join(parts))

        header = f"Found {len(results)} contact(s)"
        if query:
            header += f" matching '{query}'"
        return header + ":\n" + "\n".join(f"- {line}" for line in lines)

    def get_contact_details(name: str) -> str:
        """Get full details for a specific contact.

        Args:
            name: The contact's name (or partial name) to look up.

        Returns:
            Detailed contact information.
        """
        from icarus.pim.contacts import search_contacts as _search

        try:
            results = _search(contacts_dir, name)
        except Exception as e:  # noqa: BLE001
            return f"Error looking up contact: {e}"

        if not results:
            return f"No contact found matching '{name}'."

        # Return details of the best match
        c = results[0]
        lines = [f"Name: {c.get('fn', 'Unknown')}"]
        for email in c.get("email", []):
            lines.append(f"Email: {email}")
        for tel in c.get("tel", []):
            lines.append(f"Phone: {tel}")
        if c.get("org"):
            lines.append(f"Organization: {c['org']}")
        if c.get("title"):
            lines.append(f"Title: {c['title']}")
        if c.get("note"):
            lines.append(f"Note: {c['note']}")
        if c.get("tags"):
            lines.append(f"Tags: {', '.join(c['tags'])}")
        for key, val in c.get("x_fields", {}).items():
            # Skip X-ICARUS-TAGS since it's already shown as first-class tags
            if key.upper() == "X-ICARUS-TAGS":
                continue
            label = key.replace("X-ICARUS-", "").replace("-", " ").title()
            lines.append(f"{label}: {val}")
        return "\n".join(lines)

    def save_contact(
        full_name: str,
        email: str = "",
        phone: str = "",
        organization: str = "",
        title: str = "",
        note: str = "",
        tags: str = "",
    ) -> str:
        """Save a new contact or update an existing one.

        Args:
            full_name: The contact's full name (required).
            email: Email address.
            phone: Phone number.
            organization: Company or organization name.
            title: Job title.
            note: Additional notes about this person.
            tags: Comma-separated tags.

        Returns:
            Confirmation message.
        """
        from icarus.pim.contacts import create_contact as _create
        from icarus.pim.contacts import search_contacts as _search_contacts
        from icarus.pim.contacts import update_contact as _update

        data = {"fn": full_name}
        if email:
            data["email"] = [email]
        if phone:
            data["tel"] = [phone]
        if organization:
            data["org"] = organization
        if title:
            data["title"] = title
        if note:
            data["note"] = note
        if tags:
            data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]

        try:
            results = _search_contacts(contacts_dir, full_name)
            exact = [c for c in results if c.get("fn", "").lower() == full_name.lower()]
            if exact:
                slug = exact[0]["_path"].stem
                path = _update(contacts_dir, slug, data)
                return f"Updated contact: {full_name} ({path.name})"
            path = _create(contacts_dir, data)
            return f"Created contact: {full_name} ({path.name})"
        except Exception as e:  # noqa: BLE001
            return f"Error saving contact: {e}"

    # ── Calendar ─────────────────────────────────────────────

    def list_upcoming_events(days: int = 7) -> str:
        """List upcoming calendar events.

        Args:
            days: Number of days to look ahead (default: 7).

        Returns:
            Formatted list of upcoming events.
        """
        from datetime import date, timedelta

        from icarus.pim.calendar import list_events as _list

        try:
            start = date.today()
            end = start + timedelta(days=days)
            events = _list(calendar_dir, start, end)
        except Exception as e:  # noqa: BLE001
            return f"Error listing events: {e}"

        if not events:
            return f"No events in the next {days} day(s)."

        lines = []
        for ev in events:
            dtstart = ev.get("dtstart")
            dtend = ev.get("dtend")
            summary = ev.get("summary", "Untitled")

            # Format time range
            if dtstart:
                time_str = dtstart.strftime("%A %b %d, %H:%M")
                if dtend:
                    time_str += f"-{dtend.strftime('%H:%M')}"
            else:
                time_str = "No date"

            line = f"{time_str}: {summary}"
            if ev.get("location"):
                line += f" @ {ev['location']}"
            if ev.get("attendees"):
                names = [a["name"] or a["email"] for a in ev["attendees"]]
                line += f"\n  with: {', '.join(names)}"
            if ev.get("recurrence"):
                line += f"\n  [recurring: {ev['recurrence']}]"
            lines.append(line)

        header = f"{len(events)} event(s) in the next {days} day(s):"
        return header + "\n" + "\n".join(f"- {line}" for line in lines)

    def create_event(
        summary: str,
        start: str,
        end: str = "",
        location: str = "",
        description: str = "",
        attendees: str = "",
    ) -> str:
        """Create a new calendar event.

        Args:
            summary: Event title/summary (required).
            start: Start date/time in ISO format (e.g. "2026-03-15T10:00").
            end: End date/time in ISO format. Defaults to start + 1 hour.
            location: Event location.
            description: Event description.
            attendees: Comma-separated attendees in "Name <email>" format.
                Search contacts first to resolve names to emails.

        Returns:
            Confirmation message.
        """
        from icarus.pim.calendar import create_event as _create
        from icarus.pim.common import validate_date_not_past

        err = validate_date_not_past(start, "start")
        if err:
            return err
        if end:
            try:
                from datetime import datetime
                dt_s = datetime.fromisoformat(start)
                dt_e = datetime.fromisoformat(end)
                if dt_e <= dt_s:
                    return f"End time ({end}) must be after start time ({start})."
            except (ValueError, TypeError):
                return (
                    f"Invalid date format for end: '{end}'. "
                    f"Use ISO format like '2026-03-15T11:00'."
                )

        data: dict = {"summary": summary, "dtstart": start}
        if end:
            data["dtend"] = end
        if location:
            data["location"] = location
        if description:
            data["description"] = description
        if attendees:
            data["attendees"] = [a.strip() for a in attendees.split(",") if a.strip()]

        try:
            path = _create(calendar_dir, data)
            return f"Created event: {summary} ({path.name})"
        except ValueError as e:
            return f"Invalid date format: {e}. Use ISO format like '2026-03-15T10:00'."
        except Exception as e:  # noqa: BLE001
            return f"Error creating event: {e}"

    # ── Todos ────────────────────────────────────────────────

    def list_todos(status: str = "open", sort_by: str = "urgency") -> str:
        """List your tasks/todos.

        Args:
            status: Filter by status — "open", "completed", or "all".
            sort_by: Sort order — "urgency" (default) or "due".

        Returns:
            Formatted list of tasks with urgency scores.
        """
        from icarus.pim.todos import compute_urgency as _urgency
        from icarus.pim.todos import list_todos as _list

        try:
            todos = _list(todos_dir, status=status)
        except Exception as e:  # noqa: BLE001
            return f"Error listing todos: {e}"

        if not todos:
            return f"No {status} tasks."

        # Compute urgency and sort
        for t in todos:
            t["_urgency"] = _urgency(t)

        if sort_by == "due":
            todos.sort(
                key=lambda t: (0, t["due"]) if t.get("due") else (1,),
            )
        else:
            todos.sort(key=lambda t: t["_urgency"], reverse=True)

        lines = []
        for t in todos:
            summary = t.get("summary", "Untitled")
            urgency = t["_urgency"]
            priority = t.get("priority", "none")

            parts = [f"[{urgency:.1f}]", summary]
            if priority != "none":
                parts.append(f"(priority: {priority})")
            if t.get("due"):
                parts.append(f"due: {t['due'].strftime('%b %d')}")
            if t.get("assigned_to"):
                parts.append(f"→ {t['assigned_to']}")
            if t.get("attendees"):
                names = [a["name"] or a["email"] for a in t["attendees"]]
                parts.append(f"with: {', '.join(names)}")
            if t.get("related_to"):
                parts.append(f"[linked: {len(t['related_to'])}]")
            if t.get("project"):
                parts.append(f"#{t['project']}")
            if t.get("blocked"):
                parts.append("[BLOCKED]")
            lines.append(" ".join(parts))

        header = f"{len(todos)} {status} task(s):"
        return header + "\n" + "\n".join(f"- {line}" for line in lines)

    def add_todo(
        summary: str,
        due: str = "",
        priority: str = "medium",
        project: str = "",
        tags: str = "",
        description: str = "",
        attendees: str = "",
        assigned_to: str = "",
        related_event: str = "",
    ) -> str:
        """Add a new task/todo.

        Args:
            summary: Task description (required).
            due: Due date in ISO format (e.g. "2026-03-15").
            priority: Priority level — "high", "medium", "low", or "none".
            project: Project name for grouping.
            tags: Comma-separated tags.
            description: Detailed task description.
            attendees: Comma-separated related people in "Name <email>" format.
                Search contacts first to resolve names to emails.
            assigned_to: Person responsible in "Name <email>" format.
                For multi-user: can be a Linux user on the system.
            related_event: UID of a linked calendar event.

        Returns:
            Confirmation message.
        """
        from icarus.pim.common import validate_date_not_past
        from icarus.pim.todos import create_todo as _create

        data: dict = {"summary": summary, "priority": priority}
        if due:
            err = validate_date_not_past(due, "due date")
            if err:
                return err
            data["due"] = due
        if project:
            data["project"] = project
        if tags:
            data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
        if description:
            data["description"] = description
        if attendees:
            data["attendees"] = [a.strip() for a in attendees.split(",") if a.strip()]
        if assigned_to:
            data["assigned_to"] = assigned_to
        if related_event:
            data["related_to"] = [related_event]

        try:
            path = _create(todos_dir, data)
            return f"Created task: {summary} ({path.name})"
        except ValueError as e:
            return f"Invalid date format: {e}. Use ISO format like '2026-03-15'."
        except Exception as e:  # noqa: BLE001
            return f"Error creating task: {e}"

    def complete_todo(summary_or_id: str) -> str:
        """Mark a task as completed.

        Args:
            summary_or_id: The task summary (searches for best match)
                or exact UID to complete.

        Returns:
            Confirmation message.
        """
        from icarus.pim.todos import complete_todo as _complete
        from icarus.pim.todos import list_todos as _list

        try:
            # First try as UID (direct file lookup)
            uid_path = todos_dir / f"{summary_or_id}.ics"
            if uid_path.resolve().is_relative_to(todos_dir.resolve()) and uid_path.exists():
                _complete(todos_dir, summary_or_id)
                return f"Completed task: {summary_or_id}"

            # Search by summary
            todos = _list(todos_dir, status="open")
            query_lower = summary_or_id.lower()
            matches = [
                t for t in todos
                if query_lower in t.get("summary", "").lower()
            ]

            if not matches:
                return f"No open task found matching '{summary_or_id}'."

            if len(matches) > 1:
                names = "\n".join(f"- {t['summary']}" for t in matches[:5])
                return f"Multiple tasks match '{summary_or_id}':\n{names}\nPlease be more specific."

            uid = matches[0]["uid"]
            _complete(todos_dir, uid)
            return f"Completed task: {matches[0]['summary']}"
        except Exception as e:  # noqa: BLE001
            return f"Error completing task: {e}"

    return [
        search_contacts,
        get_contact_details,
        save_contact,
        list_upcoming_events,
        create_event,
        list_todos,
        add_todo,
        complete_todo,
    ]


