use std::arch::asm;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::os::fd::AsRawFd;
use std::os::unix::fs::PermissionsExt;
use nix::libc::{self, setgid, CLONE_NEWCGROUP, MS_NODEV, MS_NOSUID};
use nix::sched::{clone, CloneCb, CloneFlags};
use nix::sys::wait::{wait, waitpid, waitid, WaitPidFlag};
use nix::unistd::{chdir, chroot, dup2, execv, pivot_root, setuid, sleep, Gid, Pid, Uid, User, setgroups};
use nix::mount::{mount, MntFlags, MsFlags, umount2, umount};
use nix::env::clearenv;
use std::path::{Path, PathBuf};
use clap::Parser;
use error::{Result, RockerError};
mod error;
use uuid;
use std::{io, fs};
use toml;
use serde::{Deserialize, Serialize};
use std::fmt::Display;

static WORKSPACE: &str = "/root/rocker";
static USER_NAME: &str = "rocker";
static INFO_FILE: &str = "info.toml";
static LOCK_FILE: &str = ".lock";
static mut STACK: [u8; 1024*1024*1] = [0; 1024*1024*1];
static CLONE_FLAG: i32 = 0b1101100000000100000000000000000;  // CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNET;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct RockerArgs {
    // --wait/--log --run /bin/bash --image busybox 
    #[arg(long)]
    run: Option<String>,
    #[arg(long)]
    image: Option<String>,

    #[arg(long)]
    log: bool,
    #[arg(long)]
    wait: bool,

    // --logs container_id
    #[arg(long)]
    logs: Option<String>,

    // --ps
    #[arg(long)]
    ps: bool,
}


/// 从images解压到volumes
fn extend_image(image_name: &String) -> Result<(PathBuf)> {
    // 源文件
    let image_path = Path::new(WORKSPACE).join("images").join(image_name);
    if image_path.exists() == false {
        return Err(RockerError::from(io::Error::new(io::ErrorKind::NotFound, "未找到镜像")));
    }
    let image_path_str = image_path.to_str().unwrap();  // 安全的unwrap

    // volumes只读层
    let volume_path = Path::new(WORKSPACE).join("volumes").join(image_name);
    if volume_path.exists() {
        return Ok(volume_path);
    } else {
        create_dir_and_set777(&volume_path)?;
    }
    let volume_path_str = volume_path.to_str().unwrap();  // 安全的unwrap
    
    // 解压缩
    let out = std::process::Command::new("tar")
        .arg("-xvf")
        .arg(image_path_str)
        .arg("-C")
        .arg(volume_path_str)
        .output()?;
    let std_out = String::from_utf8_lossy(&out.stdout);
    let std_err = String::from_utf8_lossy(&out.stderr);
    if std_err.len() == 0 {
        println!("解压缩完毕: {std_out:?}");
        Ok(volume_path)
    } else {
        // 删除 volume_path
        std::fs::remove_dir_all(volume_path)?;
        Err(RockerError::from(io::Error::new(io::ErrorKind::Other, format!("解压缩镜像失败: {std_err}"))))
    }
}

fn init_container_lock<P: AsRef<Path>>(container_work_path:P) -> Result<i32> {
    use nix::sys::stat::Mode;
    use nix::fcntl::{OFlag, open};
    let lock_path = container_work_path.as_ref().join(LOCK_FILE);
    let lock_path_str = lock_path.as_os_str();
    let oflag = OFlag::O_RDWR | OFlag::O_CREAT;
    let mode = Mode::empty();
    Ok(open(lock_path_str, oflag, mode)?)
}

fn init_container_overlay<P: AsRef<Path>>(volume_path: P, upper_path: P, merged_path: P) -> Result<()> {
    let lower_dir = volume_path.as_ref().to_string_lossy().to_string();
    let upper_dir = upper_path.as_ref().to_string_lossy().to_string();
    let merged_dir = merged_path.as_ref().to_string_lossy().to_string();

    let dirs = format!("lowerdir={lower_dir},upperdir={upper_dir},workdir={merged_dir}");
    println!("dirs: {dirs:?}");
    let out = std::process::Command::new("mount")
        .arg("-t")
        .arg("overlay")
        .arg("overlay")
        .arg("-o")
        .arg(dirs)
        .arg(merged_dir)
        .output()?;
    
    let std_out = String::from_utf8_lossy(&out.stdout);
    let std_err = String::from_utf8_lossy(&out.stderr);

    if std_err.len() == 0 {
        println!("容器文件系统创建完成: {std_out:?}");
    } else {
        return Err(RockerError::from(io::Error::new(io::ErrorKind::Other, format!("容器文件系统创建失败: {std_err:?}"))));
    }
    Ok(())
}

fn init_container_pivot<P: AsRef<Path>>(merged_path: P) -> Result<()> {
    // 在我们没有设置 chroot之前, 需要先把所有挂载点的传播类型改为 private, 避免进程中的系统调用污染全局
    mount(None::<&str>, "/", None::<&str>, MsFlags::MS_PRIVATE | MsFlags::MS_REC, None::<&str>)?;

    // 修改overlayfs 为rootfs
    chdir(merged_path.as_ref())?;
    let pwd_path = std::env::current_dir()?;
    let pwd_str = pwd_path.to_string_lossy().to_string();

    // 挂载bind
    mount(Some(pwd_str.as_str()), pwd_str.as_str(), Some("bind"), MsFlags::MS_BIND | MsFlags::MS_REC, Some(""))?;
    
    // 创建 rootfs/.pivot_root 目录用于存储 old_root
    let pivot_root_dir = format!("{pwd_str}/.pivot_root");

    // 将系统rootfs切换到新的rootfs, 并设置权限
    std::fs::create_dir(&pivot_root_dir)?;
    std::fs::set_permissions(&pivot_root_dir, PermissionsExt::from_mode(0o777))?;
    pivot_root(pwd_str.as_str(), pivot_root_dir.as_str())?;

    // 修改当前进程工作目录(注意我们之前已经到rootfs内, 并且把根目录设置完毕了)
    chdir("/")?;

    // 卸载 old_root, 并删除临时文件
    umount2(".pivot_root", MntFlags::MNT_DETACH).unwrap();
    std::fs::remove_dir(".pivot_root").unwrap();
    Ok(())
}


fn init_container_mount() -> Result<()> {
    // 挂载proc
    let mount_flags = MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID;
    mount(Some("proc"), "/proc", Some("proc"), mount_flags, Some(""))?;  

    // 挂载dev
    mount(Some("tmpfs"), "/dev", Some("tmpfs"), mount_flags, Some("mode=755"))?;  
    Ok(())
}

fn init_container_log(log: bool) -> Result<()> {
    let log_path = Path::new("logs");
    create_dir_and_set777(log_path)?;
    let log_fd = File::create(log_path.join("log"))?;
    let log_fd_raw = log_fd.as_raw_fd();
    if log {
        unsafe {
            dup2(log_fd_raw, 1)?;
            dup2(log_fd_raw, 2)?;
        }
    }
    Ok(())
}

fn init_container_env() -> Result<()> {
    unsafe {
        clearenv().map_err(|e|RockerError::OtherError(format!("清除env失败: {e:?}")))?;
    }
    Ok(())
}
fn init_container_user(uid: Uid, gid: Gid) -> Result<()>{
    unsafe {
        setgid(gid.as_raw());
        setgroups(&[gid])?;
    }
    setuid(uid)?;
    Ok(())
}

fn create_dir_and_set777<P: AsRef<Path>>(path: P) -> Result<()> {
    fs::create_dir_all(&path)?;
    fs::set_permissions(&path, PermissionsExt::from_mode(0o777))?;
    Ok(())
}


fn parse_cmd(run: &String) -> Vec<CString>{
    let args= run
        .split(" ")
        .filter_map(|s| {
            match CString::new(s) {
                Ok(cs) => {
                    Some(cs)
                }
                Err(e) => {
                    println!("{e:?}");
                    None
                }
            }
        })
        .collect::<Vec<CString>>();
    args
}


fn check_container_is_running(pid: &Pid, main_exe: &Path) -> Result<bool> {
    // 检查pid对应的exe是否和外部传过来的相同
    let child_exe_s= format!("/proc/{pid}/exe");
    let child_exe_path = Path::new(child_exe_s.as_str());
    let target_child_exe_path = fs::read_link(child_exe_path)?;
    if target_child_exe_path != main_exe {
        std::thread::sleep(std::time::Duration::from_millis(10));
        return Ok(true);
    }

    Ok(false)
}

fn run_container(cmd: &String, args: &RockerArgs, volume_path: &PathBuf) -> Result<(String, i32)> {
    // 禁止同时wait和log
    if args.wait && args.log {
        return Err(RockerError::OtherError("--wait/--log 禁止同时使用".to_string()));
    }

    // 初始化容器工作目录
    let _container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string();
    let container_work_path = Path::new(WORKSPACE).join("containers").join(&_container_id);
    let container_upper_path = container_work_path.join("upper");
    let container_merged_path = container_work_path.join("merged");
    create_dir_and_set777(&container_work_path)?;
    create_dir_and_set777(&container_upper_path)?;
    create_dir_and_set777(&container_merged_path)?;

    let rocker_user_info =  User::from_name(USER_NAME)?.ok_or(RockerError::OtherError(format!("没找到 用户: {USER_NAME}")))?;
    let rocker_uid = rocker_user_info.uid;
    let rocker_gid = rocker_user_info.gid;

    let _cb = || {
        init_container_lock(&container_work_path).unwrap();
        init_container_overlay(volume_path, &container_upper_path, &container_merged_path).unwrap();
        init_container_pivot(&container_merged_path).unwrap();
        init_container_mount().unwrap();
        init_container_log(args.log).unwrap();
        init_container_env().unwrap();
        init_container_user(rocker_uid, rocker_gid).unwrap();
        
        let cmd_vec = parse_cmd(cmd);
        execv(&cmd_vec[0], &cmd_vec).unwrap();
        0
    };
    
    let main_exe = std::env::current_exe()?;
    unsafe {
        match clone(Box::new(_cb), STACK.as_mut_slice(), CloneFlags::from_bits_truncate(CLONE_FLAG), None) {
            Ok(child_pid) =>  {
                println!("clone ok: {child_pid:?}");

                // check_container_is_running
                while let Ok(running) = check_container_is_running(&child_pid, &main_exe) {
                    if running {
                        break;
                    }
                }
                
                // wait
                if args.wait {
                    match waitpid(child_pid, Some(WaitPidFlag::WUNTRACED)) {
                        Ok(status) => {
                            println!("{child_pid:?} exit: {status:?}");
                        }
                        Err(e) => {
                            return Err(RockerError::OtherError(format!("{child_pid} err: {e}")));
                        }
                    }
                }
                Ok((_container_id, child_pid.as_raw()))
            }
            Err(e) => {
                return Err(RockerError::OtherError(format!("clone err: {e}")));
            }
        }
    }
}

#[derive(Deserialize, Serialize, Debug)]
struct ContainerInfo {
    id: String,
    pid: i32,
    run: String,
    image: String,
    volume: String,
    env: String,
    status: String,
}

impl Display for ContainerInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let volume: String = self.volume.chars().take(20).collect();
        let env: String = self.env.chars().take(20).collect();
        write!(f, "{:<10}{:<8}{:<10}{:<20}{:<20}{:<20}{:<10}", self.id, self.pid, self.image, self.run, volume, env, &self.status)
    }
}

fn save_container_info(args: &RockerArgs, container_id: &String, pid: i32) -> Result<()> {
    let container_info_path = Path::new(WORKSPACE).join("containers").join(container_id).join(INFO_FILE);
    let container_info = ContainerInfo {
        id: container_id.clone(),
        pid: pid,
        run: args.run.as_ref().unwrap().clone(),
        image: args.image.as_ref().unwrap().clone(),
        volume: "".to_string(),
        env: "".to_string(),
        status: "".to_string(),
    };
    let toml_str = toml::to_string(&container_info)?;
    fs::write(container_info_path, toml_str)?;
    Ok(())
}

/// 读取所有容器的状态
fn show_containers() -> Result<()> {
    let containers_path = Path::new(WORKSPACE).join("containers");
    
    println!("{:<10}{:<8}{:<10}{:<20}{:<20}{:<20}{:<10}", "id", "pid", "image", "run", "volume", "env", "status");
    for entry in fs::read_dir(containers_path)? {
        let path = entry?.path();
        let info_path = path.join(INFO_FILE);
        let info_str = fs::read_to_string(info_path)?;
        let container_info: ContainerInfo = toml::from_str(&info_str)?;
        println!("{}", container_info);
    }
    Ok(())
}


fn main() -> Result<()>{
    let args = RockerArgs::parse();
    // run
    if let (Some(cmd), Some(image_name)) = (&args.run, &args.image) {
        let volume_path = extend_image(image_name)?;
        let (container_id, pid) = run_container(cmd, &args, &volume_path)?;
        save_container_info(&args, &container_id, pid)?;
    } else if args.ps {
        show_containers()?
    }

    // exec

    // logs

    // rm


    Ok(())
}