package bkosmi

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"

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

// PlaywrightClient manages a Playwright browser instance to connect to Kosmi
type PlaywrightClient struct {
	roomURL         string
	log             *logrus.Entry
	pw              *playwright.Playwright
	browser         playwright.Browser
	page            playwright.Page
	messageCallback func(*NewMessagePayload)
	connected       bool
	mu              sync.RWMutex
}

// NewPlaywrightClient creates a new Playwright-based Kosmi client
func NewPlaywrightClient(roomURL string, log *logrus.Entry) *PlaywrightClient {
	return &PlaywrightClient{
		roomURL: roomURL,
		log:     log,
	}
}

// Connect launches Playwright and navigates to the Kosmi room
func (c *PlaywrightClient) Connect() error {
	c.log.Info("Launching Playwright browser for Kosmi connection")

	// Create Playwright instance (using system Chromium, no install needed)
	pw, err := playwright.Run()
	if err != nil {
		return fmt.Errorf("failed to start Playwright: %w", err)
	}
	c.pw = pw

	// Launch browser using system Chromium
	browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
		Headless:       playwright.Bool(true),
		ExecutablePath: playwright.String("/usr/bin/chromium"),
		Args: []string{
			"--no-sandbox",
			"--disable-dev-shm-usage",
			"--disable-blink-features=AutomationControlled",
		},
	})
	if err != nil {
		return fmt.Errorf("failed to launch browser: %w", err)
	}
	c.browser = browser

	// Create context and page
	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 {
		return fmt.Errorf("failed to create context: %w", err)
	}

	page, err := context.NewPage()
	if err != nil {
		return fmt.Errorf("failed to create page: %w", err)
	}
	c.page = page

	// Inject WebSocket hook before navigation
	c.log.Info("Injecting WebSocket interceptor...")
	if err := c.injectWebSocketHook(); err != nil {
		return fmt.Errorf("failed to inject WebSocket hook: %w", err)
	}

	// Navigate to the room
	c.log.Infof("Navigating to Kosmi room: %s", c.roomURL)
	if _, err := page.Goto(c.roomURL, playwright.PageGotoOptions{
		WaitUntil: playwright.WaitUntilStateNetworkidle,
	}); err != nil {
		return fmt.Errorf("failed to navigate to room: %w", err)
	}

	// Wait for page to be ready
	if err := page.WaitForLoadState(playwright.PageWaitForLoadStateOptions{
		State: playwright.LoadStateNetworkidle,
	}); err != nil {
		c.log.Warnf("Page load state warning: %v", err)
	}

	c.log.Info("Page loaded, waiting for WebSocket connection...")
	time.Sleep(3 * time.Second)

	c.mu.Lock()
	c.connected = true
	c.mu.Unlock()

	c.log.Info("Successfully connected to Kosmi via Playwright")

	// Start message listener
	go c.listenForMessages()

	return nil
}

// injectWebSocketHook injects the WebSocket interception script
func (c *PlaywrightClient) injectWebSocketHook() error {
	script := c.getWebSocketHookScript()
	
	return c.page.AddInitScript(playwright.Script{
		Content: playwright.String(script),
	})
}

// getWebSocketHookScript returns the JavaScript to hook WebSocket
func (c *PlaywrightClient) getWebSocketHookScript() string {
	return `
		(function() {
			if (window.__KOSMI_WS_HOOK_INSTALLED__) {
				return;
			}
			
			const OriginalWebSocket = window.WebSocket;
			window.__KOSMI_MESSAGE_QUEUE__ = [];
			
			window.WebSocket = function(url, protocols) {
				const socket = new OriginalWebSocket(url, protocols);
				
				if (url.includes('engine.kosmi.io') || url.includes('gql-ws')) {
					console.log('[Kosmi Bridge] WebSocket hook active for:', url);
					window.__KOSMI_WS_CONNECTED__ = true;
					
					const originalAddEventListener = socket.addEventListener.bind(socket);
					socket.addEventListener = function(type, listener, options) {
						if (type === 'message') {
							const wrappedListener = function(event) {
								try {
									const data = JSON.parse(event.data);
									window.__KOSMI_MESSAGE_QUEUE__.push({
										timestamp: Date.now(),
										data: data
									});
								} catch (e) {}
								return listener.call(this, event);
							};
							return originalAddEventListener(type, wrappedListener, options);
						}
						return originalAddEventListener(type, listener, options);
					};
					
					let realOnMessage = null;
					Object.defineProperty(socket, 'onmessage', {
						get: function() { return realOnMessage; },
						set: function(handler) {
							realOnMessage = function(event) {
								try {
									const data = JSON.parse(event.data);
									window.__KOSMI_MESSAGE_QUEUE__.push({
										timestamp: Date.now(),
										data: data
									});
								} catch (e) {}
								if (handler) { handler.call(socket, event); }
							};
						},
						configurable: true
					});
				}
				
				return socket;
			};
			
			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;
			
			window.__KOSMI_WS_HOOK_INSTALLED__ = true;
			console.log('[Kosmi Bridge] WebSocket hook installed');
		})();
	`
}

// listenForMessages polls for new messages from the WebSocket queue
func (c *PlaywrightClient) listenForMessages() {
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	for {
		c.mu.RLock()
		if !c.connected {
			c.mu.RUnlock()
			return
		}
		c.mu.RUnlock()

		// Poll for messages
		result, err := c.page.Evaluate(`
			(function() {
				if (!window.__KOSMI_MESSAGE_QUEUE__) return [];
				const messages = window.__KOSMI_MESSAGE_QUEUE__.slice();
				window.__KOSMI_MESSAGE_QUEUE__ = [];
				return messages;
			})();
		`)
		
		if err != nil {
			c.log.Debugf("Error polling messages: %v", err)
			<-ticker.C
			continue
		}

		// Parse messages
		var messages []struct {
			Timestamp int64                  `json:"timestamp"`
			Data      map[string]interface{} `json:"data"`
		}
		
		if err := json.Unmarshal([]byte(fmt.Sprintf("%v", result)), &messages); err != nil {
			<-ticker.C
			continue
		}

		if len(messages) > 0 {
			c.log.Infof("Processing %d messages from queue", len(messages))
		}

		for _, msg := range messages {
			c.processMessage(msg.Data)
		}

		<-ticker.C
	}
}

// processMessage processes a single WebSocket message
func (c *PlaywrightClient) processMessage(data map[string]interface{}) {
	// Check if this is a newMessage subscription event
	msgType, ok := data["type"].(string)
	if !ok || msgType != "next" {
		return
	}

	payload, ok := data["payload"].(map[string]interface{})
	if !ok {
		return
	}

	dataField, ok := payload["data"].(map[string]interface{})
	if !ok {
		return
	}

	newMessage, ok := dataField["newMessage"].(map[string]interface{})
	if !ok {
		return
	}

	// Parse into our struct
	jsonBytes, err := json.Marshal(map[string]interface{}{
		"data": map[string]interface{}{
			"newMessage": newMessage,
		},
	})
	if err != nil {
		return
	}

	var msgPayload NewMessagePayload
	if err := json.Unmarshal(jsonBytes, &msgPayload); err != nil {
		return
	}

	// Call the callback
	if c.messageCallback != nil {
		c.messageCallback(&msgPayload)
	}
}

// SendMessage sends a message to the Kosmi chat
func (c *PlaywrightClient) SendMessage(text string) error {
	c.mu.RLock()
	if !c.connected {
		c.mu.RUnlock()
		return fmt.Errorf("not connected")
	}
	c.mu.RUnlock()

	selector := `div[role="textbox"][contenteditable="true"]`

	// Wait for the input to be available
	_, err := c.page.WaitForSelector(selector, playwright.PageWaitForSelectorOptions{
		Timeout: playwright.Float(5000),
	})
	if err != nil {
		return fmt.Errorf("chat input not available: %w", err)
	}

	// Get the input element
	input := c.page.Locator(selector)

	// Clear and type the message
	if err := input.Fill(text); err != nil {
		return fmt.Errorf("failed to fill message: %w", err)
	}

	// Press Enter to send
	if err := input.Press("Enter"); err != nil {
		return fmt.Errorf("failed to press Enter: %w", err)
	}

	c.log.Debugf("Sent message: %s", text)
	return nil
}

// OnMessage sets the callback for new messages
func (c *PlaywrightClient) OnMessage(callback func(*NewMessagePayload)) {
	c.messageCallback = callback
}

// Disconnect closes the browser
func (c *PlaywrightClient) Disconnect() error {
	c.mu.Lock()
	c.connected = false
	c.mu.Unlock()

	c.log.Info("Closing Playwright browser")

	if c.browser != nil {
		if err := c.browser.Close(); err != nil {
			c.log.Warnf("Error closing browser: %v", err)
		}
	}

	if c.pw != nil {
		if err := c.pw.Stop(); err != nil {
			c.log.Warnf("Error stopping Playwright: %v", err)
		}
	}

	return nil
}

