use serde::{Deserialize, Serialize};
use std::io::{self, Read};
use tao::{
    event::{Event, WindowEvent, ElementState, KeyEvent},
    event_loop::{ControlFlow, EventLoop},
    keyboard::KeyCode,
    window::WindowBuilder,
};
use wry::{WebViewBuilder, WebContext};

#[cfg(target_os = "windows")]
use tao::platform::windows::WindowExtWindows;

#[cfg(target_os = "linux")]
use tao::platform::unix::WindowExtUnix;

#[cfg(target_os = "linux")]
use wry::WebViewBuilderExtUnix;

#[cfg(windows)]
use windows::Win32::UI::WindowsAndMessaging::{
    GetSystemMenu, AppendMenuW, MF_STRING, MF_SEPARATOR, WM_SYSCOMMAND,
};
#[cfg(windows)]
use windows::Win32::UI::Shell::{
    SetWindowSubclass, DefSubclassProc,
};
#[cfg(windows)]
use windows::Win32::Foundation::{HWND, LPARAM, WPARAM, LRESULT};
#[cfg(windows)]
use windows::core::PCWSTR;
#[cfg(windows)]
use std::sync::atomic::{AtomicBool, Ordering};

// Custom menu IDs (must be < 0xF000 to avoid conflicts with system commands)
#[cfg(windows)]
const IDM_ABOUT: u32 = 0x1000;
#[cfg(windows)]
const IDM_ZOOM_IN: u32 = 0x1001;
#[cfg(windows)]
const IDM_ZOOM_OUT: u32 = 0x1002;
#[cfg(windows)]
const IDM_ZOOM_RESET: u32 = 0x1003;
#[cfg(windows)]
const IDM_ZOOM_50: u32 = 0x1010;
#[cfg(windows)]
const IDM_ZOOM_75: u32 = 0x1011;
#[cfg(windows)]
const IDM_ZOOM_100: u32 = 0x1012;
#[cfg(windows)]
const IDM_ZOOM_125: u32 = 0x1013;
#[cfg(windows)]
const IDM_ZOOM_150: u32 = 0x1014;
#[cfg(windows)]
const IDM_ZOOM_200: u32 = 0x1015;

// Global flags
#[cfg(windows)]
static ABOUT_CLICKED: AtomicBool = AtomicBool::new(false);
#[cfg(windows)]
static ZOOM_ACTION: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);

// Window subclass procedure to intercept WM_SYSCOMMAND
#[cfg(windows)]
unsafe extern "system" fn subclass_proc(
    hwnd: HWND,
    msg: u32,
    wparam: WPARAM,
    lparam: LPARAM,
    _uidsubclass: usize,
    _dwrefdata: usize,
) -> LRESULT {
    if msg == WM_SYSCOMMAND {
        let cmd = wparam.0 & 0xFFF0;
        match cmd as u32 {
            IDM_ABOUT => {
                ABOUT_CLICKED.store(true, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_IN => {
                ZOOM_ACTION.store(1, Ordering::SeqCst); // Zoom in
                return LRESULT(0);
            }
            IDM_ZOOM_OUT => {
                ZOOM_ACTION.store(2, Ordering::SeqCst); // Zoom out
                return LRESULT(0);
            }
            IDM_ZOOM_RESET => {
                ZOOM_ACTION.store(100, Ordering::SeqCst); // Reset to 100%
                return LRESULT(0);
            }
            IDM_ZOOM_50 => {
                ZOOM_ACTION.store(50, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_75 => {
                ZOOM_ACTION.store(75, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_100 => {
                ZOOM_ACTION.store(100, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_125 => {
                ZOOM_ACTION.store(125, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_150 => {
                ZOOM_ACTION.store(150, Ordering::SeqCst);
                return LRESULT(0);
            }
            IDM_ZOOM_200 => {
                ZOOM_ACTION.store(200, Ordering::SeqCst);
                return LRESULT(0);
            }
            _ => {}
        }
    }
    DefSubclassProc(hwnd, msg, wparam, lparam)
}

// JavaScript API to inject into webviews
const MSGER_JS_API: &str = include_str!("msger-api.js");

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Position {
    x: i32,
    y: i32,
    #[serde(default)]
    #[allow(dead_code)] // Used conditionally on Windows only
    screen: Option<i32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Size {
    width: i32,
    height: i32,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct MessageBoxOptions {
    #[serde(default = "default_title")]
    title: String,
    #[serde(default)]
    message: Option<String>,
    #[serde(default)]
    html: Option<String>,
    #[serde(default)]
    url: Option<String>,
    // New size object (preferred)
    #[serde(default)]
    size: Option<Size>,
    // Deprecated: use size.width instead
    #[serde(default = "default_width")]
    width: i32,
    // Deprecated: use size.height instead
    #[serde(default = "default_height")]
    height: i32,
    #[serde(default)]
    pos: Option<Position>,
    #[serde(default = "default_buttons")]
    buttons: Vec<String>,
    #[serde(default)]
    default_value: Option<String>,
    #[serde(default)]
    input_placeholder: Option<String>,
    #[serde(default)]
    allow_input: bool,
    #[serde(default)]
    timeout: Option<u64>,
    #[serde(default)]
    auto_size: bool,
    #[serde(default)]
    always_on_top: bool,
    #[serde(default)]
    fullscreen: bool,
    #[serde(default)]
    zoom: Option<f64>,
    #[serde(default)]
    debug: bool,
    #[serde(default)]
    icon: Option<String>,
    #[serde(default)]
    dev: bool,
    #[serde(default = "default_true")]
    escape_closes: bool,
    #[serde(default)]
    reset: bool,
}

fn default_true() -> bool {
    true
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
struct DebugInfo {
    html: String,
    width: i32,
    height: i32,
    auto_size: bool,
}

#[derive(Serialize, Deserialize, Debug)]
struct MessageBoxResult {
    button: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    value: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    form: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    closed: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    dismissed: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    timeout: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    debug: Option<DebugInfo>,
}

fn default_title() -> String {
    // Read version from package.json at runtime (not embedded at build time)
    let version = get_package_version().unwrap_or_else(|| "unknown".to_string());
    format!("msger {}", version)
}

fn get_package_version() -> Option<String> {
    // Read version from parent package.json (not msger-native/package.json)
    let exe_path = std::env::current_exe().ok()?;
    let exe_dir = exe_path.parent()?;

    // Try multiple possible locations for package.json
    // NOTE: Check ../../package.json FIRST to skip msger-native/package.json
    let possible_paths = vec![
        exe_dir.join("../../package.json"),        // Development: bin/../../package.json (parent of msger-native)
        exe_dir.join("../../../package.json"),     // Installed globally: lib/node_modules/@bobfrankston/msger/package.json
        exe_dir.join("../../../../package.json"),  // Deeply nested node_modules
    ];

    for path in possible_paths {
        if let Ok(content) = std::fs::read_to_string(&path) {
            // Simple JSON parsing - just extract version field
            if let Some(version_line) = content.lines().find(|line| line.contains("\"version\"")) {
                if let Some(start) = version_line.find("\":") {
                    let after_colon = &version_line[start+2..].trim_start();
                    if let Some(quote_start) = after_colon.find('"') {
                        if let Some(quote_end) = after_colon[quote_start+1..].find('"') {
                            return Some(after_colon[quote_start+1..quote_start+1+quote_end].to_string());
                        }
                    }
                }
            }
        }
    }

    None
}

fn default_width() -> i32 {
    600
}

fn default_height() -> i32 {
    400
}

fn default_buttons() -> Vec<String> {
    vec!["OK".to_string()]
}

fn load_icon(icon_path: &str) -> Option<tao::window::Icon> {
    use image::GenericImageView;

    match image::open(icon_path) {
        Ok(img) => {
            let (width, height) = img.dimensions();
            let rgba = img.to_rgba8().into_raw();

            match tao::window::Icon::from_rgba(rgba, width, height) {
                Ok(icon) => Some(icon),
                Err(e) => {
                    eprintln!("Failed to create icon from {}: {}", icon_path, e);
                    None
                }
            }
        }
        Err(e) => {
            eprintln!("Failed to load icon {}: {}", icon_path, e);
            None
        }
    }
}

fn generate_html(options: &MessageBoxOptions) -> String {
    let html_content = if let Some(ref html) = options.html {
        html.clone()
    } else if let Some(ref message) = options.message {
        let escaped = escape_html(message);
        let with_breaks = escaped.replace("\n", "<br>");
        format!("<pre style='font-family: monospace; white-space: pre-wrap; margin: 0;'>{}</pre>", with_breaks)
    } else {
        String::new()
    };

    let buttons_html: String = options
        .buttons
        .iter()
        .map(|btn| {
            // Add a class for OK buttons to make them green
            let class = if btn.eq_ignore_ascii_case("OK") {
                r#" class="ok-button""#
            } else {
                ""
            };
            format!(
                r#"<button{} onclick="sendResult('{}')">{}</button>"#,
                class,
                escape_html(btn),
                escape_html(btn)
            )
        })
        .collect::<Vec<_>>()
        .join("\n                ");

    let input_html = if options.allow_input {
        let placeholder = options.input_placeholder.as_deref().unwrap_or("");
        format!(
            r#"<input type="text" id="inputField" value="{}" placeholder="{}" />"#,
            escape_html(options.default_value.as_deref().unwrap_or("")),
            escape_html(placeholder)
        )
    } else {
        String::new()
    };

    // Load HTML template and replace placeholders
    let template = include_str!("template.html");
    let html = template
        .replace("{TITLE}", &options.title)
        .replace("{CONTENT}", &html_content)
        .replace("{INPUT}", &input_html)
        .replace("{BUTTONS}", &buttons_html)
        .replace("{AUTO_SIZE}", if options.auto_size { "true" } else { "false" })
        .replace("{FULLSCREEN}", if options.fullscreen { "true" } else { "false" })
        .replace("{ESCAPE_CLOSES}", if options.escape_closes { "true" } else { "false" })
        .replace("{RESET}", if options.reset { "true" } else { "false" })
        .replace("{ZOOM_PERCENT}", &options.zoom.unwrap_or(100.0).to_string())
        .replace("{DEFAULT_BUTTON}", options.buttons.first().unwrap_or(&"Cancel".to_string()))
        .replace("{TIMEOUT_SECONDS}", &options.timeout.map(|t| t.to_string()).unwrap_or_else(|| "0".to_string()));

    html
}

fn escape_html(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#39;")
}


fn main() {
    // Check for debug mode via environment variable
    let debug_mode = std::env::var("MSGER_DEBUG").is_ok();

    if debug_mode {
        eprintln!("🐛 DEBUG MODE ENABLED (MSGER_DEBUG environment variable set)");
    }

    // Read JSON from stdin
    let mut input = String::new();
    io::stdin()
        .read_to_string(&mut input)
        .expect("Failed to read stdin");

    // Parse JSON into options
    let options: MessageBoxOptions = serde_json::from_str(&input)
        .expect("Failed to parse JSON input");

    if debug_mode {
        eprintln!("🐛 Parsed options: title={}, buttons={:?}, fullscreen={}",
                  options.title, options.buttons, options.fullscreen);
    }

    // Create event loop and window
    let event_loop = EventLoop::new();
    
    // Calculate position with optional screen offset (Windows only)
    let final_position = if let Some(ref pos) = options.pos {
        #[cfg_attr(not(target_os = "windows"), allow(unused_mut))]
        let mut x = pos.x;
        #[cfg_attr(not(target_os = "windows"), allow(unused_mut))]
        let mut y = pos.y;

        // On Windows, get the actual monitor position if screen number provided
        #[cfg(target_os = "windows")]
        if let Some(screen_num) = pos.screen {
            // Get all monitors and sort them by physical layout
            // (bottom row first, then left to right)
            let mut monitors: Vec<_> = event_loop.available_monitors().collect();

            // Sort by Top (descending - bottom first), then by Left (ascending - left to right)
            monitors.sort_by(|a, b| {
                let a_pos = a.position();
                let b_pos = b.position();

                // Compare Y positions (descending - higher Y = lower on screen = bottom row)
                match b_pos.y.cmp(&a_pos.y) {
                    std::cmp::Ordering::Equal => {
                        // Same row: sort by X position (ascending - left to right)
                        a_pos.x.cmp(&b_pos.x)
                    }
                    other => other,
                }
            });

            // Get the specific monitor by index from sorted list
            if let Some(monitor) = monitors.get(screen_num as usize) {
                // Get the monitor's actual position from the OS
                let monitor_pos = monitor.position();

                // Add user's offset to monitor's position
                x += monitor_pos.x;
                y += monitor_pos.y;
            } else {
                eprintln!("Warning: Screen {} not found, using primary monitor", screen_num);
            }
        }

        Some(tao::dpi::LogicalPosition::new(x as f64, y as f64))
    } else {
        None
    };

    // Determine final width and height (size object takes precedence over individual width/height)
    let (final_width, final_height) = if let Some(ref size) = options.size {
        (size.width, size.height)
    } else {
        (options.width, options.height)
    };

    // Load window icon (default or custom)
    let icon = if let Some(ref icon_path) = options.icon {
        load_icon(icon_path)
    } else {
        // Try default icon next to the binary - prefer .ico on Windows, then .png
        if let Ok(exe_path) = std::env::current_exe() {
            if let Some(exe_dir) = exe_path.parent() {
                // On Windows, prefer .ico for better taskbar display
                #[cfg(target_os = "windows")]
                let icon_extensions = vec!["msger.ico", "msger.png"];
                #[cfg(not(target_os = "windows"))]
                let icon_extensions = vec!["msger.png"];
                
                let mut found_icon = None;
                for icon_name in icon_extensions {
                    let icon_path = exe_dir.join(icon_name);
                    if icon_path.exists() {
                        found_icon = load_icon(icon_path.to_str().unwrap_or(icon_name));
                        break;
                    }
                }
                
                if found_icon.is_some() {
                    found_icon
                } else {
                    // Fallback to current directory
                    #[cfg(target_os = "windows")]
                    { load_icon("msger.ico") }
                    #[cfg(not(target_os = "windows"))]
                    { load_icon("msger.png") }
                }
            } else {
                #[cfg(target_os = "windows")]
                { load_icon("msger.ico") }
                #[cfg(not(target_os = "windows"))]
                { load_icon("msger.png") }
            }
        } else {
            #[cfg(target_os = "windows")]
            { load_icon("msger.ico") }
            #[cfg(not(target_os = "windows"))]
            { load_icon("msger.png") }
        }
    };

    let mut window_builder = WindowBuilder::new()
        .with_title(&options.title)
        .with_inner_size(tao::dpi::LogicalSize::new(
            final_width as f64,
            final_height as f64,
        ))
        .with_resizable(true)
        .with_always_on_top(options.always_on_top);

    // On Linux, show window immediately (invisible approach didn't fix rendering)
    // #[cfg(target_os = "linux")]
    // {
    //     window_builder = window_builder.with_visible(false);
    // }

    // Set icon if loaded successfully
    if let Some(icon) = icon {
        window_builder = window_builder.with_window_icon(Some(icon));
    }

    // Set fullscreen mode if requested
    if options.fullscreen {
        window_builder = window_builder.with_fullscreen(Some(tao::window::Fullscreen::Borderless(None)));
    }

    if let Some(position) = final_position {
        window_builder = window_builder.with_position(position);
    }

    let window = std::sync::Arc::new(window_builder
        .build(&event_loop)
        .expect("Failed to create window"));

    // Add About item to Windows system menu and install subclass
    #[cfg(windows)]
    unsafe {
        let hwnd = HWND(window.hwnd() as *mut _);
        let hmenu = GetSystemMenu(hwnd, false);
        if !hmenu.is_invalid() {
            // Add separator
            AppendMenuW(hmenu, MF_SEPARATOR, 0, PCWSTR::null()).ok();
            
            // Add About item
            let about_text: Vec<u16> = "About msger (Window Info)\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ABOUT as usize, PCWSTR(about_text.as_ptr())).ok();
            
            // Add another separator
            AppendMenuW(hmenu, MF_SEPARATOR, 0, PCWSTR::null()).ok();
            
            // Add Zoom controls
            let zoom_in: Vec<u16> = "Zoom In\tCtrl++\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_IN as usize, PCWSTR(zoom_in.as_ptr())).ok();
            
            let zoom_out: Vec<u16> = "Zoom Out\tCtrl+-\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_OUT as usize, PCWSTR(zoom_out.as_ptr())).ok();
            
            let zoom_reset: Vec<u16> = "Reset Zoom\tCtrl+0\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_RESET as usize, PCWSTR(zoom_reset.as_ptr())).ok();
            
            // Add separator before preset zooms
            AppendMenuW(hmenu, MF_SEPARATOR, 0, PCWSTR::null()).ok();
            
            // Add preset zoom levels
            let zoom_50: Vec<u16> = "Zoom 50%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_50 as usize, PCWSTR(zoom_50.as_ptr())).ok();
            
            let zoom_75: Vec<u16> = "Zoom 75%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_75 as usize, PCWSTR(zoom_75.as_ptr())).ok();
            
            let zoom_100: Vec<u16> = "Zoom 100%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_100 as usize, PCWSTR(zoom_100.as_ptr())).ok();
            
            let zoom_125: Vec<u16> = "Zoom 125%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_125 as usize, PCWSTR(zoom_125.as_ptr())).ok();
            
            let zoom_150: Vec<u16> = "Zoom 150%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_150 as usize, PCWSTR(zoom_150.as_ptr())).ok();
            
            let zoom_200: Vec<u16> = "Zoom 200%\0".encode_utf16().collect();
            AppendMenuW(hmenu, MF_STRING, IDM_ZOOM_200 as usize, PCWSTR(zoom_200.as_ptr())).ok();
        }
        // Install window subclass to intercept WM_SYSCOMMAND
        let _ = SetWindowSubclass(hwnd, Some(subclass_proc), 1, 0).ok();
    }

    // Generate HTML
    let html = generate_html(&options);

    // Create debug info if requested
    let debug_info = if options.debug {
        Some(DebugInfo {
            html: html.clone(),
            width: options.width,
            height: options.height,
            auto_size: options.auto_size,
        })
    } else {
        None
    };

    // Create result holder
    let result = std::sync::Arc::new(std::sync::Mutex::new(None::<MessageBoxResult>));
    let result_clone = result.clone();
    let result_clone2 = result.clone();
    let debug_info_clone = debug_info.clone();
    let debug_info_clone2 = debug_info.clone();

    // Create action holder for window operations from JavaScript
    let pending_action = std::sync::Arc::new(std::sync::Mutex::new(None::<serde_json::Value>));
    let pending_action_clone = pending_action.clone();
    let pending_action_clone2 = pending_action.clone();

    // Clone window for event loop
    let window_clone = window.clone();

    // Set up timeout if specified
    let timeout_instant = options.timeout.map(|secs| {
        std::time::Instant::now() + std::time::Duration::from_secs(secs)
    });
    let timeout_result = result.clone();
    let timeout_debug = debug_info.clone();
    let timeout_buttons = options.buttons.clone();

    // Create WebContext with custom user data directory for msger (separate from other WebView2 apps)
    let data_directory = if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
        let mut path = std::path::PathBuf::from(local_app_data);
        path.push("msger");
        path.push("webview2");
        Some(path)
    } else {
        None
    };
    let mut web_context = WebContext::new(data_directory);

    // Create webview with IPC handler
    // Get GTK container for Linux, or use window reference for other platforms
    #[cfg(target_os = "linux")]
    let vbox = window.default_vbox().expect("Failed to get GTK vbox");


    // webview is only used on Windows for About menu functionality
    #[cfg_attr(not(windows), allow(unused_variables))]
    let webview = if let Some(ref url) = options.url {
        // Create initialization script with config + msger API
        let mut init_script = format!(
            "window.msgerConfig = {{ escapeCloses: {}, reset: {} }};\n{}",
            if options.escape_closes { "true" } else { "false" },
            if options.reset { "true" } else { "false" },
            MSGER_JS_API
        );

        // Add auto-title monitoring for URL mode
        init_script.push_str(r#"
// Auto-update window title from document.title
(function() {
    let lastTitle = '';
    function updateTitle() {
        const currentTitle = document.title;
        if (currentTitle && currentTitle !== lastTitle) {
            lastTitle = currentTitle;
            if (window.ipc && window.ipc.postMessage) {
                window.ipc.postMessage(JSON.stringify({
                    _action: 'setTitle',
                    title: currentTitle
                }));
            }
        }
    }

    // Check on load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', updateTitle);
    } else {
        updateTitle();
    }

    // Monitor for title changes (SPAs, dynamic pages)
    const observer = new MutationObserver(updateTitle);
    const titleEl = document.querySelector('title');
    if (titleEl) {
        observer.observe(titleEl, { childList: true });
    }

    // Also check periodically as fallback
    setInterval(updateTitle, 1000);
})();
"#);

        // Add DevTools opener if -dev flag is set
        if options.dev {
            init_script.push_str(r#"
// Open DevTools automatically when -dev flag is set
(function() {
    function openDevTools() {
        // Simulate F12 key press to open DevTools
        const event = new KeyboardEvent('keydown', {
            key: 'F12',
            code: 'F12',
            keyCode: 123,
            which: 123,
            bubbles: true,
            cancelable: true
        });
        document.dispatchEvent(event);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(openDevTools, 100));
    } else {
        setTimeout(openDevTools, 100);
    }
})();
"#);
        }

        // Load URL with msger API injected
        let builder = WebViewBuilder::with_web_context(&mut web_context)
            .with_url(url)
            .with_initialization_script(&init_script)
            .with_devtools(true)
            .with_ipc_handler(move |msg| {
                if std::env::var("MSGER_DEBUG").is_ok() {
                    eprintln!("🐛 IPC message received: {}", msg.body());
                }
                // Parse message as JSON
                if let Ok(json_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
                    // Check for action messages
                    if json_msg.get("_action").is_some() {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 IPC action message detected");
                        }
                        let mut action_lock = pending_action_clone.lock().unwrap();
                        *action_lock = Some(json_msg);
                        return;
                    }

                    // Check for resize message
                    if json_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 IPC resize message detected");
                        }
                        // This is a resize message - we'll handle it in the event loop
                        // For now, just ignore it (window resize from IPC is complex in wry/tao)
                        return;
                    }
                }

                // Parse the result from JavaScript (msg is now a Request<String>)
                if let Ok(mut res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
                    if std::env::var("MSGER_DEBUG").is_ok() {
                        eprintln!("🐛 Button result parsed: {:?} - setting result and exiting", res.button);
                    }
                    // Add debug info if requested
                    res.debug = debug_info_clone.clone();
                    let mut result_lock = result_clone.lock().unwrap();
                    *result_lock = Some(res);
                    if std::env::var("MSGER_DEBUG").is_ok() {
                        eprintln!("🐛 Result lock acquired and set");
                    }
                } else if std::env::var("MSGER_DEBUG").is_ok() {
                    eprintln!("🐛 Failed to parse IPC message as MessageBoxResult");
                }
            });

        // Platform-specific webview building
        #[cfg(target_os = "linux")]
        let webview = builder.build_gtk(vbox).expect("Failed to create webview");

        #[cfg(not(target_os = "linux"))]
        let webview = builder.build(window.as_ref()).expect("Failed to create webview");

        webview
    } else {
        // Use HTML content (msger API already in template.html)
        let builder = WebViewBuilder::with_web_context(&mut web_context)
            .with_html(&html)
            .with_devtools(true)
            .with_ipc_handler(move |msg| {
                if std::env::var("MSGER_DEBUG").is_ok() {
                    eprintln!("🐛 IPC message received: {}", msg.body());
                }
                // Parse message as JSON
                if let Ok(json_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
                    // Check for action messages
                    if json_msg.get("_action").is_some() {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 IPC action message detected");
                        }
                        let mut action_lock = pending_action_clone2.lock().unwrap();
                        *action_lock = Some(json_msg);
                        return;
                    }

                    // Check for resize message
                    if json_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 IPC resize message detected");
                        }
                        // This is a resize message - we'll handle it in the event loop
                        // For now, just ignore it (window resize from IPC is complex in wry/tao)
                        return;
                    }
                }

                // Parse the result from JavaScript (msg is now a Request<String>)
                if let Ok(mut res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
                    if std::env::var("MSGER_DEBUG").is_ok() {
                        eprintln!("🐛 Button result parsed: {:?} - setting result and exiting", res.button);
                    }
                    // Add debug info if requested
                    res.debug = debug_info_clone2.clone();
                    let mut result_lock = result_clone2.lock().unwrap();
                    *result_lock = Some(res);
                    if std::env::var("MSGER_DEBUG").is_ok() {
                        eprintln!("🐛 Result lock acquired and set");
                    }
                } else if std::env::var("MSGER_DEBUG").is_ok() {
                    eprintln!("🐛 Failed to parse IPC message as MessageBoxResult");
                }
            });

        // Platform-specific webview building
        #[cfg(target_os = "linux")]
        let webview = builder.build_gtk(vbox).expect("Failed to create webview");

        #[cfg(not(target_os = "linux"))]
        let webview = builder.build(window.as_ref()).expect("Failed to create webview");

        webview
    };

    // DevTools handling is done via JavaScript injection in init_script above
    // No additional action needed here

    // Run event loop
    event_loop.run(move |event, _,control_flow| {
        // Check for About menu click
        #[cfg(windows)]
        if ABOUT_CLICKED.swap(false, Ordering::SeqCst) {
            // Get current zoom from the page
            let zoom_script = "document.body.style.zoom || '100%'";
            let current_zoom = match webview.evaluate_script(zoom_script) {
                Ok(_) => "100%".to_string(), // Default if can't get it
                Err(_) => "100%".to_string(),
            };
            
            // Get window information
            let position = window_clone.outer_position().unwrap_or_default();
            let inner_size = window_clone.inner_size();
            let outer_size = window_clone.outer_size();
            let scale_factor = window_clone.scale_factor();
            let is_maximized = window_clone.is_maximized();
            let is_fullscreen = window_clone.fullscreen().is_some();
            let is_always_on_top = window_clone.is_always_on_top();

            // Calculate logical sizes (what you specify in the API)
            let logical_inner_width = (inner_size.width as f64 / scale_factor) as i32;
            let logical_inner_height = (inner_size.height as f64 / scale_factor) as i32;
            let logical_outer_width = (outer_size.width as f64 / scale_factor) as i32;
            let logical_outer_height = (outer_size.height as f64 / scale_factor) as i32;

            let info = format!(
                "msger v{}\\n\\n\
                Rust/wry-based message viewer\\n\\n\
                Position: x={}, y={}\\n\
                Logical Size (API): {}x{} inner\\n\
                Physical Size (Screen): {}x{} inner\\n\
                Outer Size (Physical): {}x{}\\n\
                Outer Size (Logical): {}x{}\\n\\n\
                Current Zoom: {}\\n\
                Scale Factor: {:.2} ({}% DPI)\\n\
                Maximized: {}\\n\
                Fullscreen: {}\\n\
                Always On Top: {}",
                get_package_version().unwrap_or_else(|| "unknown".to_string()),
                position.x, position.y,
                logical_inner_width, logical_inner_height,
                inner_size.width, inner_size.height,
                outer_size.width, outer_size.height,
                logical_outer_width, logical_outer_height,
                current_zoom,
                scale_factor, (scale_factor * 100.0) as i32,
                is_maximized,
                is_fullscreen,
                is_always_on_top
            );

            // Show the info in an alert
            let script = format!("alert('{}')", info);
            if let Err(e) = webview.evaluate_script(&script) {
                eprintln!("Failed to show about dialog: {}", e);
            }
        }

        // Check for zoom menu actions
        #[cfg(windows)]
        {
            let zoom_action = ZOOM_ACTION.swap(0, Ordering::SeqCst);
            if zoom_action != 0 {
                let zoom_script = match zoom_action {
                    1 => {
                        // Zoom in - get current and increase by 25%
                        "var currentZoom = parseFloat(document.body.style.zoom || '100'); document.body.style.zoom = (currentZoom + 25) + '%';"
                    }
                    2 => {
                        // Zoom out - get current and decrease by 25%
                        "var currentZoom = parseFloat(document.body.style.zoom || '100'); document.body.style.zoom = Math.max(25, currentZoom - 25) + '%';"
                    }
                    percent => {
                        // Set specific zoom level
                        &format!("document.body.style.zoom = '{}%';", percent)
                    }
                };
                
                if let Err(e) = webview.evaluate_script(zoom_script) {
                    eprintln!("Failed to set zoom: {}", e);
                }
            }
        }

        // Check timeout
        if let Some(deadline) = timeout_instant {
            if std::time::Instant::now() >= deadline {
                let mut result_lock = timeout_result.lock().unwrap();
                if result_lock.is_none() {
                    *result_lock = Some(MessageBoxResult {
                        button: timeout_buttons.first().unwrap_or(&"Cancel".to_string()).clone(),
                        value: None,
                        form: None,
                        closed: None,
                        dismissed: None,
                        timeout: Some(true),
                        debug: timeout_debug.clone(),
                    });
                    *control_flow = ControlFlow::Exit;
                    return;
                }
            }
            *control_flow = ControlFlow::WaitUntil(deadline);
        } else {
            *control_flow = ControlFlow::Wait;
        }

        match event {
            Event::WindowEvent {
                event: WindowEvent::KeyboardInput {
                    event: KeyEvent {
                        physical_key: key_code,
                        state: ElementState::Pressed,
                        ..
                    },
                    ..
                },
                ..
            } => {
                if std::env::var("MSGER_DEBUG").is_ok() {
                    eprintln!("🐛 KeyboardInput event - physical_key: {:?}", key_code);
                }
                match key_code {
                    // F11 toggles fullscreen
                    KeyCode::F11 => {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 F11 pressed - toggling fullscreen");
                        }
                        let is_fullscreen = window_clone.fullscreen().is_some();
                        if is_fullscreen {
                            if std::env::var("MSGER_DEBUG").is_ok() {
                                eprintln!("🐛 Exiting fullscreen");
                            }
                            window_clone.set_fullscreen(None);
                        } else {
                            if std::env::var("MSGER_DEBUG").is_ok() {
                                eprintln!("🐛 Entering fullscreen");
                            }
                            // Get current monitor for fullscreen
                            let monitor = window_clone.current_monitor()
                                .or_else(|| window_clone.primary_monitor());
                            window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
                        }

                        // On Linux, no additional handling needed since window starts clean
                    }
                    // Escape exits fullscreen or dismisses window
                    KeyCode::Escape => {
                        let is_fullscreen = window_clone.fullscreen().is_some();
                        if is_fullscreen {
                            // Exit fullscreen
                            window_clone.set_fullscreen(None);
                        } else {
                            // Dismiss window
                            let mut result_lock = result.lock().unwrap();
                            if result_lock.is_none() {
                                *result_lock = Some(MessageBoxResult {
                                    button: "Cancel".to_string(),
                                    value: None,
                                    form: None,
                                    closed: None,
                                    dismissed: Some(true),
                                    timeout: None,
                                    debug: debug_info.clone(),
                                });
                            }
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    _ => {}
                }
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => {
                // Window closed without button click
                let mut result_lock = result.lock().unwrap();
                if result_lock.is_none() {
                    *result_lock = Some(MessageBoxResult {
                        button: options.buttons.first().unwrap_or(&"Cancel".to_string()).clone(),
                        value: None,
                        form: None,
                        closed: Some(true),
                        dismissed: None,
                        timeout: None,
                        debug: debug_info.clone(),
                    });
                }
                *control_flow = ControlFlow::Exit;
            }
            Event::MainEventsCleared => {
                // Check if result is set first (for quick exit on button click)
                {
                    let result_lock = result.lock().unwrap();
                    if result_lock.is_some() {
                        if std::env::var("MSGER_DEBUG").is_ok() {
                            eprintln!("🐛 MainEventsCleared: Result is set, exiting");
                        }
                        *control_flow = ControlFlow::Exit;
                        return;
                    }
                }

                // Process pending actions from JavaScript
                let mut action_lock = pending_action.lock().unwrap();
                if let Some(action_msg) = action_lock.take() {
                    if let Some(action) = action_msg.get("_action").and_then(|v| v.as_str()) {
                        eprintln!("DEBUG: Received IPC action: {}", action);
                        // Wrap all window operations in catch_unwind to prevent crashes
                        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                            match action {
                                "toggleFullscreen" => {
                                    let is_fullscreen = window_clone.fullscreen().is_some();
                                    if is_fullscreen {
                                        window_clone.set_fullscreen(None);
                                    } else {
                                        // Get current monitor for fullscreen
                                        let monitor = window_clone.current_monitor()
                                            .or_else(|| window_clone.primary_monitor());
                                        window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
                                    }
                                }
                                "setFullscreen" => {
                                    if let Some(enabled) = action_msg.get("enabled").and_then(|v| v.as_bool()) {
                                        if enabled {
                                            // Get current monitor for fullscreen
                                            let monitor = window_clone.current_monitor()
                                                .or_else(|| window_clone.primary_monitor());
                                            window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
                                        } else {
                                            window_clone.set_fullscreen(None);
                                        }
                                    }
                                }
                                "escape" => {
                                    // Handle Escape key from JavaScript - exit fullscreen or dismiss window
                                    let is_fullscreen = window_clone.fullscreen().is_some();
                                    let can_close = action_msg.get("canClose").and_then(|v| v.as_bool()).unwrap_or(true);

                                    if is_fullscreen {
                                        // Always allow exiting fullscreen with Escape
                                        window_clone.set_fullscreen(None);
                                    } else if can_close {
                                        // Only dismiss window if canClose is true (controlled by escapeCloses option)
                                        let mut result_lock = result.lock().unwrap();
                                        if result_lock.is_none() {
                                            *result_lock = Some(MessageBoxResult {
                                                button: "Cancel".to_string(),
                                                value: None,
                                                form: None,
                                                closed: None,
                                                dismissed: Some(true),
                                                timeout: None,
                                                debug: None,
                                            });
                                        }
                                    }
                                    // If canClose is false and not in fullscreen, do nothing
                                }
                                "minimize" => {
                                    window_clone.set_minimized(true);
                                }
                                "maximize" => {
                                    window_clone.set_maximized(!window_clone.is_maximized());
                                }
                                "setSize" => {
                                    if let (Some(width), Some(height)) = (
                                        action_msg.get("width").and_then(|v| v.as_f64()),
                                        action_msg.get("height").and_then(|v| v.as_f64())
                                    ) {
                                        window_clone.set_inner_size(tao::dpi::LogicalSize::new(width, height));
                                    }
                                }
                                "setPosition" => {
                                    if let (Some(x), Some(y)) = (
                                        action_msg.get("x").and_then(|v| v.as_f64()),
                                        action_msg.get("y").and_then(|v| v.as_f64())
                                    ) {
                                        window_clone.set_outer_position(tao::dpi::LogicalPosition::new(x, y));
                                    }
                                }
                                "setAlwaysOnTop" => {
                                    if let Some(enabled) = action_msg.get("enabled").and_then(|v| v.as_bool()) {
                                        window_clone.set_always_on_top(enabled);
                                    }
                                }
                                "setTitle" => {
                                    if let Some(title) = action_msg.get("title").and_then(|v| v.as_str()) {
                                        window_clone.set_title(title);
                                    }
                                }
                                "test" => {
                                    eprintln!("DEBUG: Test IPC message received successfully!");
                                    // Don't do anything, just testing if IPC works
                                }
                                _ => {
                                    eprintln!("DEBUG: Unknown action: {}", action);
                                }
                            }
                        }));

                        // Log if operation failed
                        if let Err(e) = result {
                            eprintln!("Window operation '{}' panicked: {:?}", action, e);
                        }
                    }
                }
                drop(action_lock);
            }
            Event::LoopDestroyed => {
                // Output result to stdout
                let result_lock = result.lock().unwrap();
                if let Some(ref res) = *result_lock {
                    println!("{}", serde_json::to_string(res).unwrap());
                }
            }
            _ => {}
        }
    });
}
