"""
Proactive Scheduler for Brynq v2.

Allows agents to register recurring cron-like tasks that the broker
automatically executes (by posting a task to a channel) at specified intervals.
"""

import json
import os
import time
import uuid
import threading
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from brynq.server import BROKER_DIR, CHANNELS_DIR, _append_jsonl_sync

# ── Paths ──────────────────────────────────────────────────────────────────

SCHEDULER_DIR = BROKER_DIR / "scheduler"
SCHEDULER_DIR.mkdir(parents=True, exist_ok=True)

# ── Core Scheduler Logic ───────────────────────────────────────────────────

def register_cron_task(
    name: str,
    channel: str,
    cron_expr: str,
    message: str,
    msg_type: str = "request",
    session_id: str = "system",
    session_name: str = "scheduler",
    platform: str = "system"
) -> str:
    """Register a new recurring task.
    
    For MVP, `cron_expr` is parsed as a simple interval in seconds (e.g., "300" for 5 mins).
    """
    job_id = uuid.uuid4().hex[:12]
    
    try:
        interval_secs = int(cron_expr)
    except ValueError:
        raise ValueError("Invalid cron_expr: currently only seconds as integer are supported.")
        
    job = {
        "job_id": job_id,
        "name": name,
        "channel": channel,
        "interval_secs": interval_secs,
        "message": message,
        "msg_type": msg_type,
        "session_id": session_id,
        "session_name": session_name,
        "platform": platform,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "last_run": None,
        "active": True
    }
    
    path = SCHEDULER_DIR / f"{job_id}.json"
    path.write_text(json.dumps(job, indent=2))
    
    return job_id

def list_cron_tasks() -> list[dict]:
    """List all registered scheduled tasks."""
    tasks = []
    if not SCHEDULER_DIR.exists():
        return tasks
        
    for f in SCHEDULER_DIR.glob("*.json"):
        try:
            tasks.append(json.loads(f.read_text("utf-8")))
        except (json.JSONDecodeError, OSError):
            pass
    return tasks

def remove_cron_task(job_id: str) -> bool:
    """Remove a scheduled task."""
    path = SCHEDULER_DIR / f"{job_id}.json"
    if path.exists():
        try:
            path.unlink()
            return True
        except OSError:
            pass
    return False

# ── Background Runner ──────────────────────────────────────────────────────

_scheduler_running = False
_scheduler_thread = None

def _run_scheduler_loop():
    """Background thread that checks for jobs to run."""
    global _scheduler_running
    
    while _scheduler_running:
        now = datetime.now(timezone.utc)
        
        if SCHEDULER_DIR.exists():
            for f in SCHEDULER_DIR.glob("*.json"):
                try:
                    job = json.loads(f.read_text("utf-8"))
                    if not job.get("active"):
                        continue
                        
                    last_run_str = job.get("last_run")
                    interval = job.get("interval_secs", 60)
                    
                    should_run = False
                    if not last_run_str:
                        should_run = True
                    else:
                        last_run = datetime.fromisoformat(last_run_str)
                        if (now - last_run).total_seconds() >= interval:
                            should_run = True
                            
                    if should_run:
                        # Post the message
                        entry = {
                            "id": uuid.uuid4().hex[:12],
                            "timestamp": now.isoformat(),
                            "session_id": job.get("session_id", "system"),
                            "session_name": job.get("session_name", "scheduler"),
                            "platform": job.get("platform", "system"),
                            "channel": job.get("channel", "general"),
                            "msg_type": job.get("msg_type", "request"),
                            "message": f"[SCHEDULED: {job.get('name')}] {job.get('message')}",
                        }
                        
                        channel_path = CHANNELS_DIR / f"{job['channel']}.jsonl"
                        _append_jsonl_sync(channel_path, entry)
                        
                        # Update last_run
                        job["last_run"] = now.isoformat()
                        
                        tmp = f.with_suffix(".tmp")
                        tmp.write_text(json.dumps(job, indent=2))
                        os.replace(str(tmp), str(f))
                        
                except Exception:
                    pass
                    
        time.sleep(5) # Check every 5 seconds

def start_scheduler():
    """Start the background scheduler thread if not running."""
    global _scheduler_running, _scheduler_thread
    if not _scheduler_running:
        _scheduler_running = True
        _scheduler_thread = threading.Thread(target=_run_scheduler_loop, daemon=True)
        _scheduler_thread.start()

def stop_scheduler():
    """Stop the background scheduler thread."""
    global _scheduler_running
    _scheduler_running = False
