"""
Autonomous Loop — Curator-pattern background engine for report_agent.

Adapted from hermes-agent-repo/agent/curator.py. Provides an inactivity-
triggered background engine that can:
  - Auto-generate insights reports when idle
  - Suggest cron job schedules based on usage patterns
  - Run background maintenance tasks (prune old sessions, optimize DB)

The Curator pattern from Hermes uses periodic inactivity checks:
when no user interaction happens for N minutes, the agent autonomously
runs maintenance or insight-generation tasks.

Usage:
    from agent.report_agent.autonomous_loop import ReportCurator

    curator = ReportCurator()

    # Check if we should run autonomous tasks
    if curator.should_run():
        await curator.run_cycle()

    # Manual trigger
    suggestions = curator.suggest_cron_jobs()
"""

from __future__ import annotations

import json
import logging
import os
import time
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)

# ─── Constants ──────────────────────────────────────────────────────────

# Minimum idle time before autonomous tasks trigger (seconds)
DEFAULT_IDLE_THRESHOLD = 300  # 5 minutes

# Maximum age for report sessions before pruning (days)
DEFAULT_PRUNE_AGE_DAYS = 90

# State file for persistence across restarts
_STATE_FILENAME = ".report_curator_state"


def _get_state_path() -> Path:
    """Get the path to the curator state file."""
    base = Path(os.path.dirname(__file__)).parent.parent  # backend/
    return base / "data" / _STATE_FILENAME


class ReportCurator:
    """Background maintenance and autonomous insight engine.

    Adapted from hermes-agent-repo/agent/curator.py `maybe_run_curator()`
    inactivity-trigger pattern.
    """

    def __init__(
        self,
        idle_threshold: int = DEFAULT_IDLE_THRESHOLD,
        prune_age_days: int = DEFAULT_PRUNE_AGE_DAYS,
    ):
        self.idle_threshold = idle_threshold
        self.prune_age_days = prune_age_days
        self._state = self._load_state()

    # ── State persistence ──

    def _load_state(self) -> Dict[str, Any]:
        """Load curator state from disk."""
        path = _get_state_path()
        if path.exists():
            try:
                with open(path, "r", encoding="utf-8") as f:
                    return json.load(f)
            except (json.JSONDecodeError, OSError) as e:
                logger.warning("Failed to load curator state: %s", e)
        return {
            "last_run_at": 0,
            "last_insights_at": 0,
            "last_prune_at": 0,
            "total_runs": 0,
            "last_activity_at": time.time(),
        }

    def _save_state(self) -> None:
        """Persist curator state to disk."""
        path = _get_state_path()
        path.parent.mkdir(parents=True, exist_ok=True)
        try:
            with open(path, "w", encoding="utf-8") as f:
                json.dump(self._state, f, indent=2)
        except OSError as e:
            logger.warning("Failed to save curator state: %s", e)

    # ── Activity tracking ──

    def record_activity(self) -> None:
        """Record that user activity happened (resets idle timer)."""
        self._state["last_activity_at"] = time.time()

    def seconds_idle(self) -> float:
        """How many seconds since last user activity."""
        return time.time() - self._state.get("last_activity_at", time.time())

    def should_run(self) -> bool:
        """Check if autonomous tasks should run based on inactivity.

        Returns True if:
          - Idle for >= idle_threshold seconds
          - At least 1 hour since last run
        """
        idle = self.seconds_idle()
        last_run = self._state.get("last_run_at", 0)
        since_last = time.time() - last_run

        return idle >= self.idle_threshold and since_last >= 3600

    # ── Autonomous tasks ──

    async def run_cycle(self) -> Dict[str, Any]:
        """Run a full autonomous maintenance cycle.

        Tasks (in priority order):
          1. Generate usage insights (if > 1 day since last)
          2. Prune old sessions (if > 7 days since last)
          3. Suggest cron jobs based on activity patterns
        """
        results = {
            "ran_at": time.time(),
            "tasks_completed": [],
            "insights_generated": False,
            "sessions_pruned": 0,
            "cron_suggestions": [],
        }

        # Task 1: Generate insights
        last_insights = self._state.get("last_insights_at", 0)
        if time.time() - last_insights > 86400:  # 24h
            try:
                insights = await self._generate_insights()
                results["insights_generated"] = True
                results["insights_summary"] = insights
                results["tasks_completed"].append("insights")
                self._state["last_insights_at"] = time.time()
            except Exception as e:
                logger.warning("Insights generation failed: %s", e)

        # Task 2: Prune old sessions
        last_prune = self._state.get("last_prune_at", 0)
        if time.time() - last_prune > 604800:  # 7 days
            try:
                count = self._prune_old_sessions()
                results["sessions_pruned"] = count
                results["tasks_completed"].append("prune")
                self._state["last_prune_at"] = time.time()
            except Exception as e:
                logger.warning("Session pruning failed: %s", e)

        # Task 3: Suggest cron jobs
        try:
            suggestions = self.suggest_cron_jobs()
            results["cron_suggestions"] = suggestions
            if suggestions:
                results["tasks_completed"].append("cron_suggest")
        except Exception as e:
            logger.warning("Cron suggestion failed: %s", e)

        # Update state
        self._state["last_run_at"] = time.time()
        self._state["total_runs"] = self._state.get("total_runs", 0) + 1
        self._save_state()

        logger.info(
            "Curator cycle completed: %d tasks (%s)",
            len(results["tasks_completed"]),
            ", ".join(results["tasks_completed"]) or "none",
        )
        return results

    async def _generate_insights(self) -> str:
        """Generate insights report from recent session data."""
        from agent.report_agent.insights_adapter import ReportInsightsEngine

        engine = ReportInsightsEngine()
        report = engine.generate(days=7)
        return engine.format_summary(report)

    def _prune_old_sessions(self) -> int:
        """Remove sessions older than prune_age_days."""
        from agent.report_agent.hermes_bridge import ReportSessionDB

        db = ReportSessionDB()
        cutoff = time.time() - (self.prune_age_days * 86400)
        try:
            cursor = db._conn.execute(
                "SELECT COUNT(*) FROM report_sessions WHERE started_at < ?",
                (cutoff,),
            )
            count = cursor.fetchone()[0]
            if count > 0:
                db._conn.execute(
                    "DELETE FROM report_events WHERE session_id IN "
                    "(SELECT id FROM report_sessions WHERE started_at < ?)",
                    (cutoff,),
                )
                db._conn.execute(
                    "DELETE FROM report_sessions WHERE started_at < ?",
                    (cutoff,),
                )
                db._conn.commit()
                logger.info("Pruned %d sessions older than %d days", count, self.prune_age_days)
            return count
        except Exception as e:
            logger.warning("Prune failed: %s", e)
            return 0
        finally:
            db.close()

    # ── Cron job suggestions ──

    def suggest_cron_jobs(self) -> List[Dict[str, Any]]:
        """Suggest automated cron jobs based on usage patterns.

        Analyzes activity patterns and returns scheduling recommendations.
        """
        try:
            from agent.report_agent.insights_adapter import ReportInsightsEngine

            engine = ReportInsightsEngine()
            report = engine.generate(days=30)

            if report.get("empty"):
                return []

            suggestions = []
            overview = report.get("overview", {})
            activity = report.get("activity", {})
            anomalies = report.get("anomalies", [])

            # Suggest daily report if > 5 sessions/week
            total = overview.get("total_sessions", 0)
            if total >= 20:  # ~5/week over 30 days
                suggestions.append({
                    "job_type": "daily_insights",
                    "name": "Báo cáo insights hàng ngày",
                    "schedule": "0 8 * * *",
                    "prompt": "Phân tích hiệu suất report agent 24h qua: "
                              "sessions, tokens, errors, thời gian trung bình",
                    "reason": f"Hệ thống có {total} sessions/tháng — "
                              f"cần monitoring tự động",
                })

            # Suggest weekly trend report if active
            if total >= 10:
                suggestions.append({
                    "job_type": "weekly_trends",
                    "name": "Xu hướng sử dụng hàng tuần",
                    "schedule": "0 9 * * 1",  # Monday 9am
                    "prompt": "So sánh hiệu suất tuần này vs tuần trước: "
                              "sessions, success rate, avg gen time, top queries",
                    "reason": f"Đủ dữ liệu ({total} sessions) để phân tích trends",
                })

            # Suggest anomaly alert if error rate is high
            if any(a["type"] == "high_error_rate" for a in anomalies):
                suggestions.append({
                    "job_type": "error_alert",
                    "name": "Cảnh báo lỗi bất thường",
                    "schedule": "every 2h",
                    "prompt": "Kiểm tra error rate 2h qua — "
                              "nếu > 30% thì gửi cảnh báo với root cause",
                    "reason": "Error rate cao — cần monitoring real-time",
                })

            # Suggest peak-hour optimization
            busiest = activity.get("busiest_hour", {})
            if busiest and busiest.get("count", 0) >= 5:
                peak_hr = busiest["hour"]
                suggestions.append({
                    "job_type": "pre_warm",
                    "name": f"Pre-warm trước giờ cao điểm ({peak_hr}h)",
                    "schedule": f"0 {max(0, peak_hr-1)} * * *",
                    "prompt": "Warm up report pipeline: test connection DB, "
                              "verify LLM endpoint, check token budget",
                    "reason": f"Giờ cao điểm: {peak_hr}h "
                              f"({busiest['count']} sessions) — warm up trước",
                })

            return suggestions

        except Exception as e:
            logger.warning("Failed to generate cron suggestions: %s", e)
            return []

    # ── Status API ──

    def get_status(self) -> Dict[str, Any]:
        """Get curator status for dashboard display."""
        return {
            "idle_seconds": round(self.seconds_idle()),
            "should_run": self.should_run(),
            "last_run": datetime.fromtimestamp(
                self._state.get("last_run_at", 0)
            ).isoformat() if self._state.get("last_run_at") else None,
            "total_runs": self._state.get("total_runs", 0),
            "last_insights": datetime.fromtimestamp(
                self._state.get("last_insights_at", 0)
            ).isoformat() if self._state.get("last_insights_at") else None,
        }
