"""
Beta Monitoring — SaaS Turf Prédictions IA
Sprint 8 — QA, Beta Fermee, Go/No-Go
Ticket: HRT-34

Ce module :
- Collecte les feedbacks beta via l'API in-app
- Envoie des alertes Telegram en cas d'erreur détectée pendant la beta
- Génère le rapport beta final (bugs, UX, NPS)

Usage :
  # Démarrer le monitoring beta
  python tests/beta_monitor.py --watch --interval 60

  # Générer le rapport beta final
  python tests/beta_monitor.py --report

  # Test d'envoi Telegram
  python tests/beta_monitor.py --test-telegram
"""

import os
import sys
import json
import time
import sqlite3
import requests
import argparse
from datetime import datetime, timedelta
from pathlib import Path

# ============================================================
# Configuration
# ============================================================

BASE_URL = os.environ.get("APP_URL", "http://localhost:8792")
TELEGRAM_TOKEN = os.environ.get(
    "TELEGRAM_TOKEN", "8649773134:AAFqzZVtSHfPPFDadcte1B-1h23nZ8DmdYE"
)
TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")  # À configurer

BETA_DB_PATH = os.environ.get("BETA_DB_PATH", "/home/h3r7/turf_saas/turf_saas.db")
REPORTS_DIR = Path("tests/reports")
REPORTS_DIR.mkdir(parents=True, exist_ok=True)

# Seuils d'alerte
ERROR_RATE_THRESHOLD = 0.01  # 1% d'erreurs → alerte
LATENCY_P95_THRESHOLD_MS = 500  # p95 > 500ms → alerte
BETA_MIN_USERS = 10  # Minimum d'utilisateurs beta requis
NPS_TARGET = 7.0  # NPS cible (sur 10)


# ============================================================
# Alertes Telegram
# ============================================================


def send_telegram(message: str, parse_mode: str = "Markdown") -> bool:
    """Envoie un message Telegram d'alerte."""
    if not TELEGRAM_TOKEN or not TELEGRAM_CHAT_ID:
        print(f"⚠️  Telegram non configuré. Message: {message[:100]}")
        return False

    try:
        resp = requests.post(
            f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage",
            json={
                "chat_id": TELEGRAM_CHAT_ID,
                "text": message,
                "parse_mode": parse_mode,
            },
            timeout=10,
        )
        if resp.status_code == 200:
            print(f"✅ Alerte Telegram envoyée")
            return True
        else:
            print(f"❌ Telegram erreur: {resp.status_code} — {resp.text}")
            return False
    except Exception as e:
        print(f"❌ Telegram exception: {e}")
        return False


def alert_error(endpoint: str, status_code: int, message: str):
    """Alerte Telegram sur erreur critique."""
    text = (
        f"🚨 *ALERTE BETA — SaaS Turf IA*\n\n"
        f"Erreur détectée sur `{endpoint}`\n"
        f"Status: `{status_code}`\n"
        f"Message: {message[:200]}\n"
        f"Heure: {datetime.now().strftime('%H:%M:%S')}\n\n"
        f"_Ticket: HRT-34_"
    )
    send_telegram(text)


def alert_performance(p95_ms: float, error_rate: float):
    """Alerte Telegram sur dégradation de performance."""
    text = (
        f"⚠️ *ALERTE PERFORMANCE — SaaS Turf IA*\n\n"
        f"p95 latence: `{p95_ms:.0f}ms` (seuil: {LATENCY_P95_THRESHOLD_MS}ms)\n"
        f"Error rate: `{error_rate * 100:.2f}%` (seuil: {ERROR_RATE_THRESHOLD * 100:.1f}%)\n"
        f"Heure: {datetime.now().strftime('%H:%M:%S')}\n\n"
        f"_Ticket: HRT-34_"
    )
    send_telegram(text)


# ============================================================
# Collecte de métriques
# ============================================================


class BetaMonitor:
    """Moniteur actif pendant la beta fermée."""

    ENDPOINTS_TO_CHECK = [
        "/api",
        "/api/races",
        "/api/scoring",
        "/dashboard",
        "/",
    ]

    def __init__(self, base_url: str = BASE_URL):
        self.base_url = base_url.rstrip("/")
        self.errors: list[dict] = []
        self.latencies: list[float] = []
        self.check_count = 0

    def check_endpoint(self, path: str) -> dict:
        """Vérifie un endpoint et retourne le résultat."""
        start = time.time()
        try:
            resp = requests.get(f"{self.base_url}{path}", timeout=10)
            latency_ms = (time.time() - start) * 1000
            return {
                "path": path,
                "status": resp.status_code,
                "latency_ms": latency_ms,
                "ok": resp.status_code < 500,
                "timestamp": datetime.now().isoformat(),
            }
        except requests.exceptions.ConnectionError as e:
            return {
                "path": path,
                "status": 0,
                "latency_ms": 0,
                "ok": False,
                "error": str(e),
                "timestamp": datetime.now().isoformat(),
            }
        except Exception as e:
            return {
                "path": path,
                "status": 0,
                "latency_ms": 0,
                "ok": False,
                "error": str(e),
                "timestamp": datetime.now().isoformat(),
            }

    def run_checks(self) -> dict:
        """Exécute tous les checks et retourne un résumé."""
        results = [self.check_endpoint(p) for p in self.ENDPOINTS_TO_CHECK]
        self.check_count += 1

        failures = [r for r in results if not r["ok"]]
        latencies = [r["latency_ms"] for r in results if r["latency_ms"] > 0]

        p95 = (
            sorted(latencies)[int(len(latencies) * 0.95)]
            if len(latencies) >= 2
            else (latencies[0] if latencies else 0)
        )
        error_rate = len(failures) / len(results) if results else 0

        # Stocker pour rapport
        self.latencies.extend(latencies)
        self.errors.extend(failures)

        return {
            "check_number": self.check_count,
            "timestamp": datetime.now().isoformat(),
            "total_checks": len(results),
            "failures": len(failures),
            "error_rate": error_rate,
            "p95_ms": p95,
            "results": results,
        }

    def watch(self, interval_seconds: int = 60):
        """Surveillance continue avec alertes Telegram."""
        print(f"🔍 Beta monitoring démarré — {self.base_url}")
        print(f"   Intervalle: {interval_seconds}s")
        print(f"   Endpoints: {len(self.ENDPOINTS_TO_CHECK)}")
        print(f"   Ctrl+C pour arrêter\n")

        consecutive_errors = 0

        try:
            while True:
                summary = self.run_checks()
                timestamp = datetime.now().strftime("%H:%M:%S")

                status_icon = "✅" if summary["error_rate"] == 0 else "❌"
                print(
                    f"[{timestamp}] {status_icon} "
                    f"Check #{summary['check_number']} — "
                    f"p95={summary['p95_ms']:.0f}ms, "
                    f"errors={summary['failures']}/{summary['total_checks']}"
                )

                # Alertes
                if summary["error_rate"] > ERROR_RATE_THRESHOLD:
                    consecutive_errors += 1
                    if consecutive_errors >= 2:  # 2 checks consécutifs en erreur
                        for failure in summary["results"]:
                            if not failure["ok"]:
                                alert_error(
                                    failure["path"],
                                    failure.get("status", 0),
                                    failure.get("error", "Non-2xx response"),
                                )
                else:
                    consecutive_errors = 0

                if summary["p95_ms"] > LATENCY_P95_THRESHOLD_MS:
                    print(f"⚠️  Latence p95 élevée: {summary['p95_ms']:.0f}ms")
                    if summary["p95_ms"] > LATENCY_P95_THRESHOLD_MS * 2:
                        alert_performance(summary["p95_ms"], summary["error_rate"])

                # Sauvegarder les résultats
                log_file = REPORTS_DIR / "beta_monitor_log.jsonl"
                with open(log_file, "a") as f:
                    f.write(json.dumps(summary) + "\n")

                time.sleep(interval_seconds)

        except KeyboardInterrupt:
            print(f"\n⏹️  Monitoring arrêté après {self.check_count} checks")
            self.generate_report()


# ============================================================
# Rapport beta final
# ============================================================


class BetaReport:
    """Générateur de rapport beta fermée."""

    def __init__(self, base_url: str = BASE_URL):
        self.base_url = base_url
        self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    def collect_feedback_from_db(self) -> list[dict]:
        """Collecte les feedbacks depuis la BDD (table beta_feedback si elle existe)."""
        try:
            conn = sqlite3.connect(BETA_DB_PATH)
            c = conn.cursor()
            c.execute(
                "SELECT name FROM sqlite_master WHERE type='table' AND name='beta_feedback'"
            )
            if not c.fetchone():
                conn.close()
                return []
            c.execute("SELECT * FROM beta_feedback ORDER BY created_at DESC")
            rows = c.fetchall()
            conn.close()
            return [dict(zip([col[0] for col in c.description], row)) for row in rows]
        except Exception as e:
            print(f"⚠️  Impossible de lire beta_feedback: {e}")
            return []

    def collect_monitor_logs(self) -> list[dict]:
        """Lit les logs du monitoring beta."""
        log_file = REPORTS_DIR / "beta_monitor_log.jsonl"
        if not log_file.exists():
            return []
        entries = []
        with open(log_file) as f:
            for line in f:
                try:
                    entries.append(json.loads(line))
                except Exception:
                    pass
        return entries

    def generate(self) -> str:
        """Génère le rapport complet et le sauvegarde."""
        feedbacks = self.collect_feedback_from_db()
        monitor_logs = self.collect_monitor_logs()

        # Calculer NPS depuis les feedbacks
        nps_scores = [
            f.get("nps_score") for f in feedbacks if f.get("nps_score") is not None
        ]
        avg_nps = sum(nps_scores) / len(nps_scores) if nps_scores else None

        # Statistiques monitoring
        if monitor_logs:
            all_latencies = []
            total_errors = 0
            total_checks = 0
            for entry in monitor_logs:
                all_latencies.extend(
                    [
                        r["latency_ms"]
                        for r in entry.get("results", [])
                        if r.get("latency_ms", 0) > 0
                    ]
                )
                total_errors += entry.get("failures", 0)
                total_checks += entry.get("total_checks", 0)
            avg_latency = (
                sum(all_latencies) / len(all_latencies) if all_latencies else 0
            )
            overall_error_rate = total_errors / total_checks if total_checks > 0 else 0
        else:
            avg_latency = 0
            overall_error_rate = 0
            total_checks = 0

        # Construire le rapport
        report = []
        report.append("=" * 60)
        report.append("RAPPORT BETA FERMÉE — SaaS Turf Prédictions IA")
        report.append(f"Généré le : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        report.append(f"Ticket    : HRT-34")
        report.append("=" * 60)
        report.append("")
        report.append("## 1. PARTICIPANTS BETA")
        report.append(f"   Feedbacks reçus : {len(feedbacks)}")
        report.append(
            f"   NPS moyen       : {avg_nps:.1f}/10"
            if avg_nps
            else "   NPS moyen       : (en attente feedbacks)"
        )
        report.append(f"   Cible NPS       : ≥ {NPS_TARGET}/10")
        nps_ok = avg_nps is not None and avg_nps >= NPS_TARGET
        report.append(
            f"   Statut NPS      : {'✅ OBJECTIF ATTEINT' if nps_ok else '⏳ En attente' if avg_nps is None else '❌ OBJECTIF NON ATTEINT'}"
        )
        report.append("")
        report.append("## 2. BUGS SIGNALÉS")
        bugs = [f for f in feedbacks if f.get("type") == "bug"]
        critical_bugs = [b for b in bugs if b.get("severity") in ("critical", "high")]
        report.append(f"   Total bugs      : {len(bugs)}")
        report.append(f"   Critiques/High  : {len(critical_bugs)}")
        report.append(
            f"   Statut          : {'✅ 0 bug critique' if len(critical_bugs) == 0 else f'❌ {len(critical_bugs)} bug(s) critique(s)'}"
        )
        report.append("")
        report.append("## 3. PERFORMANCE RÉELLE (monitoring)")
        report.append(f"   Checks effectués: {total_checks}")
        report.append(f"   Latence moyenne : {avg_latency:.1f}ms")
        report.append(f"   Error rate      : {overall_error_rate * 100:.2f}%")
        report.append(f"   Seuil latence   : {LATENCY_P95_THRESHOLD_MS}ms")
        perf_ok = (
            avg_latency < LATENCY_P95_THRESHOLD_MS
            and overall_error_rate < ERROR_RATE_THRESHOLD
        )
        report.append(
            f"   Statut          : {'✅ OBJECTIF ATTEINT' if perf_ok else '⏳ Données insuffisantes' if total_checks == 0 else '❌ OBJECTIF NON ATTEINT'}"
        )
        report.append("")
        report.append("## 4. FEEDBACKS UX")
        ux_feedbacks = [f for f in feedbacks if f.get("type") == "ux"]
        report.append(f"   Retours UX      : {len(ux_feedbacks)}")
        if ux_feedbacks:
            for fb in ux_feedbacks[:5]:  # Top 5
                report.append(f"   - {fb.get('comment', '')[:100]}")
        report.append("")
        report.append("## 5. VERDICT BETA FERMÉE")
        users_ok = len(feedbacks) >= 5  # Au moins 5 feedbacks = 5 users satisfaits
        verdict = all([users_ok, nps_ok, len(critical_bugs) == 0])
        report.append(
            f"   Participants suffisants (≥5)  : {'✅' if users_ok else '❌'}"
        )
        report.append(f"   NPS ≥ 7/10                   : {'✅' if nps_ok else '❌'}")
        report.append(
            f"   0 bug critique               : {'✅' if len(critical_bugs) == 0 else '❌'}"
        )
        report.append("")
        report.append(
            f"   VERDICT GLOBAL : {'✅ GO — Beta réussie' if verdict else '❌ NO-GO — Conditions non remplies'}"
        )
        report.append("=" * 60)

        report_text = "\n".join(report)

        # Sauvegarder
        report_file = REPORTS_DIR / f"beta_report_{self.timestamp}.txt"
        with open(report_file, "w") as f:
            f.write(report_text)

        print(report_text)
        print(f"\nRapport sauvegardé : {report_file}")

        return report_text


# ============================================================
# CLI
# ============================================================


def main():
    parser = argparse.ArgumentParser(description="Beta Monitor — SaaS Turf IA")
    parser.add_argument("--watch", action="store_true", help="Surveillance continue")
    parser.add_argument(
        "--interval", type=int, default=60, help="Intervalle en secondes (défaut: 60)"
    )
    parser.add_argument(
        "--report", action="store_true", help="Générer le rapport beta final"
    )
    parser.add_argument(
        "--test-telegram", action="store_true", help="Tester l'envoi Telegram"
    )
    parser.add_argument(
        "--url", default=BASE_URL, help=f"URL de l'app (défaut: {BASE_URL})"
    )
    args = parser.parse_args()

    if args.test_telegram:
        print("Test d'envoi Telegram...")
        ok = send_telegram(
            "✅ *Test alerte Beta* — SaaS Turf IA\n_Ceci est un test du système d'alertes QA_\nTicket: HRT-34"
        )
        sys.exit(0 if ok else 1)

    if args.report:
        reporter = BetaReport(args.url)
        reporter.generate()
        sys.exit(0)

    if args.watch:
        monitor = BetaMonitor(args.url)
        monitor.watch(interval_seconds=args.interval)
        sys.exit(0)

    parser.print_help()


if __name__ == "__main__":
    main()
