"""
Insights Adapter — Session analytics engine for Canifa report agent.

Adapted from hermes-agent-repo/agent/insights.py. Provides automated
insight generation from report session data stored in ReportSessionDB.

The InsightsEngine connects to the report agent's SQLite state database
and computes usage analytics, performance trends, and anomaly detection
to enable autonomous report scheduling.

Usage:
    from agent.report_agent.insights_adapter import ReportInsightsEngine

    engine = ReportInsightsEngine()
    report = engine.generate(days=30)
    summary = engine.format_summary(report)
"""

from __future__ import annotations

import logging
import time
from collections import Counter, defaultdict
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

from agent.report_agent.hermes_bridge import (
    ReportSessionDB,
    format_duration,
)

logger = logging.getLogger(__name__)


class ReportInsightsEngine:
    """Generates usage insights from report agent session history.

    Adapted from hermes-agent-repo/agent/insights.py InsightsEngine,
    specialized for report generation patterns.
    """

    def __init__(self, db: ReportSessionDB = None):
        self._db = db

    @property
    def db(self) -> ReportSessionDB:
        if self._db is None:
            self._db = ReportSessionDB()
        return self._db

    def generate(self, days: int = 30) -> Dict[str, Any]:
        """Generate a comprehensive insights report for the last N days.

        Returns a structured dict with:
          - overview: aggregate stats
          - performance: timing/sizing metrics
          - tool_breakdown: which SQL tools are most used
          - activity: day/hour patterns
          - anomalies: detected issues
          - top_questions: most common query patterns
        """
        sessions = self.db.get_sessions(limit=10000, status=None)
        cutoff = time.time() - (days * 86400)
        filtered = [s for s in sessions if s.get("started_at", 0) >= cutoff]

        if not filtered:
            return {"empty": True, "days": days}

        return {
            "days": days,
            "overview": self._compute_overview(filtered),
            "performance": self._compute_performance(filtered),
            "tool_breakdown": self._compute_tool_breakdown(filtered),
            "activity": self._compute_activity_patterns(filtered),
            "anomalies": self._detect_anomalies(filtered),
            "top_questions": self._compute_top_questions(filtered),
        }

    # ── Internal computation ──

    def _compute_overview(self, sessions: List[Dict]) -> Dict[str, Any]:
        """Aggregate session statistics."""
        total = len(sessions)
        success = sum(1 for s in sessions if s.get("status") == "done")
        errors = sum(1 for s in sessions if s.get("status") == "error")
        total_input = sum(s.get("input_tokens", 0) or 0 for s in sessions)
        total_output = sum(s.get("output_tokens", 0) or 0 for s in sessions)
        total_errors = sum(s.get("error_count", 0) or 0 for s in sessions)
        total_compressions = sum(s.get("compression_count", 0) or 0 for s in sessions)
        avg_cycles = (
            sum(s.get("cycles_count", 0) or 0 for s in sessions) / total
            if total else 0
        )

        return {
            "total_sessions": total,
            "success_count": success,
            "error_count": errors,
            "success_rate": round(success / total * 100, 1) if total else 0,
            "total_tokens": total_input + total_output,
            "total_input_tokens": total_input,
            "total_output_tokens": total_output,
            "avg_cycles": round(avg_cycles, 1),
            "total_errors": total_errors,
            "total_compressions": total_compressions,
        }

    def _compute_performance(self, sessions: List[Dict]) -> Dict[str, Any]:
        """Timing and sizing metrics."""
        times = [
            s.get("generation_time_ms", 0) or 0
            for s in sessions
            if s.get("generation_time_ms")
        ]
        html_sizes = [
            s.get("html_length", 0) or 0
            for s in sessions
            if s.get("html_length")
        ]

        def _percentile(data: List[float], pct: float) -> float:
            if not data:
                return 0
            sorted_data = sorted(data)
            idx = int(len(sorted_data) * pct / 100)
            return sorted_data[min(idx, len(sorted_data) - 1)]

        return {
            "avg_generation_ms": round(sum(times) / len(times)) if times else 0,
            "p50_generation_ms": round(_percentile(times, 50)),
            "p95_generation_ms": round(_percentile(times, 95)),
            "max_generation_ms": max(times) if times else 0,
            "avg_html_length": round(sum(html_sizes) / len(html_sizes)) if html_sizes else 0,
            "max_html_length": max(html_sizes) if html_sizes else 0,
        }

    def _compute_tool_breakdown(self, sessions: List[Dict]) -> List[Dict[str, Any]]:
        """Which tools are most frequently used in report generation."""
        import json

        tool_counts: Counter = Counter()
        for s in sessions:
            tools_raw = s.get("tools_used", "[]")
            if isinstance(tools_raw, str):
                try:
                    tools = json.loads(tools_raw)
                except (json.JSONDecodeError, TypeError):
                    tools = []
            else:
                tools = tools_raw or []
            for t in tools:
                tool_counts[t] += 1

        total = sum(tool_counts.values()) or 1
        return [
            {
                "tool": name,
                "count": count,
                "percentage": round(count / total * 100, 1),
            }
            for name, count in tool_counts.most_common(20)
        ]

    def _compute_activity_patterns(self, sessions: List[Dict]) -> Dict[str, Any]:
        """Day-of-week and hour-of-day activity patterns."""
        day_names = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        by_day = [{"day": d, "count": 0} for d in day_names]
        by_hour = [{"hour": h, "count": 0} for h in range(24)]

        for s in sessions:
            ts = s.get("started_at")
            if not ts:
                continue
            dt = datetime.fromtimestamp(ts)
            by_day[dt.weekday()]["count"] += 1
            by_hour[dt.hour]["count"] += 1

        busiest_day = max(by_day, key=lambda x: x["count"]) if by_day else None
        busiest_hour = max(by_hour, key=lambda x: x["count"]) if by_hour else None

        return {
            "by_day": by_day,
            "by_hour": by_hour,
            "busiest_day": busiest_day,
            "busiest_hour": busiest_hour,
        }

    def _detect_anomalies(self, sessions: List[Dict]) -> List[Dict[str, Any]]:
        """Detect anomalous patterns in report generation."""
        anomalies = []

        # High error rate
        total = len(sessions)
        errors = sum(1 for s in sessions if s.get("status") == "error")
        if total >= 5 and errors / total > 0.3:
            anomalies.append({
                "type": "high_error_rate",
                "severity": "warning",
                "message": f"Error rate is {errors}/{total} ({errors/total*100:.0f}%) "
                           f"— investigate common failure patterns",
                "metric": round(errors / total * 100, 1),
            })

        # Slow generation (p95 > 30s)
        times = [s.get("generation_time_ms", 0) or 0 for s in sessions if s.get("generation_time_ms")]
        if times:
            p95 = sorted(times)[int(len(times) * 0.95)] if len(times) >= 5 else max(times)
            if p95 > 30000:
                anomalies.append({
                    "type": "slow_generation",
                    "severity": "info",
                    "message": f"P95 generation time is {p95/1000:.1f}s — consider query optimization",
                    "metric": p95,
                })

        # High compression rate
        total_compressions = sum(s.get("compression_count", 0) or 0 for s in sessions)
        if total >= 5 and total_compressions / total > 1.0:
            anomalies.append({
                "type": "frequent_compression",
                "severity": "info",
                "message": f"Avg {total_compressions/total:.1f} compressions per session "
                           f"— queries may be returning too much data",
                "metric": round(total_compressions / total, 1),
            })

        return anomalies

    def _compute_top_questions(self, sessions: List[Dict]) -> List[Dict[str, Any]]:
        """Find most frequently asked question patterns."""
        patterns: Counter = Counter()
        for s in sessions:
            q = (s.get("question") or "").strip().lower()
            if not q:
                continue
            # Normalize: remove dates, numbers, specific values
            import re
            normalized = re.sub(r"\d{4}[-/]\d{1,2}[-/]\d{1,2}", "<DATE>", q)
            normalized = re.sub(r"\d+", "<N>", normalized)
            normalized = normalized[:100]  # Truncate for grouping
            patterns[normalized] += 1

        return [
            {"pattern": pattern, "count": count}
            for pattern, count in patterns.most_common(10)
        ]

    # ── Formatting ──

    def format_summary(self, report: Dict[str, Any]) -> str:
        """Format insights report as markdown for LLM context injection."""
        if report.get("empty"):
            return f"📊 No report sessions in the last {report.get('days', 30)} days."

        lines = []
        o = report["overview"]
        days = report["days"]

        lines.append(f"📊 **Report Agent Insights** — Last {days} days\n")

        # Overview
        lines.append(
            f"**Sessions:** {o['total_sessions']} | "
            f"**Success rate:** {o['success_rate']}% | "
            f"**Avg cycles:** {o['avg_cycles']}"
        )
        lines.append(
            f"**Tokens:** {o['total_tokens']:,} "
            f"(in: {o['total_input_tokens']:,} / out: {o['total_output_tokens']:,})"
        )
        lines.append("")

        # Performance
        perf = report.get("performance", {})
        if perf.get("avg_generation_ms"):
            lines.append("**⚡ Performance:**")
            lines.append(
                f"  Avg: {perf['avg_generation_ms']/1000:.1f}s | "
                f"P50: {perf['p50_generation_ms']/1000:.1f}s | "
                f"P95: {perf['p95_generation_ms']/1000:.1f}s"
            )
            lines.append("")

        # Tools
        tools = report.get("tool_breakdown", [])
        if tools:
            lines.append("**🔧 Top Tools:**")
            for t in tools[:5]:
                lines.append(f"  {t['tool']} — {t['count']:,} calls ({t['percentage']}%)")
            lines.append("")

        # Anomalies
        anomalies = report.get("anomalies", [])
        if anomalies:
            lines.append("**⚠️ Anomalies:**")
            for a in anomalies:
                icon = "🔴" if a["severity"] == "warning" else "🟡"
                lines.append(f"  {icon} {a['message']}")
            lines.append("")

        # Top questions
        top_q = report.get("top_questions", [])
        if top_q:
            lines.append("**❓ Common Questions:**")
            for q in top_q[:5]:
                lines.append(f"  `{q['pattern']}` — {q['count']}×")
            lines.append("")

        return "\n".join(lines)
