"""
Model Performance
Evaluates, ranks, and compares models based on latency and success rates.
"""
from typing import Dict, Any, List
import sqlite3
import time
from .usage_tracker import UsageTracker

class ModelPerformance:
    def __init__(self, tracker: UsageTracker):
        self.tracker = tracker

    def score_model(self, model: str, days: int = 30) -> Dict[str, Any]:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - days * 86400))
            
            # Fetch all latencies to compute percentiles easily
            c.execute('''SELECT latency_ms, status FROM interactions
                         WHERE model=? AND timestamp >= ? ORDER BY latency_ms''', (model, cutoff))
            rows = c.fetchall()
            
            if not rows:
                return {"model": model, "score": 0.0, "latency_p50": 0, "latency_p95": 0, "success_rate": 0.0, "total_interactions": 0}
                
            total = len(rows)
            successes = sum(1 for r in rows if r[1] == 'success')
            success_rate = successes / float(total)
            
            p50 = rows[int(total * 0.5)][0]
            p95 = rows[int(total * 0.95)][0] if total > 20 else rows[-1][0]
            
            # Normalize latency (assuming 10000ms is "bad" and 0 is "perfect")
            normalized_latency = min(1.0, p50 / 10000.0)
            score = (success_rate * 0.6) + ((1.0 - normalized_latency) * 0.4)
            
            return {
                "model": model,
                "score": score,
                "latency_p50": p50,
                "latency_p95": p95,
                "success_rate": success_rate,
                "total_interactions": total
            }

    def rolling_average(self, model: str, metric: str, window_days: int = 7) -> List[Dict[str, Any]]:
        # For simplicity, we just calculate daily averages and then a simple window
        daily = self.tracker.model_usage_over_time(model, days=30)
        # Assuming metric is latency_ms
        res = []
        for i in range(len(daily)):
            window = daily[max(0, i - window_days + 1) : i + 1]
            avg = sum(w["avg_latency_ms"] for w in window) / len(window) if window else 0
            res.append({"date": daily[i]["date"], "value": daily[i]["avg_latency_ms"], "rolling_avg": avg})
        return res

    def best_model_for_strategy(self, strategy: str) -> str | None:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            c.execute('''SELECT model, SUM(CASE WHEN status='success' THEN 1 ELSE 0 END)*1.0/COUNT(*) as succ
                         FROM interactions WHERE strategy=? GROUP BY model ORDER BY succ DESC, AVG(latency_ms) ASC LIMIT 1''', (strategy,))
            row = c.fetchone()
            return row[0] if row else None

    def rank_models(self, days: int = 30) -> List[Dict[str, Any]]:
        top = self.tracker.top_models(days=days, limit=20)
        scored = [self.score_model(m["model"], days) for m in top]
        return sorted(scored, key=lambda x: x["score"], reverse=True)

    def degradation_alerts(self, threshold_pct: float = 20.0) -> List[Dict[str, Any]]:
        top = self.tracker.top_models(days=14, limit=10)
        alerts = []
        now = time.time()
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            for m in top:
                model = m["model"]
                recent_cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(now - 7 * 86400))
                old_cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(now - 14 * 86400))
                
                c.execute('''SELECT AVG(latency_ms) FROM interactions WHERE model=? AND timestamp >= ?''', (model, recent_cutoff))
                recent_lat = c.fetchone()[0] or 0
                
                c.execute('''SELECT AVG(latency_ms) FROM interactions WHERE model=? AND timestamp >= ? AND timestamp < ?''', (model, old_cutoff, recent_cutoff))
                old_lat = c.fetchone()[0] or 0
                
                if old_lat > 0:
                    change = ((recent_lat - old_lat) / old_lat) * 100
                    if change > threshold_pct:
                        alerts.append({"model": model, "metric": "latency_ms", "old_value": old_lat, "new_value": recent_lat, "change_pct": change})
        return alerts

    def compare_models(self, model_a: str, model_b: str, days: int = 30) -> Dict[str, Any]:
        sa = self.score_model(model_a, days)
        sb = self.score_model(model_b, days)
        winner = model_a if sa["score"] > sb["score"] else model_b
        return {
            model_a: {"score": sa["score"], "latency": sa["latency_p50"], "success_rate": sa["success_rate"]},
            model_b: {"score": sb["score"], "latency": sb["latency_p50"], "success_rate": sb["success_rate"]},
            "winner": winner
        }

    def latency_percentiles(self, model: str, days: int = 30) -> Dict[str, Any]:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - days * 86400))
            c.execute('''SELECT latency_ms FROM interactions WHERE model=? AND timestamp >= ? ORDER BY latency_ms''', (model, cutoff))
            rows = c.fetchall()
            
            if not rows:
                return {"p50": 0, "p75": 0, "p90": 0, "p95": 0, "p99": 0, "max": 0}
                
            total = len(rows)
            return {
                "p50": rows[int(total * 0.5)][0],
                "p75": rows[int(total * 0.75)][0],
                "p90": rows[int(total * 0.90)][0],
                "p95": rows[int(total * 0.95)][0],
                "p99": rows[int(total * 0.99)][0],
                "max": rows[-1][0]
            }

    def token_efficiency(self, model: str, days: int = 30) -> Dict[str, Any]:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - days * 86400))
            c.execute('''SELECT AVG(prompt_tokens), AVG(response_tokens), SUM(prompt_tokens + response_tokens)
                         FROM interactions WHERE model=? AND timestamp >= ?''', (model, cutoff))
            row = c.fetchone()
            if not row or not row[0]:
                return {"avg_prompt_tokens": 0, "avg_response_tokens": 0, "response_per_prompt_ratio": 0.0, "total_tokens": 0}
                
            return {
                "avg_prompt_tokens": row[0],
                "avg_response_tokens": row[1],
                "response_per_prompt_ratio": row[1] / row[0] if row[0] > 0 else 0.0,
                "total_tokens": row[2]
            }

    def success_rate_trend(self, model: str, days: int = 30) -> List[Dict[str, Any]]:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - days * 86400))
            c.execute('''SELECT strftime('%Y-%m-%d', timestamp), SUM(CASE WHEN status='success' THEN 1 ELSE 0 END)*1.0/COUNT(*), COUNT(*)
                         FROM interactions WHERE model=? AND timestamp >= ?
                         GROUP BY strftime('%Y-%m-%d', timestamp) ORDER BY strftime('%Y-%m-%d', timestamp)''', (model, cutoff))
            return [{"date": r[0], "success_rate": r[1], "total": r[2]} for r in c.fetchall()]

    def error_analysis(self, model: str, days: int = 30) -> Dict[str, Any]:
        with sqlite3.connect(self.tracker.db_path) as conn:
            c = conn.cursor()
            cutoff = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - days * 86400))
            c.execute('''SELECT status, COUNT(*) FROM interactions WHERE model=? AND timestamp >= ? GROUP BY status''', (model, cutoff))
            rows = c.fetchall()
            
            total = sum(r[1] for r in rows)
            errors = sum(r[1] for r in rows if r[0] != 'success')
            error_by_status = {r[0]: r[1] for r in rows if r[0] != 'success'}
            
            return {
                "total_errors": errors,
                "errors_by_status": error_by_status,
                "error_rate": errors / float(total) if total > 0 else 0.0
            }

    def recommend_model(self, task_hint: str | None = None) -> str:
        # If task hint matches a strategy
        if task_hint:
            best = self.best_model_for_strategy(task_hint)
            if best: return best
            
        ranked = self.rank_models(days=7)
        if ranked:
            return ranked[0]["model"]
        return "claude" # fallback

    def performance_summary(self) -> Dict[str, Any]:
        ranked = self.rank_models(days=30)
        if not ranked:
            return {"total_models": 0, "top_model": None, "worst_model": None, "avg_score": 0.0, "alerts": []}
            
        return {
            "total_models": len(ranked),
            "top_model": ranked[0]["model"],
            "worst_model": ranked[-1]["model"],
            "avg_score": sum(m["score"] for m in ranked) / len(ranked),
            "alerts": self.degradation_alerts()
        }
