"""Built-in tool: schedule a delayed recall for the current session."""

from __future__ import annotations

from datetime import datetime, timezone

from ._internal.base import Tool, ToolResult, current_session_id, current_user_role
from ._internal.time_parser import parse_when


class ScheduleRecallTool(Tool):
    name = "schedule_recall"
    description = (
        "Schedule a headless callback for the current session. "
        "At the chosen time Navi wakes up, reads your self-instruction, and continues working using other tools. "
        "Only one pending recall per session is allowed — cancel the old one first if you need a new timer.\n\n"
        "CORE PRINCIPLE: this is a TOOL for CONTINUING WORK, not a chat reminder.\n"
        "  BAD message: 'Tell the user that 2 hours have passed.'\n"
        "  GOOD message: 'Read /tmp/build.log with filesystem. If errors, read last 50 lines and report. Otherwise confirm success.'\n\n"
        "Call types:\n"
        "  once      — single delayed action (check logs in 30m, continue after reboot).\n"
        "  recurring — periodic action with interval_seconds (poll API every 5 min, check inbox every 15 min).\n"
        "  immediate — fire ASAP; use to offload heavy multi-tool work without blocking the chat.\n\n"
        "Popular scenarios:\n"
        "  1. Hit iteration limit? Schedule immediate with context 'Continue Nginx config from step 3...'\n"
        "  2. Waiting for a build/export? Schedule once for estimated finish time and tell yourself which files to check.\n"
        "  3. Periodic monitoring? Schedule recurring 900s (15 min) with context 'Read /var/log/app/errors.log...'\n"
        "  4. Heavy task with 20+ tool calls? Use immediate so user can keep chatting while you work headlessly.\n\n"
        "Rules:\n"
        "  • additional_context_message must read like a todo item for yourself.\n"
        "  • Mention specific tools, files, or URLs — future-you has the same tools but not your short-term memory.\n"
        "  • Multi-phase tasks: chain recalls — phase 1 runs, then schedules recall for phase 2.\n"
        "  • Only one pending recall per session. Use manage_recall cancel before scheduling a new one."
    )
    parameters = {
        "type": "object",
        "properties": {
            "call_type": {
                "type": "string",
                "enum": ["once", "recurring", "immediate"],
                "description": "Type of recall: once (single), recurring (repeats), immediate (ASAP).",
            },
            "when": {
                "type": "string",
                "description": (
                    "When to trigger. Absolute ISO datetime or relative phrase "
                    "like '30m', '2h 15m', '1d 6h', 'in 3 hours', 'tomorrow at 09:00'. "
                    "Ignored for immediate calls."
                ),
            },
            "interval_seconds": {
                "type": "integer",
                "description": "Repeat interval in seconds. Required only for recurring.",
            },
            "internal_comment": {
                "type": "string",
                "description": "Optional human-readable note why this recall was set.",
            },
            "additional_context_message": {
                "type": "string",
                "description": (
                    "Self-instruction describing exactly what to do when the recall fires. "
                    "Write it as a todo for yourself: mention specific tools, files, or steps. "
                    "This is injected as a system message and Navi acts on it with other tools."
                ),
            },
        },
        "required": ["call_type", "additional_context_message"],
    }

    def __init__(self, scheduler: "RecallScheduler" | None = None) -> None:
        self._scheduler = scheduler

    async def execute(self, params: dict) -> ToolResult:
        from navi.core.scheduler import RecallExistsError, RecallScheduler

        scheduler = self._scheduler
        if scheduler is None:
            return ToolResult(
                success=False,
                output="Scheduler not available.",
                error="no scheduler",
            )

        session_id = current_session_id.get(None)
        if not session_id:
            return ToolResult(
                success=False,
                output="No current session.",
                error="missing session_id",
            )

        call_type = (params.get("call_type") or "").strip().lower()
        when = (params.get("when") or "").strip()
        interval_seconds: int | None = params.get("interval_seconds")
        internal_comment: str | None = params.get("internal_comment")
        additional_context_message = (params.get("additional_context_message") or "").strip()

        if call_type not in ("once", "recurring", "immediate"):
            return ToolResult(
                success=False,
                output=f"Invalid call_type: {call_type}. Use once, recurring, or immediate.",
                error="bad call_type",
            )

        if call_type == "recurring":
            if not interval_seconds or interval_seconds <= 0:
                return ToolResult(
                    success=False,
                    output="interval_seconds is required and must be > 0 for recurring recalls.",
                    error="missing interval",
                )
        else:
            interval_seconds = None

        # Resolve trigger time
        if call_type == "immediate":
            trigger_at = datetime.now(timezone.utc)
        elif when:
            trigger_at, error = parse_when(when)
            if error:
                return ToolResult(success=False, output=error, error="parse_failed")
        else:
            return ToolResult(
                success=False,
                output="'when' is required for once/recurring recalls.",
                error="missing when",
            )

        if not additional_context_message:
            return ToolResult(
                success=False,
                output="additional_context_message is required.",
                error="missing context",
            )

        try:
            recall = await scheduler.schedule_recall(
                session_id=session_id,
                call_type=call_type,
                trigger_at=trigger_at,
                interval_seconds=interval_seconds,
                internal_comment=internal_comment,
                additional_context_message=additional_context_message,
            )
        except RecallExistsError as exc:
            return ToolResult(
                success=False,
                output=f"This session already has a pending recall. Cancel it first: {exc}",
                error="recall_exists",
            )
        except Exception as exc:
            return ToolResult(
                success=False,
                output=f"Failed to schedule recall: {exc}",
                error="schedule_failed",
            )

        return ToolResult(
            success=True,
            output=(
                f"Recall scheduled.\n"
                f"  ID: {recall.id}\n"
                f"  Type: {recall.call_type}\n"
                f"  Trigger: {recall.trigger_at.isoformat()}\n"
                + (
                    f"  Interval: {recall.interval_seconds}s\n"
                    if recall.interval_seconds
                    else ""
                )
                + (f"  Comment: {recall.internal_comment}\n" if recall.internal_comment else "")
            ),
        )
