#!/usr/bin/env python3
"""
Zoom Network Checker by DASecure
TUI app to verify your connection is ready for Zoom calls
"""

import subprocess
import re
import statistics
import time
import socket
import sys
from datetime import datetime

try:
    from rich.console import Console
    from rich.panel import Panel
    from rich.table import Table
    from rich.live import Live
    from rich.progress import Progress, SpinnerColumn, TextColumn
    from rich import box
except ImportError:
    print("Installing rich...")
    subprocess.run([sys.executable, "-m", "pip", "install", "rich"], check=True)
    from rich.console import Console
    from rich.panel import Panel
    from rich.table import Table
    from rich.live import Live
    from rich.progress import Progress, SpinnerColumn, TextColumn
    from rich import box

try:
    import speedtest
except ImportError:
    print("Installing speedtest-cli...")
    subprocess.run([sys.executable, "-m", "pip", "install", "speedtest-cli"], check=True)
    import speedtest

console = Console()

# Zoom requirements
ZOOM_REQUIREMENTS = {
    "download_min": 3.0,      # Mbps for HD
    "upload_min": 3.0,        # Mbps for HD
    "latency_max": 150,       # ms
    "jitter_max": 30,         # ms
    "packet_loss_max": 1.0,   # %
}


def tcp_latency_test(host, port=443, count=5):
    """Test latency using TCP connection (works without ping)"""
    times = []
    failures = 0
    
    for _ in range(count):
        try:
            start = time.time()
            sock = socket.create_connection((host, port), timeout=3)
            end = time.time()
            sock.close()
            times.append((end - start) * 1000)  # Convert to ms
        except Exception:
            failures += 1
        time.sleep(0.1)
    
    if not times:
        return None, None, 100.0
    
    latency = statistics.mean(times)
    jitter = statistics.stdev(times) if len(times) > 1 else 0
    packet_loss = (failures / count) * 100
    
    return latency, jitter, packet_loss


def speed_test():
    """Run a speed test and return download/upload speeds in Mbps"""
    try:
        st = speedtest.Speedtest()
        st.get_best_server()
        
        # Run tests
        download = st.download() / 1_000_000  # Convert to Mbps
        upload = st.upload() / 1_000_000      # Convert to Mbps
        
        return download, upload, st.results.server.get("sponsor", "Unknown")
    except Exception as e:
        return None, None, None


def check_zoom_readiness(results):
    """Determine if connection is ready for Zoom"""
    issues = []
    
    if results["download"] and results["download"] < ZOOM_REQUIREMENTS["download_min"]:
        issues.append(f"Download too slow ({results['download']:.1f} Mbps < {ZOOM_REQUIREMENTS['download_min']} Mbps)")
    
    if results["upload"] and results["upload"] < ZOOM_REQUIREMENTS["upload_min"]:
        issues.append(f"Upload too slow ({results['upload']:.1f} Mbps < {ZOOM_REQUIREMENTS['upload_min']} Mbps)")
    
    if results["latency"] and results["latency"] > ZOOM_REQUIREMENTS["latency_max"]:
        issues.append(f"Latency too high ({results['latency']:.0f} ms > {ZOOM_REQUIREMENTS['latency_max']} ms)")
    
    if results["jitter"] and results["jitter"] > ZOOM_REQUIREMENTS["jitter_max"]:
        issues.append(f"Jitter too high ({results['jitter']:.1f} ms > {ZOOM_REQUIREMENTS['jitter_max']} ms)")
    
    if results["packet_loss"] and results["packet_loss"] > ZOOM_REQUIREMENTS["packet_loss_max"]:
        issues.append(f"Packet loss too high ({results['packet_loss']:.1f}% > {ZOOM_REQUIREMENTS['packet_loss_max']}%)")
    
    return len(issues) == 0, issues


def get_quality_score(results):
    """Calculate an overall quality score (0-100)"""
    score = 100
    
    if results["download"]:
        if results["download"] < 1:
            score -= 30
        elif results["download"] < 3:
            score -= 15
        elif results["download"] >= 10:
            score += 5
    
    if results["upload"]:
        if results["upload"] < 1:
            score -= 30
        elif results["upload"] < 3:
            score -= 15
        elif results["upload"] >= 10:
            score += 5
    
    if results["latency"]:
        if results["latency"] > 200:
            score -= 20
        elif results["latency"] > 100:
            score -= 10
        elif results["latency"] < 30:
            score += 5
    
    if results["jitter"]:
        if results["jitter"] > 50:
            score -= 15
        elif results["jitter"] > 20:
            score -= 8
    
    if results["packet_loss"]:
        if results["packet_loss"] > 3:
            score -= 20
        elif results["packet_loss"] > 1:
            score -= 10
    
    return max(0, min(100, score))


def format_status(value, good_threshold, bad_threshold, unit="", reverse=False):
    """Format a value with color based on thresholds"""
    if value is None:
        return "[dim]N/A[/dim]"
    
    if reverse:
        good = value <= good_threshold
        bad = value >= bad_threshold
    else:
        good = value >= good_threshold
        bad = value <= bad_threshold
    
    if good:
        return f"[green]{value:.1f}{unit}[/green] ✓"
    elif bad:
        return f"[red]{value:.1f}{unit}[/red] ✗"
    else:
        return f"[yellow]{value:.1f}{unit}[/yellow] ~"


def run_check():
    """Run all network checks and display results"""
    console.clear()
    
    # Header
    console.print(Panel.fit(
        "[bold cyan]📹 Zoom Network Checker[/bold cyan]\n"
        "[dim]by DASecure[/dim]\n"
        "[dim]Verifying your connection for smooth video calls[/dim]",
        box=box.ROUNDED
    ))
    console.print()
    
    results = {
        "download": None,
        "upload": None,
        "latency": None,
        "jitter": None,
        "packet_loss": None,
        "server": None
    }
    
    # Run latency test first (fast) - TCP-based since ping may not be available
    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        transient=True,
    ) as progress:
        task = progress.add_task("Testing latency...", total=None)
        # Try multiple hosts for reliability
        for host in ["cloudflare.com", "google.com", "speedtest.net"]:
            results["latency"], results["jitter"], results["packet_loss"] = tcp_latency_test(host)
            if results["latency"] is not None:
                break
    
    # Run speed test
    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        transient=True,
    ) as progress:
        task = progress.add_task("Running speed test (this may take 10-20 seconds)...", total=None)
        results["download"], results["upload"], results["server"] = speed_test()
    
    console.clear()
    
    # Results table
    table = Table(show_header=True, header_style="bold cyan", box=box.ROUNDED)
    table.add_column("Metric", style="white", width=20)
    table.add_column("Value", width=25)
    table.add_column("Zoom Requirement", style="dim", width=20)
    
    # Download
    dl_status = format_status(results["download"], 3.0, 1.0, " Mbps")
    table.add_row("Download Speed", dl_status, "≥ 3 Mbps (HD)")
    
    # Upload
    ul_status = format_status(results["upload"], 3.0, 1.0, " Mbps")
    table.add_row("Upload Speed", ul_status, "≥ 3 Mbps (HD)")
    
    # Latency
    lat_status = format_status(results["latency"], 150, 250, " ms", reverse=True)
    table.add_row("Latency (Ping)", lat_status, "< 150 ms")
    
    # Jitter
    jit_status = format_status(results["jitter"], 30, 50, " ms", reverse=True)
    table.add_row("Jitter", jit_status, "< 30 ms")
    
    # Packet Loss
    loss_status = format_status(results["packet_loss"], 1.0, 3.0, "%", reverse=True)
    table.add_row("Packet Loss", loss_status, "< 1%")
    
    console.print(table)
    console.print()
    
    # Server info
    if results["server"]:
        console.print(f"[dim]Speed test server: {results['server']}[/dim]")
    
    # Quality score
    score = get_quality_score(results)
    if score >= 90:
        score_color = "green"
        score_label = "Excellent"
    elif score >= 70:
        score_color = "yellow"
        score_label = "Good"
    elif score >= 50:
        score_color = "orange"
        score_label = "Fair"
    else:
        score_color = "red"
        score_label = "Poor"
    
    console.print()
    console.print(f"[bold]Quality Score:[/bold] [{score_color}]{score}/100 ({score_label})[/{score_color}]")
    console.print()
    
    # Zoom readiness
    is_ready, issues = check_zoom_readiness(results)
    
    if is_ready:
        console.print(Panel(
            "[bold green]✅ ZOOM READY[/bold green]\n\n"
            "Your connection should handle Zoom calls smoothly.\n"
            "HD video should work fine.",
            box=box.ROUNDED,
            border_style="green"
        ))
    else:
        issues_text = "\n".join([f"• {i}" for i in issues])
        console.print(Panel(
            "[bold red]⚠️  ISSUES DETECTED[/bold red]\n\n"
            f"{issues_text}\n\n"
            "[dim]You may experience lag, frozen video, or audio issues.[/dim]",
            box=box.ROUNDED,
            border_style="red"
        ))
    
    console.print()
    console.print(f"[dim]Tested at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]")


def main():
    """Main entry point"""
    import argparse
    
    parser = argparse.ArgumentParser(description="Check if your network is ready for Zoom calls")
    parser.add_argument("-w", "--watch", action="store_true", help="Continuously monitor (every 5 min)")
    args = parser.parse_args()
    
    if args.watch:
        try:
            while True:
                run_check()
                console.print("\n[dim]Next check in 5 minutes. Press Ctrl+C to stop.[/dim]")
                time.sleep(300)
        except KeyboardInterrupt:
            console.print("\n[yellow]Stopped.[/yellow]")
    else:
        run_check()


if __name__ == "__main__":
    main()
