package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/chromedp/chromedp"
)

// Standalone script to extract JWT token from Kosmi using browser automation
// Usage: go run get-token.go --email "email@email.com" --password "password"

var (
	emailFlag    = flag.String("email", "", "Email for Kosmi login")
	passwordFlag = flag.String("password", "", "Password for Kosmi login")
	headlessFlag = flag.Bool("headless", true, "Run browser in headless mode")
	verboseFlag  = flag.Bool("verbose", false, "Enable verbose logging")
)

func main() {
	flag.Parse()

	if *emailFlag == "" || *passwordFlag == "" {
		log.Fatal("Error: --email and --password are required")
	}

	// Set up logging
	if !*verboseFlag {
		log.SetOutput(os.Stderr)
	}

	log.Println("Starting browser automation to extract JWT token...")
	log.Printf("Email: %s", *emailFlag)
	log.Printf("Headless mode: %v", *headlessFlag)

	token, err := extractToken(*emailFlag, *passwordFlag, *headlessFlag)
	if err != nil {
		log.Fatalf("Failed to extract token: %v", err)
	}

	// Output token to stdout (so it can be captured)
	fmt.Println(token)
	
	if *verboseFlag {
		log.Printf("✓ Successfully extracted token (length: %d)", len(token))
	}
}

func extractToken(email, password string, headless bool) (string, error) {
	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
	defer cancel()

	// Set up chromedp options
	opts := []chromedp.ExecAllocatorOption{
		chromedp.NoFirstRun,
		chromedp.NoDefaultBrowserCheck,
		chromedp.DisableGPU,
		chromedp.NoSandbox,
	}

	if headless {
		opts = append(opts, chromedp.Headless)
	}

	// Create allocator context
	allocCtx, cancel := chromedp.NewExecAllocator(ctx, opts...)
	defer cancel()

	// Create browser context - suppress cookie errors unless verbose
	logFunc := func(string, ...interface{}) {}
	if *verboseFlag {
		logFunc = log.Printf
	}
	ctx, cancel = chromedp.NewContext(allocCtx, chromedp.WithLogf(logFunc))
	defer cancel()

	var token string

	// Run the automation tasks
	err := chromedp.Run(ctx,
		// Step 1: Navigate to Kosmi
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Navigating to https://app.kosmi.io")
			return chromedp.Navigate("https://app.kosmi.io").Do(ctx)
		}),
		
		// Step 2: Wait for page to load
		chromedp.WaitReady("body"),
		chromedp.Sleep(3*time.Second),
		
		// Step 3: Find and click Login button
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Looking for Login button...")
			
			// Log all buttons we can see
			if *verboseFlag {
				var buttonTexts []string
				chromedp.Evaluate(`
					Array.from(document.querySelectorAll('button')).map(el => el.textContent.trim())
				`, &buttonTexts).Do(ctx)
				log.Printf("  Found buttons: %v", buttonTexts)
			}
			
			var found bool
			if err := chromedp.Evaluate(`
				(() => {
					const buttons = Array.from(document.querySelectorAll('button'));
					const btn = buttons.find(el => {
						const text = el.textContent.trim();
						return text === 'Login' || text === 'Log in';
					});
					if (btn) {
						btn.click();
						return true;
					}
					return false;
				})()
			`, &found).Do(ctx); err != nil {
				return err
			}
			
			if !found {
				return fmt.Errorf("Login button not found")
			}
			
			log.Println("✓ Clicked Login button")
			return nil
		}),
		
		// Step 4: Wait for login modal
		chromedp.Sleep(3*time.Second),
		
		// Step 5: Click "Login with Email" button
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Looking for 'Login with Email' button...")
			
			if *verboseFlag {
				var buttonTexts []string
				chromedp.Evaluate(`
					Array.from(document.querySelectorAll('button')).map(el => el.textContent.trim())
				`, &buttonTexts).Do(ctx)
				log.Printf("  Found buttons: %v", buttonTexts)
			}
			
			var found bool
			if err := chromedp.Evaluate(`
				(() => {
					const btn = Array.from(document.querySelectorAll('button')).find(el => 
						el.textContent.includes('Email')
					);
					if (btn) {
						btn.click();
						return true;
					}
					return false;
				})()
			`, &found).Do(ctx); err != nil {
				return err
			}
			
			if !found {
				return fmt.Errorf("'Login with Email' button not found")
			}
			
			log.Println("✓ Clicked 'Login with Email' button")
			return nil
		}),
		
		// Step 6: Wait for email form
		chromedp.Sleep(3*time.Second),
		chromedp.WaitVisible(`input[type="password"]`, chromedp.ByQuery),
		
		// Step 7: Fill in email
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Filling email field...")
			return nil
		}),
		chromedp.Click(`input[placeholder*="Email"], input[placeholder*="Username"]`, chromedp.ByQuery),
		chromedp.Sleep(200*time.Millisecond),
		chromedp.SendKeys(`input[placeholder*="Email"], input[placeholder*="Username"]`, email, chromedp.ByQuery),
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("✓ Email entered")
			return nil
		}),
		
		chromedp.Sleep(500*time.Millisecond),
		
		// Step 8: Fill in password
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Filling password field...")
			return nil
		}),
		chromedp.Click(`input[type="password"]`, chromedp.ByQuery),
		chromedp.Sleep(200*time.Millisecond),
		chromedp.SendKeys(`input[type="password"]`, password+"\n", chromedp.ByQuery),
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("✓ Password entered and form submitted")
			return nil
		}),
		
		chromedp.Sleep(500*time.Millisecond),
		
		// Step 10: Wait for login to complete - check for modal to close
		chromedp.ActionFunc(func(ctx context.Context) error {
			log.Println("→ Waiting for login to complete...")
			
			// Wait for the login modal to disappear (indicates successful login)
			maxAttempts := 30 // 15 seconds total
			for i := 0; i < maxAttempts; i++ {
				time.Sleep(500 * time.Millisecond)
				
				// Check if login modal is gone (successful login)
				var modalGone bool
				chromedp.Evaluate(`
					(() => {
						// Check if the email/password form is still visible
						const emailInput = document.querySelector('input[placeholder*="Email"], input[placeholder*="Username"]');
						const passwordInput = document.querySelector('input[type="password"]');
						return !emailInput && !passwordInput;
					})()
				`, &modalGone).Do(ctx)
				
				if modalGone {
					log.Println("✓ Login modal closed - login successful")
					// Modal is gone, wait a bit more for token to be set
					time.Sleep(2 * time.Second)
					chromedp.Evaluate(`localStorage.getItem('token')`, &token).Do(ctx)
					if token != "" {
						log.Println("✓ Authenticated token received")
						return nil
					}
				}
				
				// Check for error messages
				var errorText string
				chromedp.Evaluate(`
					(() => {
						const errorEl = document.querySelector('[role="alert"], .error, .error-message');
						return errorEl ? errorEl.textContent.trim() : '';
					})()
				`, &errorText).Do(ctx)
				
				if errorText != "" && errorText != "null" {
					return fmt.Errorf("login failed: %s", errorText)
				}
			}
			
			// Timeout - get whatever token is there
			log.Println("⚠ Login timeout - extracting current token")
			chromedp.Evaluate(`localStorage.getItem('token')`, &token).Do(ctx)
			
			if token == "" {
				return fmt.Errorf("login timeout: no token found")
			}
			
			log.Println("✓ Token extracted (may be anonymous)")
			return nil
		}),
	)

	if err != nil {
		return "", fmt.Errorf("browser automation failed: %w", err)
	}

	if token == "" {
		return "", fmt.Errorf("failed to extract token from localStorage")
	}

	return token, nil
}

