use proxywarden_lib::component_detection::DetectedSingBox;
use proxywarden_lib::singbox_service::{
    build_singbox_setup_status, ensure_safe_singbox_install_dir, parse_service_command_output,
    service_control_script, SingBoxServiceAction,
};
use std::path::{Path, PathBuf};
#[cfg(windows)]
use std::process::Command as ProcessCommand;

#[test]
fn setup_status_reports_missing_items_when_singbox_is_absent() {
    let status = build_singbox_setup_status(None);

    assert!(!status.ready);
    assert_eq!(status.missing_count, 3);
    assert_eq!(status.items[0].id, "sing-box-binary");
    assert!(status.items[0].details.contains("SagerNet/sing-box"));
    assert_eq!(status.items[1].id, "winsw-wrapper");
    assert!(status.items[1].details.contains("winsw/winsw"));
    assert_eq!(status.items[2].id, "windows-service");
}

#[test]
fn setup_status_reports_ready_when_binary_wrapper_and_service_exist() {
    let detected = detected_singbox(true, true, true);
    let status = build_singbox_setup_status(Some(&detected));

    assert!(status.ready);
    assert_eq!(status.missing_count, 0);
    assert!(status.items.iter().all(|item| item.installed));
}

#[test]
fn parses_last_json_service_command_output_line() {
    let output = br#"
noise
{"success":true,"code":"started","serviceName":"ProxyWardenSingBox","status":"Running","processId":42}
"#;
    let parsed = parse_service_command_output(output).expect("service json should parse");

    assert!(parsed.success);
    assert_eq!(parsed.code, "started");
    assert_eq!(parsed.service_name, Some("ProxyWardenSingBox".to_string()));
    assert_eq!(parsed.status, Some("Running".to_string()));
    assert_eq!(parsed.process_id, Some(42));
}

#[test]
fn safe_install_dir_allows_only_proxywarden_singbox_folder() {
    assert!(
        ensure_safe_singbox_install_dir(Path::new(r"C:\Program Files\ProxyWarden\sing-box"))
            .is_ok()
    );
    assert!(ensure_safe_singbox_install_dir(Path::new(r"C:\Windows")).is_err());
    assert!(ensure_safe_singbox_install_dir(Path::new(r"C:\Program Files\sing-box")).is_err());
}

#[test]
fn service_control_script_targets_named_service_and_action() {
    let script = service_control_script(
        SingBoxServiceAction::Start,
        "ProxyWardenSingBox",
        None,
        None,
    );

    assert!(script.contains("$serviceName = 'ProxyWardenSingBox'"));
    assert!(script.contains("$action = 'start'"));
    assert!(script.contains("ConvertTo-Json -Compress"));
}

#[test]
fn service_control_script_syncs_generated_config_before_start() {
    let source = Path::new(r"C:\ProgramData\ProxyWarden\generated\sing-box-config.json");
    let target = Path::new(r"C:\Program Files\ProxyWarden\sing-box\config.json");
    let script = service_control_script(
        SingBoxServiceAction::Start,
        "ProxyWardenSingBox",
        Some(source),
        Some(target),
    );

    assert!(script.contains(
        "$configSource = 'C:\\ProgramData\\ProxyWarden\\generated\\sing-box-config.json'"
    ));
    assert!(
        script.contains("$configTarget = 'C:\\Program Files\\ProxyWarden\\sing-box\\config.json'")
    );
    assert!(script.contains("Copy-Item -LiteralPath $configSource"));
    assert!(script.contains("'config_sync_failed'"));
}

#[test]
#[cfg(windows)]
fn install_singbox_script_parses_as_powershell() {
    let script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("..")
        .join("scripts")
        .join("install-singbox.ps1");
    let escaped_path = script_path.display().to_string().replace('\'', "''");
    let parser = format!(
        "$tokens = $null; $errors = $null; [System.Management.Automation.Language.Parser]::ParseFile('{escaped_path}', [ref]$tokens, [ref]$errors) | Out-Null; if ($errors.Count -gt 0) {{ $errors | ForEach-Object {{ $_.Message }}; exit 1 }}"
    );
    let output = ProcessCommand::new("powershell")
        .args(["-NoProfile", "-NonInteractive", "-Command", &parser])
        .output()
        .expect("powershell parser should run");

    assert!(
        output.status.success(),
        "install-singbox.ps1 should parse\nstdout:\n{}\nstderr:\n{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr),
    );
}

fn detected_singbox(binary_exists: bool, wrapper_exists: bool, running: bool) -> DetectedSingBox {
    DetectedSingBox {
        install_dir: PathBuf::from(r"C:\Program Files\ProxyWarden\sing-box"),
        executable_path: PathBuf::from(r"C:\Program Files\ProxyWarden\sing-box\sing-box.exe"),
        wrapper_path: PathBuf::from(
            r"C:\Program Files\ProxyWarden\sing-box\ProxyWardenSingBox.exe",
        ),
        binary_exists,
        wrapper_exists,
        running,
        service_name: "ProxyWardenSingBox".to_string(),
    }
}
