"""Cron tool for scheduling reminders and tasks."""

from typing import Any

from nanobot.agent.tools.base import Tool
from nanobot.cron.service import CronService
from nanobot.cron.types import CronSchedule


class CronTool(Tool):
    """Tool to schedule reminders and recurring tasks."""
    
    def __init__(self, cron_service: CronService):
        self._cron = cron_service
        self._channel = ""
        self._chat_id = ""
    
    def set_context(self, channel: str, chat_id: str) -> None:
        """Set the current session context for delivery."""
        self._channel = channel
        self._chat_id = chat_id
    
    @property
    def name(self) -> str:
        return "cron"
    
    @property
    def description(self) -> str:
        return """Schedule reminders and recurring tasks. REQUIRED: Always include 'action' parameter ('add', 'list', or 'remove').

For 'add' action:
- MUST include 'message' parameter - extract the reminder/task text from user's request
- Examples: 'remind me to call mama' → message='call mama'

For timing patterns:
- 'remind me in X seconds' → in_seconds=X (DO NOT use 'at')
- 'every X seconds' (forever) → every_seconds=X
- 'every X seconds for Y seconds' → EVERY_SECONDS=X AND IN_SECONDS=Y (creates multiple reminders, DO NOT use 'at')
- 'at specific time' → at='ISO datetime' (only when user specifies exact time)

CRITICAL: For 'every X seconds for Y seconds', you MUST use both every_seconds AND in_seconds together. DO NOT use 'at' parameter for this pattern."""
    
    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "enum": ["add", "list", "remove"],
                    "description": "REQUIRED: Action to perform. Use 'add' to create a reminder, 'list' to see all jobs, or 'remove' to delete a job."
                },
                "message": {
                    "type": "string",
                    "description": "REQUIRED for 'add' action: The reminder message to send. Extract this from the user's request. Examples: 'Remind me to call mama' → message='call mama', 'Remind me every hour to drink water' → message='drink water', 'Schedule a task to check email' → message='check email'. Always extract the actual task/reminder text, not the full user request."
                },
                "every_seconds": {
                    "type": "integer",
                    "description": "Interval in seconds (for recurring tasks). For 'every X seconds for Y seconds', use BOTH every_seconds AND in_seconds together to create multiple reminders."
                },
                "cron_expr": {
                    "type": "string",
                    "description": "Cron expression like '0 9 * * *' (for scheduled tasks)"
                },
                "tz": {
                    "type": "string",
                    "description": "IANA timezone for cron expressions (e.g. 'America/Vancouver')"
                },
                "at": {
                    "type": "string",
                    "description": "ISO datetime string for one-time execution at a SPECIFIC time. Format: YYYY-MM-DDTHH:MM:SS (e.g. '2026-03-03T12:19:30'). ONLY use this when user specifies an exact time like 'at 3pm' or 'at 2026-03-03 14:30'. DO NOT use 'at' for 'every X seconds for Y seconds' - use every_seconds + in_seconds instead."
                },
                "in_seconds": {
                    "type": "integer",
                    "description": "Schedule reminder in N seconds from now, OR duration for recurring reminders. Use this instead of calculating 'at' manually. Examples: 'remind me in 25 seconds' → in_seconds=25. For 'every 10 seconds for the next minute' → every_seconds=10 AND in_seconds=60 (creates 6 reminders)."
                },
                "reminder": {
                    "type": "boolean",
                    "description": "If true, this is a simple reminder (message sent directly to user). If false or omitted, this is a task (agent executes the message). Use reminder=true for 'remind me to X', reminder=false for 'schedule a task to do X'."
                },
                "job_id": {
                    "type": "string",
                    "description": "Job ID (for remove)"
                }
            },
            "required": ["action"]
        }
    
    async def execute(
        self,
        action: str,
        message: str = "",
        every_seconds: int | None = None,
        cron_expr: str | None = None,
        tz: str | None = None,
        at: str | None = None,
        in_seconds: int | None = None,
        reminder: bool = False,
        job_id: str | None = None,
        **kwargs: Any
    ) -> str:
        from loguru import logger
        logger.debug(f"CronTool.execute: action={action}, message={message[:50] if message else None}, every_seconds={every_seconds}, at={at}, in_seconds={in_seconds}, reminder={reminder}, channel={self._channel}, chat_id={self._chat_id}")
        
        if action == "add":
            result = self._add_job(message, every_seconds, cron_expr, tz, at, in_seconds, reminder)
            logger.debug(f"CronTool._add_job result: {result}")
            return result
        elif action == "list":
            return self._list_jobs()
        elif action == "remove":
            return self._remove_job(job_id)
        return f"Unknown action: {action}"
    
    def _add_job(
        self,
        message: str,
        every_seconds: int | None,
        cron_expr: str | None,
        tz: str | None,
        at: str | None,
        in_seconds: int | None = None,
        reminder: bool = False,
    ) -> str:
        if not message:
            return "Error: message is required for 'add' action. You must extract the reminder/task text from the user's request. Example: if user says 'remind me to call mama', use message='call mama'. If user says 'remind me every hour to drink water', use message='drink water'."
        
        # Detect common mistake: using 'at' with 'every_seconds' when 'in_seconds' should be used
        if every_seconds is not None and at is not None and in_seconds is None:
            return f"Error: You used 'at' with 'every_seconds', but for 'every X seconds for Y seconds' pattern, you MUST use 'in_seconds' instead of 'at'. Example: 'every 10 seconds for the next minute' → every_seconds=10 AND in_seconds=60 (NOT 'at'). The 'in_seconds' parameter specifies the duration, and the tool will create multiple reminders automatically."
        
        # Use defaults for CLI mode if context not set
        channel = self._channel or "cli"
        chat_id = self._chat_id or "direct"
        
        # Validate timezone only if used with cron_expr
        if tz and cron_expr:
            from zoneinfo import ZoneInfo
            try:
                ZoneInfo(tz)
            except (KeyError, Exception):
                return f"Error: unknown timezone '{tz}'"
        elif tz and not cron_expr:
            # Ignore tz if not used with cron_expr (common mistake)
            tz = None
        
        # Build schedule - prioritize 'in_seconds' for relative time, then 'at' for absolute time
        delete_after = False
        
        # Special case: recurring job with duration limit (every_seconds + in_seconds)
        if every_seconds is not None and in_seconds is not None:
            # Create multiple one-time jobs for "every X seconds for Y seconds"
            from datetime import datetime, timedelta
            num_jobs = max(1, in_seconds // every_seconds)
            results = []
            for i in range(num_jobs):
                job_time = datetime.now() + timedelta(seconds=i * every_seconds)
                job_at = job_time.isoformat()
                try:
                    dt = datetime.fromisoformat(job_at)
                    at_ms = int(dt.timestamp() * 1000)
                    schedule = CronSchedule(kind="at", at_ms=at_ms)
                    job = self._cron.add_job(
                        name=f"{message[:25]} ({i+1}/{num_jobs})" if num_jobs > 1 else message[:30],
                        schedule=schedule,
                        message=message,
                        deliver=True,
                        channel=channel,
                        to=chat_id,
                        delete_after_run=True,
                        reminder=reminder,
                    )
                    results.append(f"Created job '{job.name}' (id: {job.id})")
                except Exception as e:
                    results.append(f"Error creating job {i+1}: {str(e)}")
            return f"Created {len([r for r in results if 'Created' in r])} reminder(s):\n" + "\n".join(results)
        
        # Handle relative time (in_seconds) - compute datetime automatically
        if in_seconds is not None:
            from datetime import datetime, timedelta
            from time import time as _time
            future_time = datetime.now() + timedelta(seconds=in_seconds)
            at = future_time.isoformat()
            # Fall through to 'at' handling below
        
        if at:
            # One-time reminder at specific time
            from datetime import datetime
            try:
                # Check if agent passed description text, Python code, or other invalid values
                if "iso datetime" in at.lower() or "e.g." in at.lower() or "example" in at.lower() or at.startswith("("):
                    return f"Error: You passed description text '{at}' instead of an actual datetime string. You must: 1) Read current time from system prompt (e.g. '2026-03-03 12:19:04'), 2) Add requested seconds/minutes to it, 3) Format as ISO string like '2026-03-03T12:19:29'. Do NOT use description text or examples."
                
                if "datetime.now()" in at or "timedelta" in at:
                    return f"Error: You passed Python code '{at}' instead of an actual datetime string. You must compute the datetime value first, then pass the ISO format string (e.g. '2026-03-03T12:19:29')."
                
                dt = datetime.fromisoformat(at)
                # If datetime is naive (no timezone), assume local timezone
                if dt.tzinfo is None:
                    import time
                    # Get local timezone offset
                    local_offset = time.timezone if (time.daylight == 0) else time.altzone
                    # Convert naive datetime to UTC-aware for consistent timestamp calculation
                    dt = dt.replace(tzinfo=None)
                    # Calculate timestamp assuming local time
                    at_ms = int(dt.timestamp() * 1000)
                else:
                    at_ms = int(dt.timestamp() * 1000)
                
                # Validate that the time is in the future (allow 5 second buffer for processing)
                from time import time as _time
                from datetime import datetime as _dt
                now_ms = int(_time() * 1000)
                buffer_ms = 5000  # 5 second buffer for processing time
                if at_ms <= (now_ms + buffer_ms):
                    now_str = _dt.now().strftime("%Y-%m-%d %H:%M:%S")
                    scheduled_str = _dt.fromtimestamp(at_ms / 1000).strftime("%Y-%m-%d %H:%M:%S")
                    diff_sec = (now_ms - at_ms) / 1000
                    if diff_sec > 0:
                        return f"Error: scheduled time ({scheduled_str}) is in the past by {diff_sec:.0f} seconds. Current time is {now_str}. You must ADD the requested seconds to the current time. Example: if current time is 12:21:46 and user wants reminder in 25 seconds, calculate 12:21:46 + 25 seconds = 12:22:11, then pass '2026-03-03T12:22:11'."
                    else:
                        return f"Error: scheduled time ({scheduled_str}) is too close to current time ({now_str}). You must ADD the requested seconds to the current time. Example: if current time is 12:21:46 and user wants reminder in 25 seconds, calculate 12:21:46 + 25 seconds = 12:22:11, then pass '2026-03-03T12:22:11'."
                
                schedule = CronSchedule(kind="at", at_ms=at_ms)
                delete_after = True
            except (ValueError, Exception) as e:
                return f"Error: invalid datetime format for 'at': {str(e)}. Expected ISO format like '2026-03-03T12:05:30', not Python code."
        elif every_seconds:
            # Recurring reminder
            schedule = CronSchedule(kind="every", every_ms=every_seconds * 1000)
        elif cron_expr:
            # Cron expression
            schedule = CronSchedule(kind="cron", expr=cron_expr, tz=tz)
        else:
            return "Error: either every_seconds, cron_expr, or at is required"
        
        try:
            job = self._cron.add_job(
                name=message[:30],
                schedule=schedule,
                message=message,
                deliver=True,
                channel=channel,
                to=chat_id,
                delete_after_run=delete_after,
                reminder=reminder,
            )
            return f"Created job '{job.name}' (id: {job.id})"
        except Exception as e:
            return f"Error creating cron job: {str(e)}"
    
    def _list_jobs(self) -> str:
        jobs = self._cron.list_jobs()
        if not jobs:
            return "No scheduled jobs."
        lines = [f"- {j.name} (id: {j.id}, {j.schedule.kind})" for j in jobs]
        return "Scheduled jobs:\n" + "\n".join(lines)
    
    def _remove_job(self, job_id: str | None) -> str:
        if not job_id:
            return "Error: job_id is required for remove"
        if self._cron.remove_job(job_id):
            return f"Removed job {job_id}"
        return f"Job {job_id} not found"
