//! Standalone mode - WebView with its own window
//!
//! This module handles creating WebView instances in standalone mode,
//! where the WebView creates and manages its own window.

use std::sync::{Arc, Mutex};
use tao::event_loop::EventLoopBuilder;
use tao::window::WindowBuilder;
use wry::WebViewBuilder as WryWebViewBuilder;

use super::config::WebViewConfig;
use super::event_loop::UserEvent;
use super::webview_inner::WebViewInner;
use crate::ipc::{IpcHandler, IpcMessage, MessageQueue};

/// Create standalone WebView with its own window
pub fn create_standalone(
    config: WebViewConfig,
    ipc_handler: Arc<IpcHandler>,
    message_queue: Arc<MessageQueue>,
) -> Result<WebViewInner, Box<dyn std::error::Error>> {
    // Allow event loop to be created on any thread (required for DCC integration)
    // Use UserEvent for custom events (wake-up for immediate message processing)
    #[cfg(target_os = "windows")]
    let event_loop = {
        use tao::platform::windows::EventLoopBuilderExtWindows;
        EventLoopBuilder::<UserEvent>::with_user_event()
            .with_any_thread(true)
            .build()
    };

    #[cfg(not(target_os = "windows"))]
    let event_loop = EventLoopBuilder::<UserEvent>::with_user_event().build();

    #[cfg_attr(not(target_os = "windows"), allow(unused_mut))]
    let mut window_builder = WindowBuilder::new()
        .with_title(&config.title)
        .with_inner_size(tao::dpi::LogicalSize::new(config.width, config.height))
        .with_resizable(config.resizable)
        .with_decorations(config.decorations)
        .with_transparent(config.transparent);

    // Parent/owner on Windows
    #[cfg(target_os = "windows")]
    {
        use crate::webview::config::EmbedMode;
        use tao::platform::windows::WindowBuilderExtWindows;

        if let Some(parent) = config.parent_hwnd {
            match config.embed_mode {
                EmbedMode::Child => {
                    tracing::info!("Creating WS_CHILD window (same-thread parenting required)");
                    // Child windows typically have no decorations
                    window_builder = window_builder
                        .with_decorations(false)
                        .with_parent_window(parent as isize);
                }
                EmbedMode::Owner => {
                    tracing::info!("Creating owned window (cross-thread safe)");
                    window_builder = window_builder.with_owner_window(parent as isize);
                }
                EmbedMode::None => {}
            }
        }
    }

    let window = window_builder.build(&event_loop)?;

    // No manual SetParent needed when using builder-ext on Windows

    // Create the WebView with IPC handler
    let mut webview_builder = WryWebViewBuilder::new();
    if config.dev_tools {
        webview_builder = webview_builder.with_devtools(true);
    }

    // Inject event bridge as initialization script so it persists across navigations
    let event_bridge_script: &str = r#"
    (function() {
        console.log('[AuroraView] Initializing event bridge...');

        // Event handlers registry
        const eventHandlers = new Map();

        // Pending call registry for auroraview.call Promise resolution
        let auroraviewCallIdCounter = 0;
        const auroraviewPendingCalls = new Map();

        function auroraviewGenerateCallId() {
            auroraviewCallIdCounter += 1;
            return 'av_call_' + Date.now() + '_' + auroraviewCallIdCounter;
        }

        // Handle call_result events coming back from Python (Python -> JS)
        window.addEventListener('__auroraview_call_result', function(event) {
            try {
                const detail = event && event.detail ? event.detail : {};
                const id = detail.id;
                if (!id) {
                    console.warn('[AuroraView] call_result without id:', detail);
                    return;
                }
                const pending = auroraviewPendingCalls.get(id);
                if (!pending) {
                    console.warn('[AuroraView] No pending call for id:', id);
                    return;
                }
                auroraviewPendingCalls.delete(id);
                if (detail.ok) {
                    pending.resolve(detail.result);
                } else {
                    const errInfo = detail.error || {};
                    const error = new Error(errInfo.message || 'AuroraView call failed');
                    if (errInfo.name) {
                        error.name = errInfo.name;
                    }
                    if (Object.prototype.hasOwnProperty.call(errInfo, 'code')) {
                        error.code = errInfo.code;
                    }
                    if (Object.prototype.hasOwnProperty.call(errInfo, 'data')) {
                        error.data = errInfo.data;
                    }
                    pending.reject(error);
                }
            } catch (e) {
                console.error('[AuroraView] Failed to handle __auroraview_call_result:', e);
            }
        });



        // Primary AuroraView bridge API
        // Primary name: window.auroraview
        window.auroraview = {
            // High-level call API (JS -> Python, Promise-based)
            call: function(method, params) {
                console.log('[AuroraView] Calling Python method via auroraview.call:', method, params);
                return new Promise(function(resolve, reject) {
                    const id = auroraviewGenerateCallId();
                    auroraviewPendingCalls.set(id, { resolve: resolve, reject: reject });

                    try {
                        const payload = {
                            type: 'call',
                            id: id,
                            method: method,
                        };
                        if (typeof params !== 'undefined') {
                            payload.params = params;
                        }
                        window.ipc.postMessage(JSON.stringify(payload));
                    } catch (e) {
                        console.error('[AuroraView] Failed to send call via IPC:', e);
                        auroraviewPendingCalls.delete(id);
                        reject(e);
                    }
                });
            },

            // Low-level event API (JS -> Python)
            send_event: function(eventName, data) {
                console.log('[AuroraView] Sending event to Python:', eventName, data);
                try {
                    window.ipc.postMessage(JSON.stringify({
                        type: 'event',
                        event: eventName,
                        detail: data || {},
                    }));
                } catch (e) {
                    console.error('[AuroraView] Failed to send event via IPC:', e);
                }
            },

            // Register event handler for Python -> JS communication (CustomEvent-based)
            on: function(eventName, callback) {
                console.log('[AuroraView] Registering handler for event:', eventName);
                if (!eventHandlers.has(eventName)) {
                    eventHandlers.set(eventName, []);
                    var name = eventName;
                    window.addEventListener(name, function(event) {
                        try {
                            var detail = event && event.detail ? event.detail : {};
                            // Strip internal marker used by the backend to avoid leaking implementation details
                            if (detail && Object.prototype.hasOwnProperty.call(detail, '__aurora_from_python')) {
                                detail = Object.assign({}, detail);
                                delete detail.__aurora_from_python;
                            }
                            var handlers = eventHandlers.get(name);
                            if (handlers && handlers.length > 0) {
                                handlers.forEach(function(handler) {
                                    try {
                                        handler(detail);
                                    } catch (e) {
                                        console.error('[AuroraView] Error in event handler for', name, e);
                                    }
                                });
                            }
                        } catch (e) {
                            console.error('[AuroraView] Failed to handle event from Python:', e);
                        }
                    });
                }
                eventHandlers.get(eventName).push(callback);
            },
        };


        // High-level auroraview.api.* sugar over call()
        window.auroraview.api = new Proxy({}, {
            get: function(_target, prop) {
                if (typeof prop !== 'string') {
                    return undefined;
                }
                return function(...args) {
                    var method = 'api.' + prop;
                    var params;
                    if (args.length === 0) {
                        params = undefined;
                    } else if (args.length === 1) {
                        params = args[0];
                    } else {
                        params = args;
                    }
                    return window.auroraview.call(method, params);
                };
            },
        });

        // High-level helper (Qt-style API), kept for compatibility
        window.AuroraView = class {
            constructor() {
                this.ready = true; // Always ready since we're in init script
                console.log('[AuroraView] Helper class initialized');
            }

            // Qt-style emit (JavaScript -> Python)
            emit(signal, data = {}) {
                window.auroraview.send_event(signal, data);
                return this;
            }

            // Qt-style connect (Python -> JavaScript)
            on(signal, slot) {
                if (typeof slot !== 'function') {
                    console.error('[AuroraView] Slot must be a function');
                    return this;
                }
                window.auroraview.on(signal, slot);
                return this;
            }

            // Alias for consistency
            connect(signal, slot) {
                return this.on(signal, slot);
            }

            // Check if ready (always true in init script)
            isReady() {
                return this.ready;
            }
        };

        // Convenience instance: legacy window.aurora
        window.aurora = new window.AuroraView();

        // Intercept CustomEvent dispatch for backward compatibility
        const originalDispatchEvent = window.dispatchEvent;
        window.dispatchEvent = function(event) {
            if (event instanceof CustomEvent) {
                // Ignore events emitted from Python to avoid feedback loop
                if (event.detail && event.detail.__aurora_from_python === true) {
                    return originalDispatchEvent.call(this, event);
                }
                try {
                    const message = {
                        type: 'event',
                        event: event.type,
                        detail: event.detail,
                    };
                    window.ipc.postMessage(JSON.stringify(message));
                } catch (e) {
                    console.error('[AuroraView] Failed to send event via IPC:', e);
                }
            }
            return originalDispatchEvent.call(this, event);
        };

        // Notify page scripts that the AuroraView bridge is ready.
        try {
            var readyEvent;
            if (typeof Event === 'function') {
                readyEvent = new Event('auroraviewready');
            } else {
                readyEvent = document.createEvent('Event');
                readyEvent.initEvent('auroraviewready', true, true);
            }
            window.dispatchEvent(readyEvent);
            console.log('[AuroraView] ✓ Dispatched auroraviewready event');
        } catch (e) {
            console.error('[AuroraView] Failed to dispatch auroraviewready event:', e);
        }

        console.log('[AuroraView] ✓ Bridge initialized');
        console.log('[AuroraView] ✓ Primary API: window.auroraview.call()/send_event()/on()/api.*');
        console.log('[AuroraView] ✓ Qt-style class: new AuroraView()');
    })();
    "#;

    // IMPORTANT: use initialization script so it reloads with every page load
    webview_builder = webview_builder.with_initialization_script(event_bridge_script);

    // Add IPC handler to capture events and calls from JavaScript
    let ipc_handler_clone = ipc_handler.clone();
    webview_builder = webview_builder.with_ipc_handler(move |request| {
        tracing::debug!("IPC message received");

        // The request body is a String
        let body_str = request.body();
        tracing::debug!("IPC body: {}", body_str);

        if let Ok(message) = serde_json::from_str::<serde_json::Value>(body_str) {
            if let Some(msg_type) = message.get("type").and_then(|v| v.as_str()) {
                if msg_type == "event" {
                    if let Some(event_name) = message.get("event").and_then(|v| v.as_str()) {
                        let detail = message
                            .get("detail")
                            .cloned()
                            .unwrap_or(serde_json::Value::Null);
                        tracing::info!(
                            "Event received from JavaScript: {} with detail: {}",
                            event_name,
                            detail
                        );

                        let ipc_message = IpcMessage {
                            event: event_name.to_string(),
                            data: detail,
                            id: None,
                        };

                        if let Err(e) = ipc_handler_clone.handle_message(ipc_message) {
                            tracing::error!("Error handling event: {}", e);
                        }
                    }
                } else if msg_type == "call" {
                    if let Some(method) = message.get("method").and_then(|v| v.as_str()) {
                        let params = message
                            .get("params")
                            .cloned()
                            .unwrap_or(serde_json::Value::Null);
                        let id = message
                            .get("id")
                            .and_then(|v| v.as_str())
                            .map(|s| s.to_string());

                        tracing::info!(
                            "Call received from JavaScript: {} with params: {} id: {:?}",
                            method,
                            params,
                            id
                        );

                        let mut payload = serde_json::Map::new();
                        payload.insert("params".to_string(), params);
                        if let Some(ref call_id) = id {
                            payload.insert(
                                "id".to_string(),
                                serde_json::Value::String(call_id.clone()),
                            );
                        }

                        let ipc_message = IpcMessage {
                            event: method.to_string(),
                            data: serde_json::Value::Object(payload),
                            id,
                        };

                        if let Err(e) = ipc_handler_clone.handle_message(ipc_message) {
                            tracing::error!("Error handling call: {}", e);
                        }
                    }
                }
            }
        }
    });

    let webview = webview_builder.build(&window)?;

    // Apply initial content from config if provided
    if let Some(ref url) = config.url {
        let script = format!("window.location.href = '{}';", url);
        webview.evaluate_script(&script)?;
    } else if let Some(ref html) = config.html {
        webview.load_html(html)?;
    }

    // Create event loop proxy for sending close events
    let event_loop_proxy = event_loop.create_proxy();

    // Create lifecycle manager
    use crate::webview::lifecycle::LifecycleManager;
    let lifecycle = Arc::new(LifecycleManager::new());
    lifecycle.set_state(crate::webview::lifecycle::LifecycleState::Active);

    // Standalone mode doesn't need platform manager (uses event loop instead)
    let platform_manager = None;

    #[allow(clippy::arc_with_non_send_sync)]
    Ok(WebViewInner {
        webview: Arc::new(Mutex::new(webview)),
        window: Some(window),
        event_loop: Some(event_loop),
        message_queue,
        event_loop_proxy: Some(event_loop_proxy),
        lifecycle,
        platform_manager,
        #[cfg(target_os = "windows")]
        backend: None, // Only used in DCC mode
    })
}
