package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"time"

	"github.com/playwright-community/playwright-go"
)

const (
	defaultRoomURL = "https://app.kosmi.io/room/@hyperspaceout"
	logFile        = "auth-monitor.log"
)

func main() {
	roomURL := flag.String("room", defaultRoomURL, "Kosmi room URL")
	loginMode := flag.Bool("login", false, "Monitor login flow (navigate to login page first)")
	testReconnect := flag.Bool("reconnect", false, "Test reconnection behavior (will pause network)")
	flag.Parse()

	log.Println("🔍 Starting Kosmi Auth & Reconnection Monitor")
	log.Printf("📡 Room URL: %s", *roomURL)
	log.Printf("🔐 Login mode: %v", *loginMode)
	log.Printf("🔄 Reconnect test: %v", *testReconnect)
	log.Printf("📝 Log file: %s", logFile)
	log.Println()

	// Create log file
	f, err := os.Create(logFile)
	if err != nil {
		log.Fatalf("Failed to create log file: %v", err)
	}
	defer f.Close()

	// Helper to log to both console and file
	logBoth := func(format string, args ...interface{}) {
		msg := fmt.Sprintf(format, args...)
		log.Println(msg)
		fmt.Fprintf(f, "%s %s\n", time.Now().Format("15:04:05.000"), msg)
	}

	// Set up interrupt handler
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	// Launch Playwright
	pw, err := playwright.Run()
	if err != nil {
		log.Fatalf("Failed to start Playwright: %v", err)
	}

	// Cleanup function
	cleanup := func() {
		logBoth("\n👋 Shutting down...")
		if pw != nil {
			pw.Stop()
		}
		os.Exit(0)
	}

	// Handle interrupt
	go func() {
		<-interrupt
		cleanup()
	}()

	// Launch browser (visible so we can interact for login)
	browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
		Headless: playwright.Bool(false),
	})
	if err != nil {
		log.Fatalf("Failed to launch browser: %v", err)
	}

	// Create context
	context, err := browser.NewContext(playwright.BrowserNewContextOptions{
		UserAgent: playwright.String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"),
	})
	if err != nil {
		log.Fatalf("Failed to create context: %v", err)
	}

	// Monitor all network requests
	context.On("request", func(request playwright.Request) {
		url := request.URL()
		method := request.Method()
		
		// Log all Kosmi-related requests
		if containsKosmi(url) {
			logBoth("🌐 [HTTP REQUEST] %s %s", method, url)
			
			// Log POST data (likely contains login credentials or GraphQL mutations)
			if method == "POST" {
				postData, err := request.PostData()
				if err == nil && postData != "" {
					logBoth("   📤 POST Data: %s", postData)
				}
			}
			
			// Log headers for auth-related requests
			headers := request.Headers()
			if authHeader, ok := headers["authorization"]; ok {
				logBoth("   🔑 Authorization: %s", authHeader)
			}
			if cookie, ok := headers["cookie"]; ok {
				logBoth("   🍪 Cookie: %s", truncate(cookie, 100))
			}
		}
	})

	// Monitor all network responses
	context.On("response", func(response playwright.Response) {
		url := response.URL()
		status := response.Status()
		
		if containsKosmi(url) {
			logBoth("📨 [HTTP RESPONSE] %d %s", status, url)
			
			// Try to get response body for auth endpoints
			if status >= 200 && status < 300 {
				body, err := response.Body()
				if err == nil && len(body) > 0 && len(body) < 50000 {
					// Try to parse as JSON
					var jsonData interface{}
					if json.Unmarshal(body, &jsonData) == nil {
						prettyJSON, _ := json.MarshalIndent(jsonData, "   ", "  ")
						logBoth("   📦 Response Body: %s", string(prettyJSON))
					}
				}
			}
			
			// Log Set-Cookie headers
			headers := response.Headers()
			if setCookie, ok := headers["set-cookie"]; ok {
				logBoth("   🍪 Set-Cookie: %s", setCookie)
			}
		}
	})

	// Create page
	page, err := context.NewPage()
	if err != nil {
		log.Fatalf("Failed to create page: %v", err)
	}

	// Inject comprehensive monitoring script BEFORE navigation
	logBoth("📝 Injecting WebSocket, storage, and reconnection monitoring script...")
	monitorScript := getMonitoringScript()
	logBoth("   Script length: %d characters", len(monitorScript))
	if err := page.AddInitScript(playwright.Script{
		Content: playwright.String(monitorScript),
	}); err != nil {
		log.Fatalf("Failed to inject script: %v", err)
	}
	logBoth("   ✅ Script injected successfully")

	// Listen to console messages
	page.On("console", func(msg playwright.ConsoleMessage) {
		text := msg.Text()
		msgType := msg.Type()

		// Format the output nicely
		prefix := "💬"
		switch msgType {
		case "log":
			prefix = "📋"
		case "error":
			prefix = "❌"
		case "warning":
			prefix = "⚠️"
		case "info":
			prefix = "ℹ️"
		}

		logBoth("%s [BROWSER %s] %s", prefix, msgType, text)
	})

	// Navigate based on mode
	if *loginMode {
		logBoth("🔐 Login mode: Navigate to login page first")
		logBoth("   Please log in manually in the browser")
		logBoth("   We'll capture all auth traffic")
		
		// Navigate to main Kosmi page (will redirect to login if not authenticated)
		logBoth("🌐 Navigating to https://app.kosmi.io...")
		timeout := 30000.0 // 30 seconds
		_, err := page.Goto("https://app.kosmi.io", playwright.PageGotoOptions{
			WaitUntil: playwright.WaitUntilStateDomcontentloaded,
			Timeout:   &timeout,
		})
		if err != nil {
			logBoth("⚠️  Navigation error: %v", err)
			logBoth("   Continuing anyway - page might still be usable")
		} else {
			logBoth("✅ Page loaded successfully")
		}
		
		logBoth("")
		logBoth("📋 Instructions:")
		logBoth("   1. Log in with your credentials in the browser")
		logBoth("   2. Navigate to the room: %s", *roomURL)
		logBoth("   3. Browser console messages should appear here")
		logBoth("   4. Press Ctrl+C when done")
		logBoth("")
		logBoth("💡 Expected console messages:")
		logBoth("   - '[Monitor] Installing...'")
		logBoth("   - '[WS MONITOR] WebSocket created...'")
		logBoth("   - '[FETCH] Request to...'")
		logBoth("")
		
	} else {
		// Navigate directly to room (anonymous flow)
		logBoth("🌐 Navigating to %s...", *roomURL)
		timeout := 30000.0 // 30 seconds
		_, err := page.Goto(*roomURL, playwright.PageGotoOptions{
			WaitUntil: playwright.WaitUntilStateDomcontentloaded,
			Timeout:   &timeout,
		})
		if err != nil {
			logBoth("⚠️  Navigation error: %v", err)
			logBoth("   Continuing anyway - page might still be usable")
		} else {
			logBoth("✅ Page loaded successfully")
		}
	}

	// Wait for WebSocket to connect
	time.Sleep(5 * time.Second)

	// Capture storage data
	logBoth("\n📦 Capturing storage data...")
	captureStorage(page, logBoth)

	if *testReconnect {
		logBoth("\n🔄 Testing reconnection behavior...")
		logBoth("   This will simulate network disconnection")
		logBoth("   Watch how the browser handles reconnection")
		
		// Simulate network offline
		logBoth("   📡 Setting network to OFFLINE...")
		if err := context.SetOffline(true); err != nil {
			logBoth("   ❌ Failed to set offline: %v", err)
		} else {
			logBoth("   ✅ Network is now OFFLINE")
			logBoth("   ⏳ Waiting 10 seconds...")
			time.Sleep(10 * time.Second)
			
			// Restore network
			logBoth("   📡 Setting network to ONLINE...")
			if err := context.SetOffline(false); err != nil {
				logBoth("   ❌ Failed to set online: %v", err)
			} else {
				logBoth("   ✅ Network is now ONLINE")
				logBoth("   ⏳ Observing reconnection behavior...")
				time.Sleep(10 * time.Second)
			}
		}
	}

	// Capture final storage state
	logBoth("\n📦 Capturing final storage state...")
	captureStorage(page, logBoth)

	// Keep monitoring
	logBoth("\n⏳ Monitoring all traffic... Press Ctrl+C to stop")
	if *loginMode {
		logBoth("💡 TIP: Try logging in, navigating to rooms, and logging out")
		logBoth("      We'll capture all authentication flows")
	}

	// Wait forever (interrupt handler will exit)
	select {}
}

// captureStorage captures localStorage, sessionStorage, and cookies
func captureStorage(page playwright.Page, logBoth func(string, ...interface{})) {
	// Capture localStorage
	localStorageResult, err := page.Evaluate(`
		(function() {
			const data = {};
			for (let i = 0; i < localStorage.length; i++) {
				const key = localStorage.key(i);
				data[key] = localStorage.getItem(key);
			}
			return data;
		})();
	`)
	if err == nil {
		if localStorage, ok := localStorageResult.(map[string]interface{}); ok {
			logBoth("   📦 localStorage:")
			for key, value := range localStorage {
				logBoth("      %s: %s", key, truncate(fmt.Sprintf("%v", value), 100))
			}
		}
	}

	// Capture sessionStorage
	sessionStorageResult, err := page.Evaluate(`
		(function() {
			const data = {};
			for (let i = 0; i < sessionStorage.length; i++) {
				const key = sessionStorage.key(i);
				data[key] = sessionStorage.getItem(key);
			}
			return data;
		})();
	`)
	if err == nil {
		if sessionStorage, ok := sessionStorageResult.(map[string]interface{}); ok {
			logBoth("   📦 sessionStorage:")
			for key, value := range sessionStorage {
				logBoth("      %s: %s", key, truncate(fmt.Sprintf("%v", value), 100))
			}
		}
	}

	// Capture cookies
	cookiesResult, err := page.Evaluate(`
		(function() {
			return document.cookie.split(';').map(c => {
				const parts = c.trim().split('=');
				return {
					name: parts[0],
					value: parts.slice(1).join('=')
				};
			}).filter(c => c.name && c.value);
		})();
	`)
	if err == nil {
		if cookiesArray, ok := cookiesResult.([]interface{}); ok {
			logBoth("   🍪 Cookies:")
			for _, cookieItem := range cookiesArray {
				if cookie, ok := cookieItem.(map[string]interface{}); ok {
					name := cookie["name"]
					value := cookie["value"]
					logBoth("      %s: %s", name, truncate(fmt.Sprintf("%v", value), 100))
				}
			}
		}
	}

	// Capture WebSocket status
	wsStatusResult, err := page.Evaluate(`
		(function() {
			return window.__KOSMI_WS_STATUS__ || { error: "No WebSocket found" };
		})();
	`)
	if err == nil {
		if wsStatus, ok := wsStatusResult.(map[string]interface{}); ok {
			logBoth("   🔌 WebSocket Status:")
			for key, value := range wsStatus {
				logBoth("      %s: %v", key, value)
			}
		}
	}
}

// getMonitoringScript returns the comprehensive monitoring script
func getMonitoringScript() string {
	return `
		(function() {
			if (window.__KOSMI_MONITOR_INSTALLED__) return;
			
			console.log('[Monitor] Installing comprehensive monitoring hooks...');
			
			// Store original WebSocket constructor
			const OriginalWebSocket = window.WebSocket;
			let wsInstance = null;
			let messageCount = 0;
			let reconnectAttempts = 0;
			
			// Hook WebSocket constructor
			window.WebSocket = function(url, protocols) {
				console.log('🔌 [WS MONITOR] WebSocket created:', url, 'protocols:', protocols);
				const socket = new OriginalWebSocket(url, protocols);
				
				if (url.includes('engine.kosmi.io') || url.includes('gql-ws')) {
					wsInstance = socket;
					
					// Track connection lifecycle
					socket.addEventListener('open', (event) => {
						console.log('✅ [WS MONITOR] WebSocket OPENED');
						reconnectAttempts = 0;
						updateWSStatus('OPEN', url);
					});
					
					socket.addEventListener('close', (event) => {
						console.log('🔴 [WS MONITOR] WebSocket CLOSED:', event.code, event.reason, 'wasClean:', event.wasClean);
						reconnectAttempts++;
						updateWSStatus('CLOSED', url, { code: event.code, reason: event.reason, reconnectAttempts });
					});
					
					socket.addEventListener('error', (event) => {
						console.error('❌ [WS MONITOR] WebSocket ERROR:', event);
						updateWSStatus('ERROR', url);
					});
					
					// Intercept outgoing messages
					const originalSend = socket.send;
					socket.send = function(data) {
						messageCount++;
						console.log('📤 [WS MONITOR] SEND #' + messageCount + ':', data);
						try {
							const parsed = JSON.parse(data);
							console.log('   Type:', parsed.type, 'ID:', parsed.id);
							if (parsed.payload) {
								console.log('   Payload:', JSON.stringify(parsed.payload, null, 2));
								
								// Check for auth-related messages
								if (parsed.type === 'connection_init' && parsed.payload.token) {
									console.log('   🔑 [AUTH] Connection init with token:', parsed.payload.token.substring(0, 50) + '...');
								}
							}
						} catch (e) {
							// Not JSON
						}
						return originalSend.call(this, data);
					};
					
					// Intercept incoming messages
					socket.addEventListener('message', (event) => {
						messageCount++;
						console.log('📥 [WS MONITOR] RECEIVE #' + messageCount + ':', event.data);
						try {
							const parsed = JSON.parse(event.data);
							console.log('   Type:', parsed.type, 'ID:', parsed.id);
							if (parsed.payload) {
								console.log('   Payload:', JSON.stringify(parsed.payload, null, 2));
							}
						} catch (e) {
							// Not JSON
						}
					});
				}
				
				return socket;
			};
			
			// Preserve WebSocket properties
			window.WebSocket.prototype = OriginalWebSocket.prototype;
			window.WebSocket.CONNECTING = OriginalWebSocket.CONNECTING;
			window.WebSocket.OPEN = OriginalWebSocket.OPEN;
			window.WebSocket.CLOSING = OriginalWebSocket.CLOSING;
			window.WebSocket.CLOSED = OriginalWebSocket.CLOSED;
			
			// Monitor localStorage changes
			const originalSetItem = Storage.prototype.setItem;
			Storage.prototype.setItem = function(key, value) {
				console.log('💾 [STORAGE] localStorage.setItem:', key, '=', value.substring(0, 100));
				if (key.toLowerCase().includes('token') || key.toLowerCase().includes('auth')) {
					console.log('   🔑 [AUTH] Auth-related storage detected!');
				}
				return originalSetItem.call(this, key, value);
			};
			
			// Monitor sessionStorage changes
			const originalSessionSetItem = sessionStorage.setItem;
			sessionStorage.setItem = function(key, value) {
				console.log('💾 [STORAGE] sessionStorage.setItem:', key, '=', value.substring(0, 100));
				if (key.toLowerCase().includes('token') || key.toLowerCase().includes('auth')) {
					console.log('   🔑 [AUTH] Auth-related storage detected!');
				}
				return originalSessionSetItem.call(this, key, value);
			};
			
			// Monitor fetch requests (for GraphQL mutations)
			const originalFetch = window.fetch;
			window.fetch = function(url, options) {
				if (typeof url === 'string' && url.includes('kosmi.io')) {
					console.log('🌐 [FETCH] Request to:', url);
					if (options && options.body) {
						console.log('   📤 Body:', options.body);
						try {
							const parsed = JSON.parse(options.body);
							if (parsed.query) {
								console.log('   📝 GraphQL Query:', parsed.query.substring(0, 200));
								if (parsed.query.includes('login') || parsed.query.includes('auth')) {
									console.log('   🔑 [AUTH] Auth-related GraphQL detected!');
								}
							}
						} catch (e) {
							// Not JSON
						}
					}
				}
				return originalFetch.apply(this, arguments).then(response => {
					if (typeof url === 'string' && url.includes('kosmi.io')) {
						console.log('📨 [FETCH] Response from:', url, 'status:', response.status);
					}
					return response;
				});
			};
			
			// Helper to update WebSocket status
			function updateWSStatus(state, url, extra = {}) {
				window.__KOSMI_WS_STATUS__ = {
					state,
					url,
					timestamp: new Date().toISOString(),
					messageCount,
					...extra
				};
			}
			
			// Monitor network connectivity
			window.addEventListener('online', () => {
				console.log('🌐 [NETWORK] Browser is ONLINE');
			});
			
			window.addEventListener('offline', () => {
				console.log('🌐 [NETWORK] Browser is OFFLINE');
			});
			
			window.__KOSMI_MONITOR_INSTALLED__ = true;
			console.log('[Monitor] ✅ All monitoring hooks installed');
		})();
	`
}

// Helper functions
func containsKosmi(url string) bool {
	return contains(url, "kosmi.io") || contains(url, "engine.kosmi.io") || contains(url, "app.kosmi.io")
}

func contains(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}

func truncate(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen] + "..."
}

