use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::process::Command;

/// 前端传过来的"快捷方式"定义，与 JSON 中的 shortcut 项保持一致
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Shortcut {
    pub id: String,
    pub label: String,
    #[serde(default)]
    pub icon: String,
    /// "url" | "app" | "cmd" | "powershell"
    #[serde(rename = "type")]
    pub kind: String,
    pub target: String,
    #[serde(default)]
    pub args: Vec<String>,
    #[serde(default)]
    pub cwd: Option<String>,
    /// 是否静默执行（仅对 cmd/powershell 生效）：
    /// - false（默认）：弹出控制台窗口，用户能看到输出，适合调试或交互命令（pause/read）
    /// - true：隐藏控制台，适合后台脚本
    #[serde(default)]
    pub silent: bool,
}

/// 根据 type 分发执行：
/// - url：交给前端用 opener 打开（这里直接返回标记，让前端处理）
/// - app：直接以独立进程方式启动可执行文件
/// - cmd：通过 cmd.exe /C 执行
/// - powershell：通过 powershell.exe -NoProfile -Command 执行
pub fn execute(shortcut: &Shortcut) -> Result<()> {
    match shortcut.kind.as_str() {
        "url" => {
            // url 类型在前端通过 opener 插件处理；保留这里以便扩展（例如内置浏览器）
            anyhow::bail!("url shortcuts should be handled by frontend opener");
        }
        "app" => spawn_app(shortcut),
        "cmd" => spawn_cmd(shortcut),
        "powershell" => spawn_powershell(shortcut),
        other => anyhow::bail!("unsupported shortcut type: {other}"),
    }
}

fn spawn_app(s: &Shortcut) -> Result<()> {
    let mut cmd = Command::new(&s.target);
    if !s.args.is_empty() {
        cmd.args(&s.args);
    }
    apply_cwd(&mut cmd, &s.cwd);
    // 启动可执行文件本身不弹出控制台
    hide_console(&mut cmd);
    cmd.spawn()
        .with_context(|| format!("failed to spawn app: {}", s.target))?;
    Ok(())
}

/// 用 `cmd.exe /C` 执行一行命令。默认显式打开一个新控制台窗口让用户能看到输出。
fn spawn_cmd(s: &Shortcut) -> Result<()> {
    let mut cmd = Command::new("cmd.exe");
    // /K 保持窗口不关闭，便于用户看完输出；silent 模式用 /C 静默
    if s.silent {
        cmd.arg("/C").arg(&s.target);
        hide_console(&mut cmd);
    } else {
        // 用 start "title" cmd /K 在新窗口里运行，这样窗口不会因为父进程退出而立即销毁
        // 这里直接走 cmd /K 的方式，CREATE_NEW_CONSOLE 标志可见
        cmd.arg("/C")
            .arg("start")
            .arg("") // title 占位
            .arg("cmd.exe")
            .arg("/K")
            .arg(&s.target);
        new_console(&mut cmd);
    }
    apply_cwd(&mut cmd, &s.cwd);
    cmd.spawn()
        .with_context(|| format!("failed to spawn cmd: {}", s.target))?;
    Ok(())
}

/// 用 powershell.exe 执行一段脚本。
fn spawn_powershell(s: &Shortcut) -> Result<()> {
    let mut cmd = Command::new("powershell.exe");
    cmd.arg("-NoProfile");
    if s.silent {
        cmd.arg("-WindowStyle").arg("Hidden");
        cmd.arg("-Command").arg(&s.target);
        hide_console(&mut cmd);
    } else {
        // -NoExit 让窗口在命令执行完后保留，方便查看输出
        cmd.arg("-NoExit").arg("-Command").arg(&s.target);
        new_console(&mut cmd);
    }
    apply_cwd(&mut cmd, &s.cwd);
    cmd.spawn()
        .with_context(|| format!("failed to spawn powershell: {}", s.target))?;
    Ok(())
}

fn apply_cwd(cmd: &mut Command, cwd: &Option<String>) {
    if let Some(c) = cwd {
        if !c.trim().is_empty() {
            cmd.current_dir(c);
        }
    }
}

/// Windows 下隐藏子进程的控制台窗口
#[cfg(target_os = "windows")]
fn hide_console(cmd: &mut Command) {
    use std::os::windows::process::CommandExt;
    // CREATE_NO_WINDOW = 0x08000000
    cmd.creation_flags(0x0800_0000);
}

#[cfg(not(target_os = "windows"))]
fn hide_console(_cmd: &mut Command) {}

/// Windows 下显式给子进程开一个新的可见控制台窗口
#[cfg(target_os = "windows")]
fn new_console(cmd: &mut Command) {
    use std::os::windows::process::CommandExt;
    // CREATE_NEW_CONSOLE = 0x00000010
    cmd.creation_flags(0x0000_0010);
}

#[cfg(not(target_os = "windows"))]
fn new_console(_cmd: &mut Command) {}
