#!/usr/bin/env python3
"""
Comprehensive ngrok diagnostics
Identifies ERR_NGROK_3200 and other connection issues
"""

import requests
import json
import subprocess
import sys
import time
from datetime import datetime

def log(msg, level="INFO"):
    """Print formatted log message"""
    timestamp = datetime.now().strftime("%H:%M:%S")
    print(f"[{timestamp}] [{level}] {msg}")

def check_ngrok_running():
    """Check if ngrok process is running"""
    log("Checking if ngrok is running...")
    try:
        result = subprocess.run(['tasklist', '/FI', 'IMAGENAME eq ngrok.exe'], 
                              capture_output=True, text=True)
        if 'ngrok.exe' in result.stdout:
            log("✓ ngrok process is running", "OK")
            return True
        else:
            log("✗ ngrok process NOT running", "ERROR")
            return False
    except Exception as e:
        log(f"Failed to check ngrok process: {e}", "ERROR")
        return False

def check_flask_localhost():
    """Check if Flask is running on localhost:5000"""
    log("Checking Flask on http://localhost:5000...")
    try:
        response = requests.get('http://localhost:5000', timeout=5)
        if response.status_code == 200:
            log(f"✓ Flask is running (HTTP {response.status_code})", "OK")
            return True
        else:
            log(f"✗ Flask returned {response.status_code}", "WARNING")
            return True  # Still running, just got unexpected code
    except requests.exceptions.ConnectionError:
        log("✗ Cannot connect to Flask on localhost:5000", "ERROR")
        return False
    except Exception as e:
        log(f"✗ Error checking Flask: {e}", "ERROR")
        return False

def check_ngrok_api():
    """Check if ngrok API is accessible"""
    log("Checking ngrok API at http://127.0.0.1:4040...")
    try:
        response = requests.get('http://127.0.0.1:4040/api/tunnels', timeout=5)
        if response.status_code == 200:
            log("✓ ngrok API is accessible", "OK")
            return response.json()
        else:
            log(f"✗ ngrok API returned {response.status_code}", "ERROR")
            return None
    except requests.exceptions.ConnectionError:
        log("✗ Cannot connect to ngrok API", "ERROR")
        log("   Make sure ngrok is running: ngrok http 5000", "INFO")
        return None
    except Exception as e:
        log(f"✗ Error accessing ngrok API: {e}", "ERROR")
        return None

def check_ngrok_tunnels():
    """Check ngrok tunnel status"""
    log("Fetching ngrok tunnel information...")
    try:
        tunnels_data = check_ngrok_api()
        if not tunnels_data:
            return None
        
        tunnels = tunnels_data.get('tunnels', [])
        if not tunnels:
            log("✗ No tunnels found in ngrok", "ERROR")
            return None
        
        log(f"✓ Found {len(tunnels)} tunnel(s)", "OK")
        
        for tunnel in tunnels:
            name = tunnel.get('name', 'unknown')
            proto = tunnel.get('proto', 'unknown')
            public_url = tunnel.get('public_url', 'unknown')
            config = tunnel.get('config', {})
            
            if isinstance(config, str):
                try:
                    config = eval(config)  # Parse config string
                except:
                    pass
            
            addr = config.get('addr', 'unknown') if isinstance(config, dict) else 'unknown'
            
            log(f"  Tunnel: {name}", "INFO")
            log(f"    Protocol: {proto}", "INFO")
            log(f"    Public URL: {public_url}", "INFO")
            log(f"    Forwarding to: {addr}", "INFO")
        
        return tunnels_data
    except Exception as e:
        log(f"✗ Error checking tunnels: {e}", "ERROR")
        return None

def test_ngrok_url(public_url):
    """Test if ngrok URL is accessible"""
    log(f"Testing ngrok public URL: {public_url}...")
    try:
        response = requests.get(public_url, timeout=10, verify=False)
        if response.status_code == 200:
            log(f"✓ ngrok URL is accessible (HTTP {response.status_code})", "OK")
            return True
        else:
            log(f"✗ ngrok URL returned {response.status_code}", "WARNING")
            return False
    except requests.exceptions.ConnectTimeout:
        log("✗ Connection timeout to ngrok URL - ERR_NGROK_3200 (Connection timeout)", "ERROR")
        return False
    except requests.exceptions.ConnectionError as e:
        log(f"✗ Connection error to ngrok URL: {e}", "ERROR")
        if "3200" in str(e):
            log("  This could be ERR_NGROK_3200 - check network connectivity", "WARNING")
        return False
    except Exception as e:
        log(f"✗ Error testing ngrok URL: {e}", "ERROR")
        return False

def check_port_5000():
    """Check if port 5000 is in use"""
    log("Checking port 5000 usage...")
    try:
        result = subprocess.run(['netstat', '-ano', '|', 'findstr', '5000'], 
                              shell=True, capture_output=True, text=True)
        if result.stdout:
            log("✓ Port 5000 is in use", "OK")
            log(f"  Details: {result.stdout[:100]}", "INFO")
            return True
        else:
            log("✗ Port 5000 is not in use", "WARNING")
            return False
    except Exception as e:
        log(f"Could not check port 5000: {e}", "WARNING")
        return False

def check_credentials_json():
    """Check if credentials.json exists and is valid"""
    log("Checking credentials.json...")
    try:
        with open('credentials.json', 'r') as f:
            creds = json.load(f)
        
        redirect_uris = creds.get('web', {}).get('redirect_uris', [])
        if redirect_uris:
            log(f"✓ credentials.json found with {len(redirect_uris)} redirect URI(s)", "OK")
            for uri in redirect_uris:
                log(f"  - {uri}", "INFO")
            return creds
        else:
            log("✗ No redirect URIs in credentials.json", "ERROR")
            return None
    except FileNotFoundError:
        log("✗ credentials.json not found", "ERROR")
        return None
    except json.JSONDecodeError:
        log("✗ credentials.json is not valid JSON", "ERROR")
        return None
    except Exception as e:
        log(f"✗ Error reading credentials.json: {e}", "ERROR")
        return None

def diagnose_error_3200():
    """Diagnose ERR_NGROK_3200 specifically"""
    log("\n" + "="*70, "INFO")
    log("DIAGNOSING ERR_NGROK_3200", "INFO")
    log("="*70, "INFO")
    
    log("\nERR_NGROK_3200 typically indicates:", "INFO")
    log("  1. Connection timeout or network connectivity issue", "INFO")
    log("  2. DNS resolution failure for ngrok domain", "INFO")
    log("  3. Firewall or network blocking the connection", "INFO")
    log("  4. ngrok free tier rate limiting or account issues", "INFO")
    
    return

def main():
    print("\n" + "="*70)
    print("NGROK DIAGNOSTICS - Comprehensive System Check")
    print("="*70 + "\n")
    
    # Run all checks
    flask_ok = check_flask_localhost()
    ngrok_running = check_ngrok_running()
    api_ok = check_ngrok_api()
    tunnels = check_ngrok_tunnels()
    port_ok = check_port_5000()
    creds = check_credentials_json()
    
    # Test ngrok URL if we have tunnels
    if tunnels and tunnels.get('tunnels'):
        print()
        tunnel = tunnels['tunnels'][0]
        public_url = tunnel.get('public_url')
        if public_url:
            test_ngrok_url(public_url)
    
    # Diagnostic recommendations
    print("\n" + "="*70)
    print("DIAGNOSTIC SUMMARY", "INFO")
    print("="*70)
    
    status = {
        "Flask (localhost:5000)": "✓" if flask_ok else "✗",
        "ngrok Process": "✓" if ngrok_running else "✗",
        "ngrok API": "✓" if api_ok else "✗",
        "Port 5000": "✓" if port_ok else "✗",
        "Credentials": "✓" if creds else "✗",
    }
    
    for check, result in status.items():
        print(f"{result} {check}")
    
    print("\nRECOMMENDATIONS:")
    if not ngrok_running:
        print("1. Start ngrok: ngrok http 5000")
    if not flask_ok and ngrok_running:
        print("1. Restart Flask/Python app")
    if creds and not any(creds.get('web', {}).get('redirect_uris', [])):
        print("1. Run: python update_ngrok_uri.py")
    if ngrok_running and api_ok and not test_ngrok_url(tunnels['tunnels'][0]['public_url'] if tunnels and tunnels.get('tunnels') else ""):
        print("1. Check firewall/network settings")
        print("2. Check ngrok account status (may be rate limited)")
        print("3. Try: ngrok http --host-header=rewrite 5000")
    
    print("\n" + "="*70 + "\n")

if __name__ == '__main__':
    main()
