"""PocketPaw Scheduler - Proactive reminders and scheduled tasks.

Simple reminder system with natural language time parsing.
"""

import json
import logging
import re
import uuid
from datetime import datetime, timedelta
from pathlib import Path
from typing import Callable, Optional

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.date import DateTrigger
from dateutil import parser as date_parser

logger = logging.getLogger(__name__)


def get_reminders_path() -> Path:
    """Get the reminders file path."""
    config_dir = Path.home() / ".pocketclaw"
    config_dir.mkdir(exist_ok=True)
    return config_dir / "reminders.json"


def load_reminders() -> list[dict]:
    """Load reminders from file."""
    path = get_reminders_path()
    if path.exists():
        try:
            data = json.loads(path.read_text())
            return data.get("reminders", [])
        except (json.JSONDecodeError, Exception):
            pass
    return []


def save_reminders(reminders: list[dict]) -> None:
    """Save reminders to file."""
    path = get_reminders_path()
    data = {
        "reminders": reminders,
        "updated_at": datetime.now().isoformat()
    }
    path.write_text(json.dumps(data, indent=2))


def parse_natural_time(text: str) -> Optional[datetime]:
    """Parse natural language time expressions.

    Supports:
    - "in X minutes/hours/days"
    - "at HH:MM" or "at H:MM AM/PM"
    - "tomorrow at HH:MM"
    - Absolute dates/times
    """
    text = text.lower().strip()
    now = datetime.now()

    # Pattern: "in X minutes/hours/days"
    relative_match = re.search(
        r'in\s+(\d+)\s*(minute|min|hour|hr|day|second|sec)s?',
        text
    )
    if relative_match:
        amount = int(relative_match.group(1))
        unit = relative_match.group(2)

        if unit in ('minute', 'min'):
            return now + timedelta(minutes=amount)
        elif unit in ('hour', 'hr'):
            return now + timedelta(hours=amount)
        elif unit == 'day':
            return now + timedelta(days=amount)
        elif unit in ('second', 'sec'):
            return now + timedelta(seconds=amount)

    # Pattern: "at HH:MM" or "at H:MM AM/PM"
    at_match = re.search(r'at\s+(\d{1,2}):?(\d{2})?\s*(am|pm)?', text)
    if at_match:
        hour = int(at_match.group(1))
        minute = int(at_match.group(2) or 0)
        period = at_match.group(3)

        if period == 'pm' and hour < 12:
            hour += 12
        elif period == 'am' and hour == 12:
            hour = 0

        target = now.replace(hour=hour, minute=minute, second=0, microsecond=0)

        # Check if "tomorrow" is mentioned
        if 'tomorrow' in text:
            target += timedelta(days=1)
        # If time is in the past today, schedule for tomorrow
        elif target <= now:
            target += timedelta(days=1)

        return target

    # Pattern: "tomorrow" (defaults to 9am)
    if 'tomorrow' in text and not at_match:
        return (now + timedelta(days=1)).replace(hour=9, minute=0, second=0, microsecond=0)

    # Try dateutil parser for other formats
    try:
        parsed = date_parser.parse(text, fuzzy=True)
        if parsed > now:
            return parsed
    except (ValueError, TypeError):
        pass

    return None


def extract_reminder_text(message: str) -> str:
    """Extract the reminder text from a message.

    E.g., "remind me in 5 minutes to call mom" -> "call mom"
    """
    # Remove common patterns
    patterns = [
        r'^remind\s+me\s+',
        r'in\s+\d+\s*(minute|min|hour|hr|day|second|sec)s?\s*',
        r'at\s+\d{1,2}:?\d{0,2}\s*(am|pm)?\s*',
        r'tomorrow\s*',
        r'^to\s+',
    ]

    text = message.lower()
    for pattern in patterns:
        text = re.sub(pattern, '', text, flags=re.IGNORECASE)

    # Clean up
    text = text.strip()

    # Capitalize first letter
    if text:
        text = text[0].upper() + text[1:]

    return text or message


class ReminderScheduler:
    """Manages scheduled reminders with APScheduler."""

    def __init__(self):
        self.scheduler = AsyncIOScheduler()
        self.reminders: list[dict] = []
        self.callback: Optional[Callable] = None
        self._started = False

    def start(self, callback: Optional[Callable] = None):
        """Start the scheduler and load saved reminders."""
        if self._started:
            return

        self.callback = callback
        self.reminders = load_reminders()

        # Reschedule active reminders
        now = datetime.now()
        active_reminders = []

        for reminder in self.reminders:
            trigger_time = datetime.fromisoformat(reminder["trigger_at"])
            if trigger_time > now:
                self._add_job(reminder)
                active_reminders.append(reminder)
            else:
                logger.info(f"Skipping past reminder: {reminder['id']}")

        self.reminders = active_reminders
        save_reminders(self.reminders)

        self.scheduler.start()
        self._started = True
        logger.info(f"Scheduler started with {len(self.reminders)} reminders")

    def stop(self):
        """Stop the scheduler."""
        if self._started:
            self.scheduler.shutdown(wait=False)
            self._started = False

    async def _trigger_reminder(self, reminder_id: str):
        """Called when a reminder is due."""
        reminder = next((r for r in self.reminders if r["id"] == reminder_id), None)
        if not reminder:
            return

        logger.info(f"Reminder triggered: {reminder['text']}")

        # Call callback if set
        if self.callback:
            await self.callback(reminder)

        # Remove from list
        self.reminders = [r for r in self.reminders if r["id"] != reminder_id]
        save_reminders(self.reminders)

    def _add_job(self, reminder: dict):
        """Add a scheduler job for a reminder."""
        trigger_time = datetime.fromisoformat(reminder["trigger_at"])
        self.scheduler.add_job(
            self._trigger_reminder,
            trigger=DateTrigger(run_date=trigger_time),
            args=[reminder["id"]],
            id=reminder["id"],
            replace_existing=True
        )

    def add_reminder(self, message: str) -> Optional[dict]:
        """Add a reminder from a natural language message.

        Args:
            message: Natural language like "remind me in 5 minutes to call mom"

        Returns:
            Reminder dict if successful, None if time couldn't be parsed
        """
        trigger_time = parse_natural_time(message)
        if not trigger_time:
            return None

        reminder_text = extract_reminder_text(message)

        reminder = {
            "id": str(uuid.uuid4()),
            "text": reminder_text,
            "original": message,
            "trigger_at": trigger_time.isoformat(),
            "created_at": datetime.now().isoformat()
        }

        self.reminders.append(reminder)
        save_reminders(self.reminders)

        if self._started:
            self._add_job(reminder)

        logger.info(f"Added reminder: {reminder_text} at {trigger_time}")
        return reminder

    def delete_reminder(self, reminder_id: str) -> bool:
        """Delete a reminder by ID."""
        reminder = next((r for r in self.reminders if r["id"] == reminder_id), None)
        if not reminder:
            return False

        # Remove from scheduler
        try:
            self.scheduler.remove_job(reminder_id)
        except Exception:
            pass

        # Remove from list
        self.reminders = [r for r in self.reminders if r["id"] != reminder_id]
        save_reminders(self.reminders)

        logger.info(f"Deleted reminder: {reminder_id}")
        return True

    def get_reminders(self) -> list[dict]:
        """Get all active reminders."""
        return self.reminders

    def format_time_remaining(self, reminder: dict) -> str:
        """Format the time remaining for a reminder."""
        trigger_time = datetime.fromisoformat(reminder["trigger_at"])
        delta = trigger_time - datetime.now()

        if delta.total_seconds() < 0:
            return "past"

        total_seconds = int(delta.total_seconds())

        if total_seconds < 60:
            return f"in {total_seconds}s"
        elif total_seconds < 3600:
            minutes = total_seconds // 60
            return f"in {minutes}m"
        elif total_seconds < 86400:
            hours = total_seconds // 3600
            minutes = (total_seconds % 3600) // 60
            if minutes:
                return f"in {hours}h {minutes}m"
            return f"in {hours}h"
        else:
            days = total_seconds // 86400
            return f"in {days}d"


# Singleton instance
_scheduler: Optional[ReminderScheduler] = None


def get_scheduler() -> ReminderScheduler:
    """Get the singleton scheduler instance."""
    global _scheduler
    if _scheduler is None:
        _scheduler = ReminderScheduler()
    return _scheduler
