//! /proc/sys/kernel - 内核参数目录
//!
//! 提供内核参数配置接口

use crate::{
    debug::klog::loglevel::KERNEL_LOG_LEVEL,
    filesystem::{
        procfs::{
            template::{Builder, DirOps, FileOps, ProcDir, ProcDirBuilder, ProcFileBuilder},
            utils::proc_read,
        },
        vfs::{FilePrivateData, IndexNode, InodeMode},
    },
    libs::mutex::MutexGuard,
    process::{namespace::uts_namespace::NewUtsName, ProcessManager},
};
use alloc::{
    format,
    string::{String, ToString},
    sync::{Arc, Weak},
    vec::Vec,
};
use core::sync::atomic::{AtomicU32, Ordering};
use system_error::SystemError;

static OVERFLOW_UID: AtomicU32 = AtomicU32::new(DEFAULT_OVERFLOW_ID);
static OVERFLOW_GID: AtomicU32 = AtomicU32::new(DEFAULT_OVERFLOW_ID);

const DEFAULT_OVERFLOW_ID: u32 = 65534;
const MAX_OVERFLOW_ID: u32 = 65535;

/// /proc/sys/kernel 目录的 DirOps 实现
#[derive(Debug)]
pub struct KernelDirOps;

impl KernelDirOps {
    pub fn new_inode(parent: Weak<dyn IndexNode>) -> Arc<dyn IndexNode> {
        ProcDirBuilder::new(Self, InodeMode::from_bits_truncate(0o555))
            .parent(parent)
            .build()
            .unwrap()
    }
}

impl DirOps for KernelDirOps {
    fn lookup_child(
        &self,
        dir: &ProcDir<Self>,
        name: &str,
    ) -> Result<Arc<dyn IndexNode>, SystemError> {
        match name {
            "printk" => {
                let mut cached_children = dir.cached_children().write();
                if let Some(child) = cached_children.get(name) {
                    return Ok(child.clone());
                }

                let inode = PrintkFileOps::new_inode(dir.self_ref_weak().clone());
                cached_children.insert(name.to_string(), inode.clone());
                return Ok(inode);
            }
            "overflowuid" | "overflowgid" => {
                let mut cached_children = dir.cached_children().write();
                if let Some(child) = cached_children.get(name) {
                    return Ok(child.clone());
                }

                let kind = if name == "overflowuid" {
                    OverflowIdKind::Uid
                } else {
                    OverflowIdKind::Gid
                };
                let inode = OverflowIdFileOps::new_inode(dir.self_ref_weak().clone(), kind);
                cached_children.insert(name.to_string(), inode.clone());
                return Ok(inode);
            }
            "hostname" | "domainname" => {
                let mut cached_children = dir.cached_children().write();
                if let Some(child) = cached_children.get(name) {
                    return Ok(child.clone());
                }

                let kind = if name == "hostname" {
                    UtsStringKind::Hostname
                } else {
                    UtsStringKind::Domainname
                };
                let inode = UtsStringFileOps::new_inode(dir.self_ref_weak().clone(), kind);
                cached_children.insert(name.to_string(), inode.clone());
                return Ok(inode);
            }
            _ => Err(SystemError::ENOENT),
        }
    }

    fn populate_children(&self, dir: &ProcDir<Self>) {
        let mut cached_children = dir.cached_children().write();
        cached_children
            .entry("printk".to_string())
            .or_insert_with(|| PrintkFileOps::new_inode(dir.self_ref_weak().clone()));
        cached_children
            .entry("overflowuid".to_string())
            .or_insert_with(|| {
                OverflowIdFileOps::new_inode(dir.self_ref_weak().clone(), OverflowIdKind::Uid)
            });
        cached_children
            .entry("overflowgid".to_string())
            .or_insert_with(|| {
                OverflowIdFileOps::new_inode(dir.self_ref_weak().clone(), OverflowIdKind::Gid)
            });
        cached_children
            .entry("hostname".to_string())
            .or_insert_with(|| {
                UtsStringFileOps::new_inode(dir.self_ref_weak().clone(), UtsStringKind::Hostname)
            });
        cached_children
            .entry("domainname".to_string())
            .or_insert_with(|| {
                UtsStringFileOps::new_inode(dir.self_ref_weak().clone(), UtsStringKind::Domainname)
            });
    }
}

#[derive(Debug, Clone, Copy)]
enum UtsStringKind {
    Hostname,
    Domainname,
}

#[derive(Debug)]
struct UtsStringFileOps {
    kind: UtsStringKind,
}

impl UtsStringFileOps {
    fn new_inode(parent: Weak<dyn IndexNode>, kind: UtsStringKind) -> Arc<dyn IndexNode> {
        ProcFileBuilder::new(Self { kind }, InodeMode::from_bits_truncate(0o644))
            .parent(parent)
            .build()
            .unwrap()
    }

    fn snapshot(&self) -> [u8; NewUtsName::MAXLEN] {
        let uts_ns = ProcessManager::current_utsns();
        match self.kind {
            UtsStringKind::Hostname => uts_ns.hostname(),
            UtsStringKind::Domainname => uts_ns.domainname(),
        }
    }
}

impl FileOps for UtsStringFileOps {
    fn read_at(
        &self,
        offset: usize,
        len: usize,
        buf: &mut [u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        let value = self.snapshot();
        let value_len = value
            .iter()
            .position(|byte| *byte == 0)
            .unwrap_or(value.len());
        let mut content = Vec::with_capacity(value_len + 1);
        content.extend_from_slice(&value[..value_len]);
        content.push(b'\n');
        proc_read(offset, len, buf, &content)
    }

    fn write_at(
        &self,
        offset: usize,
        _len: usize,
        buf: &[u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        if buf.is_empty() {
            return Ok(0);
        }

        let uts_ns = ProcessManager::current_utsns();
        let old = match self.kind {
            UtsStringKind::Hostname => uts_ns.hostname(),
            UtsStringKind::Domainname => uts_ns.domainname(),
        };
        let old_len = old.iter().position(|byte| *byte == 0).unwrap_or(old.len());
        if offset > old_len {
            return Ok(buf.len());
        }

        let input_len = buf
            .iter()
            .position(|byte| *byte == 0 || *byte == b'\n')
            .unwrap_or(buf.len());
        let copy_len = input_len.min((NewUtsName::MAXLEN - 1).saturating_sub(offset));
        let mut value = [0u8; NewUtsName::MAXLEN];
        value[..offset].copy_from_slice(&old[..offset]);
        value[offset..offset + copy_len].copy_from_slice(&buf[..copy_len]);
        match self.kind {
            UtsStringKind::Hostname => uts_ns.set_hostname(&value[..offset + copy_len])?,
            UtsStringKind::Domainname => uts_ns.set_domainname(&value[..offset + copy_len])?,
        }
        Ok(buf.len())
    }
}

/// /proc/sys/kernel/printk 文件的 FileOps 实现
#[derive(Debug)]
pub struct PrintkFileOps;

impl PrintkFileOps {
    pub fn new_inode(parent: Weak<dyn IndexNode>) -> Arc<dyn IndexNode> {
        ProcFileBuilder::new(Self, InodeMode::from_bits_truncate(0o644))
            .parent(parent)
            .build()
            .unwrap()
    }

    /// 读取当前的内核日志级别配置
    fn read_config() -> String {
        let levels = KERNEL_LOG_LEVEL.get_all_levels();
        format!(
            "{}\t{}\t{}\t{}\n",
            levels[0], levels[1], levels[2], levels[3]
        )
    }

    /// 写入内核日志级别配置
    fn write_config(data: &[u8]) -> Result<usize, SystemError> {
        let input = core::str::from_utf8(data).map_err(|_| SystemError::EINVAL)?;
        let parts: Vec<&str> = input.split_whitespace().collect();

        if parts.is_empty() {
            return Err(SystemError::EINVAL);
        }

        // 只处理第一个值（控制台日志级别）
        if let Ok(level) = parts[0].parse::<u8>() {
            KERNEL_LOG_LEVEL.set_console_level(level)?;
            log::info!(
                "sysctl: set console log level to {} via /proc/sys/kernel/printk",
                level
            );
            Ok(data.len())
        } else {
            log::warn!("sysctl: invalid loglevel value '{}'", parts[0]);
            Err(SystemError::EINVAL)
        }
    }
}

impl FileOps for PrintkFileOps {
    fn read_at(
        &self,
        offset: usize,
        len: usize,
        buf: &mut [u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        let content = Self::read_config();
        proc_read(offset, len, buf, content.as_bytes())
    }

    fn write_at(
        &self,
        _offset: usize,
        _len: usize,
        buf: &[u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        Self::write_config(buf)
    }
}

#[derive(Debug)]
pub struct OverflowIdFileOps {
    which: OverflowIdKind,
}

#[derive(Debug, Clone, Copy)]
enum OverflowIdKind {
    Uid,
    Gid,
}

impl OverflowIdFileOps {
    fn new_inode(parent: Weak<dyn IndexNode>, which: OverflowIdKind) -> Arc<dyn IndexNode> {
        ProcFileBuilder::new(Self { which }, InodeMode::from_bits_truncate(0o644))
            .parent(parent)
            .build()
            .unwrap()
    }
}

impl FileOps for OverflowIdFileOps {
    fn read_at(
        &self,
        offset: usize,
        len: usize,
        buf: &mut [u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        let val = match self.which {
            OverflowIdKind::Uid => OVERFLOW_UID.load(Ordering::Relaxed),
            OverflowIdKind::Gid => OVERFLOW_GID.load(Ordering::Relaxed),
        };
        let content = alloc::format!("{}\n", val);
        proc_read(offset, len, buf, content.as_bytes())
    }

    fn write_at(
        &self,
        offset: usize,
        _len: usize,
        buf: &[u8],
        _data: MutexGuard<FilePrivateData>,
    ) -> Result<usize, SystemError> {
        // offset > 0 时静默忽略写入，返回成功但数据不生效。
        if offset > 0 {
            return Ok(buf.len());
        }

        let input = core::str::from_utf8(buf).map_err(|_| SystemError::EINVAL)?;
        let val: u32 = input.trim().parse().map_err(|_| SystemError::EINVAL)?;
        if val > MAX_OVERFLOW_ID {
            return Err(SystemError::EINVAL);
        }
        match self.which {
            OverflowIdKind::Uid => OVERFLOW_UID.store(val, Ordering::Relaxed),
            OverflowIdKind::Gid => OVERFLOW_GID.store(val, Ordering::Relaxed),
        }
        Ok(buf.len())
    }
}
