package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"time"

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

const (
	roomURL = "https://app.kosmi.io/room/@hyperspaceout"
	logFile = "image-upload-capture.log"
)

func main() {
	log.Println("🔍 Starting Kosmi WebSocket Monitor (Image Upload Capture)")
	log.Printf("📡 Room URL: %s", roomURL)
	log.Printf("📝 Log file: %s", logFile)
	log.Println("This will capture ALL WebSocket traffic and HTTP requests...")
	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()

	// Set up interrupt handler
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	
	// 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)
	}

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

	// Launch browser
	browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
		Headless: playwright.Bool(false), // Keep visible so we can see what's happening
	})
	if err != nil {
		log.Fatalf("Failed to launch browser: %v", err)
	}
	defer browser.Close()

	// Create context with permissions
	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"),
		AcceptDownloads: playwright.Bool(true),
		// Grant all permissions to avoid upload blocking
		Permissions: []string{"clipboard-read", "clipboard-write"},
	})
	if err != nil {
		log.Fatalf("Failed to create context: %v", err)
	}
	
	// Grant permissions for the Kosmi domain
	if err := context.GrantPermissions([]string{"clipboard-read", "clipboard-write"}, playwright.BrowserContextGrantPermissionsOptions{
		Origin: playwright.String("https://app.kosmi.io"),
	}); err != nil {
		logBoth("⚠️  Warning: Failed to grant permissions: %v", err)
	}

	// Listen to all network requests
	context.On("request", func(request playwright.Request) {
		logBoth("🌐 [HTTP REQUEST] %s %s", request.Method(), request.URL())
		postData, err := request.PostData()
		if err == nil && postData != "" {
			logBoth("   POST Data: %s", postData)
		}
	})

	context.On("response", func(response playwright.Response) {
		status := response.Status()
		url := response.URL()
		logBoth("📨 [HTTP RESPONSE] %d %s", status, url)
		
		// Try to get response body for file uploads
		if status >= 200 && status < 300 {
			body, err := response.Body()
			if err == nil && len(body) > 0 && len(body) < 10000 {
				// Log if it looks like JSON
				var jsonData interface{}
				if json.Unmarshal(body, &jsonData) == nil {
					prettyJSON, _ := json.MarshalIndent(jsonData, "   ", "  ")
					logBoth("   Response Body: %s", string(prettyJSON))
				}
			}
		}
	})

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

	// Inject WebSocket monitoring script BEFORE navigation
	logBoth("📝 Injecting WebSocket and file upload monitoring script...")
	if err := page.AddInitScript(playwright.Script{
		Content: playwright.String(`
			(function() {
				const OriginalWebSocket = window.WebSocket;
				let messageCount = 0;
				
				window.WebSocket = function(url, protocols) {
					console.log('🔌 [WS MONITOR] WebSocket created:', url, 'protocols:', protocols);
					const socket = new OriginalWebSocket(url, protocols);
					
					socket.addEventListener('open', (event) => {
						console.log('✅ [WS MONITOR] WebSocket OPENED');
					});
					
					socket.addEventListener('close', (event) => {
						console.log('🔴 [WS MONITOR] WebSocket CLOSED:', event.code, event.reason);
					});
					
					socket.addEventListener('error', (event) => {
						console.error('❌ [WS MONITOR] WebSocket ERROR:', event);
					});
					
					// Intercept outgoing messages
					const originalSend = socket.send;
					socket.send = function(data) {
						messageCount++;
						// Check if binary data
						if (data instanceof ArrayBuffer || data instanceof Blob) {
							console.log('📤 [WS MONITOR] SEND #' + messageCount + ': [BINARY DATA]', 
								'Type:', data.constructor.name, 
								'Size:', data.byteLength || data.size);
						} else {
							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));
								}
							} catch (e) {
								// Not JSON
							}
						}
						return originalSend.call(this, data);
					};
					
					// Intercept incoming messages
					socket.addEventListener('message', (event) => {
						messageCount++;
						// Check if binary data
						if (event.data instanceof ArrayBuffer || event.data instanceof Blob) {
							console.log('📥 [WS MONITOR] RECEIVE #' + messageCount + ': [BINARY DATA]',
								'Type:', event.data.constructor.name,
								'Size:', event.data.byteLength || event.data.size);
						} else {
							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;
			})();
		`),
	}); err != nil {
		log.Fatalf("Failed to inject script: %v", err)
	}

	// 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)
	})
	
	// Listen to file chooser events
	page.On("filechooser", func(fileChooser playwright.FileChooser) {
		logBoth("📁 [FILE CHOOSER] File dialog opened!")
		logBoth("   Multiple: %v", fileChooser.IsMultiple())
		
		// Try to set the test image
		absPath, err := filepath.Abs("blurt.jpg")
		if err != nil {
			logBoth("   ❌ Failed to get absolute path: %v", err)
			return
		}
		
		logBoth("   📎 Setting file: %s", absPath)
		if err := fileChooser.SetFiles(absPath, playwright.FileChooserSetFilesOptions{}); err != nil {
			logBoth("   ❌ Failed to set file: %v", err)
		} else {
			logBoth("   ✅ File set successfully!")
		}
	})

	// Navigate to room
	logBoth("🌐 Navigating to %s...", roomURL)
	if _, err := page.Goto(roomURL, playwright.PageGotoOptions{
		WaitUntil: playwright.WaitUntilStateDomcontentloaded,
	}); err != nil {
		log.Fatalf("Failed to navigate: %v", err)
	}

	logBoth("✅ Page loaded! Monitoring WebSocket traffic and HTTP requests...")
	logBoth("📸 Please manually upload the test image (blurt.jpg) in the Kosmi chat")
	logBoth("   Look for the attachment/upload button in the chat interface")
	logBoth("Press Ctrl+C when done to stop monitoring")
	logBoth("")

	// Wait for a bit to see initial traffic
	time.Sleep(5 * time.Second)

	// Try to trigger file upload programmatically (fallback to manual)
	logBoth("\n📎 Attempting to trigger file upload dialog...")
	result, err := page.Evaluate(`
		(async function() {
			// Try to find file input or upload button
			const fileInputs = document.querySelectorAll('input[type="file"]');
			if (fileInputs.length > 0) {
				console.log('Found', fileInputs.length, 'file inputs');
				fileInputs[0].click();
				return { success: true, method: 'file-input' };
			}
			
			// Try to find buttons with upload-related text/icons
			const buttons = document.querySelectorAll('button, [role="button"]');
			for (let btn of buttons) {
				const text = btn.textContent.toLowerCase();
				const ariaLabel = btn.getAttribute('aria-label')?.toLowerCase() || '';
				if (text.includes('upload') || text.includes('attach') || text.includes('file') ||
				    ariaLabel.includes('upload') || ariaLabel.includes('attach') || ariaLabel.includes('file')) {
					console.log('Found upload button:', btn.textContent, ariaLabel);
					btn.click();
					return { success: true, method: 'upload-button' };
				}
			}
			
			return { success: false, error: 'No upload button found' };
		})();
	`)
	
	if err != nil {
		logBoth("❌ Failed to trigger upload: %v", err)
		logBoth("⚠️  FALLBACK: Please manually click the upload/attachment button in Kosmi")
	} else {
		resultMap := result.(map[string]interface{})
		if success, ok := resultMap["success"].(bool); ok && success {
			method := resultMap["method"].(string)
			logBoth("✅ Triggered upload via %s", method)
		} else {
			logBoth("❌ Could not find upload button: %v", resultMap["error"])
			logBoth("⚠️  FALLBACK: Please manually click the upload/attachment button in Kosmi")
		}
	}

	// Keep monitoring
	logBoth("\n⏳ Monitoring all traffic... Upload an image in Kosmi, then press Ctrl+C to stop")
	logBoth("💡 TIP: If upload doesn't work in this browser, just note what you see in a normal browser")
	logBoth("      We mainly need to see the HTTP requests and WebSocket messages")
	
	// Wait for interrupt with proper cleanup
	<-interrupt
	logBoth("\n👋 Stopping monitor...")
	
	// Close browser and context to stop all goroutines
	if page != nil {
		page.Close()
	}
	if context != nil {
		context.Close()
	}
	if browser != nil {
		browser.Close()
	}
	
	// Give goroutines time to finish
	time.Sleep(500 * time.Millisecond)
	
	logBoth("✅ Monitor stopped. Check %s for captured traffic", logFile)
}

