# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# Licensed under the MIT License

"""
Inline Confirmation System  (v1.1.33)

Replaces the old confirm=true parameter pattern. Skills return a
ConfirmationRequired object when user approval is needed. The agent
pipeline intercepts it, stores the deferred call, and returns a sentinel
string. Channels render the appropriate UI and re-execute on approval.

Usage in a skill handler:
    from familiar.core.confirmations import needs_confirmation, email_preview

    def send_email(data):
        to      = data.get("to", "")
        subject = data.get("subject", "")
        body    = data.get("body", "")

        if not data.get("_confirmed"):
            return needs_confirmation(
                tool_name="send_email",
                tool_input=data,
                preview=email_preview(to, subject, body),
                risk="high",
            )
        # ... actually send
"""

from __future__ import annotations

import dataclasses

# Channel-routable sentinel — written into the agent response string so
# channels can detect it without a shared import.
SENTINEL_PREFIX = "__PENDING__:"
EMAIL_MENU_PREFIX = "__EMAIL_MENU__:"
CALENDAR_MENU_PREFIX = "__CALENDAR_MENU__:"


@dataclasses.dataclass
class ConfirmationRequired:
    """
    Returned by a skill handler to pause execution and request confirmation.

    tool_name:  Skill tool name (e.g. 'send_email')
    tool_input: Full original input dict (minus _confirmed / _context keys)
    preview:    Plain-language summary shown in the confirmation UI
    risk:       'high' | 'medium' | 'low'  — colours the channel UI
    """

    tool_name: str
    tool_input: dict
    preview: str
    risk: str = "medium"

    def is_confirmation_required(self) -> bool:
        """Duck-type check used by agent.py to avoid a hard import."""
        return True


def needs_confirmation(
    tool_name: str,
    tool_input: dict,
    preview: str,
    risk: str = "medium",
) -> ConfirmationRequired:
    """
    Call this from skill handlers to request confirmation.
    Strips _confirmed and _context from stored input so re-execution is clean.
    """
    clean = {k: v for k, v in tool_input.items() if k not in ("_confirmed", "_context")}
    return ConfirmationRequired(tool_name=tool_name, tool_input=clean, preview=preview, risk=risk)


# ── Preview builders ───────────────────────────────────────────────────────────


def email_preview(to: str, subject: str, body: str) -> str:
    subj = subject or "(no subject)"
    words = len(body.split())
    lines = body.strip().splitlines()
    first = (lines[0][:80] + "…" if len(lines[0]) > 80 else lines[0]) if lines else ""
    return f"Send email to {to}\n  Subject: {subj}\n  {words} word{'s' if words != 1 else ''}" + (
        f": {first}" if first else ""
    )


def calendar_create_preview(title: str, start: str, duration: int, attendees: list) -> str:
    att = ""
    if attendees:
        names = [str(a) for a in attendees[:3]]
        extra = len(attendees) - 3
        att = "\n  With: " + ", ".join(names) + (f" +{extra} more" if extra > 0 else "")
    return f"Create event: {title}\n  {start}  ·  {duration} min" + att


def calendar_delete_preview(title: str, start: str) -> str:
    return f"Delete event: {title}\n  {start}\n  This cannot be undone."


def drive_create_preview(title: str, word_count: int, folder: str) -> str:
    folder_str = f"\n  In: {folder}" if folder else ""
    return (
        f"Create Google Doc: {title}\n"
        f"  {word_count} word{'s' if word_count != 1 else ''}" + folder_str
    )


def drive_update_preview(file_name: str, word_count: int) -> str:
    return (
        f"Replace content of: {file_name}\n"
        f"  {word_count} word{'s' if word_count != 1 else ''}  ·  Existing content erased."
    )


def drive_append_preview(file_name: str, word_count: int) -> str:
    return f"Append to: {file_name}\n  {word_count} word{'s' if word_count != 1 else ''}"


def caldav_create_preview(title: str, start: str, duration: int, server: str) -> str:
    return f"Create CalDAV event: {title}\n  {start}  ·  {duration} min\n  Server: {server}"


def caldav_delete_preview(title: str, start: str) -> str:
    return f"Delete CalDAV event: {title}\n  {start}\n  This cannot be undone."


def nextcloud_upload_preview(filename: str, dest: str) -> str:
    return f"Upload to Nextcloud: {filename}\n  Destination: {dest}"


def nextcloud_delete_preview(path: str) -> str:
    return f"Delete from Nextcloud: {path}\n  This cannot be undone."


def nextcloud_sync_preview(direction: str, count: int) -> str:
    return f"Sync {count} contacts {direction}\n  This will merge with existing records."


def meeting_preview(title: str, start: str, attendees: list, send_invites: bool) -> str:
    names = [a.get("name", str(a)) if isinstance(a, dict) else str(a) for a in attendees[:4]]
    extra = len(attendees) - 4
    att = ", ".join(names) + (f" +{extra} more" if extra > 0 else "")
    inv = "\n  Invites will be sent by email." if send_invites and attendees else ""
    return f"Schedule: {title}\n  {start}" + (f"\n  With: {att}" if att else "") + inv
