use std::process::Command;
use tauri::command;

use crate::process_ext::HideWindow;

/// Generate PowerShell script for Windows Toast Notification
fn generate_powershell_toast_script(title: &str, body: &str) -> String {
    format!(
        r#"
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null

$APP_ID = 'Hikari Desktop'

$template = @"
<toast>
    <visual>
        <binding template="ToastText02">
            <text id="1">{}</text>
            <text id="2">{}</text>
        </binding>
    </visual>
    <audio src="ms-winsoundevent:Notification.Default" />
</toast>
"@

$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
$xml.LoadXml($template)

$toast = New-Object Windows.UI.Notifications.ToastNotification $xml
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($APP_ID).Show($toast)
"#,
        title.replace("\"", "`\""),
        body.replace("\"", "`\"")
    )
}

/// Format simple notification message
fn format_simple_notification(title: &str, body: &str) -> String {
    format!("{}\n\n{}", title, body)
}

/// Build notify-send command for testing (doesn't execute)
#[cfg(test)]
fn build_notify_send_command(title: &str, body: &str) -> (String, Vec<String>) {
    (
        "notify-send".to_string(),
        vec![
            title.to_string(),
            body.to_string(),
            "--urgency=normal".to_string(),
            "--app-name=Hikari Desktop".to_string(),
        ],
    )
}

/// Build Windows PowerShell command for testing (doesn't execute)
#[cfg(test)]
fn build_windows_powershell_command(title: &str, body: &str) -> (String, Vec<String>) {
    let script = generate_powershell_toast_script(title, body);
    (
        "pwsh.exe".to_string(),
        vec![
            "-NoProfile".to_string(),
            "-WindowStyle".to_string(),
            "Hidden".to_string(),
            "-Command".to_string(),
            script,
        ],
    )
}

/// Build simple notification command for testing (doesn't execute)
#[cfg(test)]
fn build_simple_notification_command(title: &str, body: &str) -> (String, Vec<String>) {
    let message = format_simple_notification(title, body);
    (
        "cmd.exe".to_string(),
        vec!["/c".to_string(), "msg".to_string(), "*".to_string(), message],
    )
}

#[command]
pub async fn send_notify_send(title: String, body: String) -> Result<(), String> {
    // Use notify-send for Linux/WSL
    let output = Command::new("notify-send")
        .hide_window()
        .arg(&title)
        .arg(&body)
        .arg("--urgency=normal")
        .arg("--app-name=Hikari Desktop")
        .output()
        .map_err(|e| {
            format!(
                "Failed to execute notify-send: {}. Make sure libnotify-bin is installed.",
                e
            )
        })?;

    if !output.status.success() {
        let error = String::from_utf8_lossy(&output.stderr);
        return Err(format!("notify-send failed: {}", error));
    }

    Ok(())
}

#[command]
pub async fn send_windows_notification(title: String, body: String) -> Result<(), String> {
    // Create PowerShell script for Windows Toast Notification
    let ps_script = generate_powershell_toast_script(&title, &body);

    // Try PowerShell Core first (pwsh), then fall back to Windows PowerShell
    let output = Command::new("pwsh.exe")
        .hide_window()
        .arg("-NoProfile")
        .arg("-WindowStyle")
        .arg("Hidden")
        .arg("-Command")
        .arg(&ps_script)
        .output()
        .or_else(|_| {
            Command::new("powershell.exe")
                .hide_window()
                .arg("-NoProfile")
                .arg("-WindowStyle")
                .arg("Hidden")
                .arg("-Command")
                .arg(&ps_script)
                .output()
        })
        .map_err(|e| format!("Failed to execute PowerShell: {}", e))?;

    if !output.status.success() {
        let error = String::from_utf8_lossy(&output.stderr);
        return Err(format!("PowerShell script failed: {}", error));
    }

    Ok(())
}

// Alternative: Use Windows built-in MSG command for simple notifications
#[command]
pub async fn send_simple_notification(title: String, body: String) -> Result<(), String> {
    let message = format_simple_notification(&title, &body);

    Command::new("cmd.exe")
        .hide_window()
        .arg("/c")
        .arg("msg")
        .arg("*")
        .arg(&message)
        .output()
        .map_err(|e| format!("Failed to send message: {}", e))?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_powershell_toast_script_basic() {
        let script = generate_powershell_toast_script("Title", "Body");

        assert!(script.contains("Hikari Desktop"));
        assert!(script.contains("Title"));
        assert!(script.contains("Body"));
        assert!(script.contains("ToastNotification"));
    }

    #[test]
    fn test_generate_powershell_toast_script_escapes_quotes() {
        let script = generate_powershell_toast_script("Title with \"quotes\"", "Body with \"quotes\"");

        // Quotes should be escaped as `" in PowerShell
        assert!(script.contains("Title with `\"quotes`\""));
        assert!(script.contains("Body with `\"quotes`\""));
    }

    #[test]
    fn test_generate_powershell_toast_script_with_special_chars() {
        let script = generate_powershell_toast_script("Title: Test", "Body\nwith\nnewlines");

        assert!(script.contains("Title: Test"));
        assert!(script.contains("Body\nwith\nnewlines"));
    }

    #[test]
    fn test_generate_powershell_toast_script_unicode() {
        let script = generate_powershell_toast_script("日本語 Title", "Unicode: 🎉");

        assert!(script.contains("日本語 Title"));
        assert!(script.contains("Unicode: 🎉"));
    }

    #[test]
    fn test_generate_powershell_toast_script_empty() {
        let script = generate_powershell_toast_script("", "");

        // Should still contain the structure
        assert!(script.contains("Hikari Desktop"));
        assert!(script.contains("ToastNotification"));
    }

    #[test]
    fn test_format_simple_notification_basic() {
        let message = format_simple_notification("Title", "Body");

        assert_eq!(message, "Title\n\nBody");
    }

    #[test]
    fn test_format_simple_notification_with_newlines() {
        let message = format_simple_notification("Multi\nLine\nTitle", "Multi\nLine\nBody");

        assert!(message.contains("Multi\nLine\nTitle"));
        assert!(message.contains("\n\n"));
        assert!(message.contains("Multi\nLine\nBody"));
    }

    #[test]
    fn test_format_simple_notification_unicode() {
        let message = format_simple_notification("日本語", "🎉 Unicode");

        assert_eq!(message, "日本語\n\n🎉 Unicode");
    }

    #[test]
    fn test_format_simple_notification_empty() {
        let message = format_simple_notification("", "");

        assert_eq!(message, "\n\n");
    }

    #[test]
    fn test_format_simple_notification_long_text() {
        let long_title = "A".repeat(1000);
        let long_body = "B".repeat(1000);
        let message = format_simple_notification(&long_title, &long_body);

        assert!(message.starts_with(&long_title));
        assert!(message.ends_with(&long_body));
        assert!(message.contains("\n\n"));
    }

    #[test]
    fn test_generate_powershell_toast_script_multiple_quotes() {
        let script = generate_powershell_toast_script(
            "\"Quoted\" \"Multiple\" \"Times\"",
            "\"More\" \"Quotes\" \"Here\""
        );

        // Each quote should be escaped
        assert!(script.contains("`\"Quoted`\" `\"Multiple`\" `\"Times`\""));
        assert!(script.contains("`\"More`\" `\"Quotes`\" `\"Here`\""));
    }

    // E2E Integration Tests - Command Structure Verification

    #[test]
    fn test_e2e_notify_send_command_structure() {
        let (command, args) = build_notify_send_command("Test Title", "Test Body");

        assert_eq!(command, "notify-send");
        assert_eq!(args.len(), 4);
        assert_eq!(args[0], "Test Title");
        assert_eq!(args[1], "Test Body");
        assert_eq!(args[2], "--urgency=normal");
        assert_eq!(args[3], "--app-name=Hikari Desktop");
    }

    #[test]
    fn test_e2e_notify_send_with_special_chars() {
        let (command, args) =
            build_notify_send_command("Title with \"quotes\"", "Body\nwith\nnewlines");

        assert_eq!(command, "notify-send");
        assert_eq!(args[0], "Title with \"quotes\"");
        assert_eq!(args[1], "Body\nwith\nnewlines");
        // notify-send handles these directly
    }

    #[test]
    fn test_e2e_windows_powershell_command_structure() {
        let (command, args) = build_windows_powershell_command("Test Title", "Test Body");

        assert_eq!(command, "pwsh.exe");
        assert_eq!(args.len(), 5);
        assert_eq!(args[0], "-NoProfile");
        assert_eq!(args[1], "-WindowStyle");
        assert_eq!(args[2], "Hidden");
        assert_eq!(args[3], "-Command");

        // Verify the script in args[4] contains expected elements
        let script = &args[4];
        assert!(script.contains("Test Title"));
        assert!(script.contains("Test Body"));
        assert!(script.contains("Hikari Desktop"));
        assert!(script.contains("ToastNotification"));
    }

    #[test]
    fn test_e2e_windows_powershell_quote_escaping() {
        let (_, args) =
            build_windows_powershell_command("Title with \"quotes\"", "Body with \"quotes\"");

        let script = &args[4];
        // Verify quotes are properly escaped in the PowerShell script
        assert!(script.contains("Title with `\"quotes`\""));
        assert!(script.contains("Body with `\"quotes`\""));
    }

    #[test]
    fn test_e2e_simple_notification_command_structure() {
        let (command, args) = build_simple_notification_command("Test Title", "Test Body");

        assert_eq!(command, "cmd.exe");
        assert_eq!(args.len(), 4);
        assert_eq!(args[0], "/c");
        assert_eq!(args[1], "msg");
        assert_eq!(args[2], "*");
        assert_eq!(args[3], "Test Title\n\nTest Body");
    }

    #[test]
    fn test_e2e_simple_notification_multiline() {
        let (_, args) =
            build_simple_notification_command("Multi\nLine\nTitle", "Multi\nLine\nBody");

        let message = &args[3];
        assert!(message.contains("Multi\nLine\nTitle"));
        assert!(message.contains("\n\n"));
        assert!(message.contains("Multi\nLine\nBody"));
    }

    #[test]
    fn test_e2e_command_consistency_across_platforms() {
        // Test that different platforms use consistent parameters
        let title = "Consistency Test";
        let body = "Testing cross-platform consistency";

        // Linux command
        let (notify_cmd, notify_args) = build_notify_send_command(title, body);
        assert!(notify_cmd.contains("notify"));
        assert!(notify_args.iter().any(|arg| arg.contains("Hikari Desktop")));

        // Windows PowerShell command
        let (ps_cmd, ps_args) = build_windows_powershell_command(title, body);
        assert!(ps_cmd.contains("pwsh") || ps_cmd.contains("powershell"));
        let ps_script = &ps_args[4];
        assert!(ps_script.contains("Hikari Desktop"));

        // Windows simple command
        let (msg_cmd, msg_args) = build_simple_notification_command(title, body);
        assert!(msg_cmd.contains("cmd"));
        assert!(msg_args[3].contains(title));
        assert!(msg_args[3].contains(body));
    }

    #[test]
    fn test_e2e_unicode_support_across_platforms() {
        let title = "日本語 Title";
        let body = "Unicode: 🎉";

        // Verify all platforms preserve unicode
        let (_, notify_args) = build_notify_send_command(title, body);
        assert_eq!(notify_args[0], title);
        assert_eq!(notify_args[1], body);

        let (_, ps_args) = build_windows_powershell_command(title, body);
        let ps_script = &ps_args[4];
        assert!(ps_script.contains(title));
        assert!(ps_script.contains(body));

        let (_, msg_args) = build_simple_notification_command(title, body);
        assert!(msg_args[3].contains(title));
        assert!(msg_args[3].contains(body));
    }

    #[test]
    fn test_e2e_empty_input_handling() {
        // Test that empty inputs are handled gracefully
        let (_, notify_args) = build_notify_send_command("", "");
        assert_eq!(notify_args[0], "");
        assert_eq!(notify_args[1], "");

        let (_, ps_args) = build_windows_powershell_command("", "");
        let ps_script = &ps_args[4];
        assert!(ps_script.contains("Hikari Desktop")); // Still has app name

        let (_, msg_args) = build_simple_notification_command("", "");
        assert_eq!(msg_args[3], "\n\n");
    }
}
