use anyhow::Result;
use log::{info, warn};
use std::{
    fs,
    io::Write,
    os::unix::{
        fs::{symlink, PermissionsExt},
        process::CommandExt,
    },
    path::Path,
    process::{Command, Stdio},
};

pub fn start_uid_scanner_daemon() -> Result<()> {
    const SCANNER_PATH: &str = "/data/adb/uid_scanner";
    const LINK_DIR: &str = "/data/adb/ksu/bin";
    const LINK_PATH: &str = "/data/adb/ksu/bin/uid_scanner";
    const SERVICE_DIR: &str = "/data/adb/service.d";
    const SERVICE_PATH: &str = "/data/adb/service.d/uid_scanner.sh";

    if !Path::new(SCANNER_PATH).exists() {
        warn!("uid scanner binary not found at {}", SCANNER_PATH);
        return Ok(());
    }

    if let Err(e) = fs::set_permissions(SCANNER_PATH, fs::Permissions::from_mode(0o755)) {
        warn!("failed to set permissions for {}: {}", SCANNER_PATH, e);
    }

    #[cfg(unix)]
    {
        if let Err(e) = fs::create_dir_all(LINK_DIR) {
            warn!("failed to create {}: {}", LINK_DIR, e);
        } else if !Path::new(LINK_PATH).exists() {
            match symlink(SCANNER_PATH, LINK_PATH) {
                Ok(_) => info!("created symlink {} -> {}", SCANNER_PATH, LINK_PATH),
                Err(e) => warn!("failed to create symlink: {}", e),
            }
        }
    }

    if let Err(e) = fs::create_dir_all(SERVICE_DIR) {
        warn!("failed to create {}: {}", SERVICE_DIR, e);
    } else if !Path::new(SERVICE_PATH).exists() {
        let content = r#"#!/system/bin/sh
# KSU uid_scanner auto-restart script
until [ -d "/sdcard/Android" ]; do sleep 1; done
sleep 10
/data/adb/uid_scanner restart
"#;
        match fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(SERVICE_PATH)
            .and_then(|mut f| {
                f.write_all(content.as_bytes())?;
                f.sync_all()?;
                fs::set_permissions(SERVICE_PATH, fs::Permissions::from_mode(0o755))
            }) {
            Ok(_) => info!("created service script {}", SERVICE_PATH),
            Err(e) => warn!("failed to write {}: {}", SERVICE_PATH, e),
        }
    }

    info!("starting uid scanner daemon with highest priority");
    let mut cmd = Command::new(SCANNER_PATH);
    cmd.arg("start")
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .current_dir("/");

    unsafe {
        cmd.pre_exec(|| {
            libc::nice(-20);
            libc::setsid();
            Ok(())
        });
    }

    match cmd.spawn() {
        Ok(child) => {
            info!("uid scanner daemon started with pid: {}", child.id());
            std::mem::drop(child);
        }
        Err(e) => warn!("failed to start uid scanner daemon: {}", e),
    }

    Ok(())
}
