use std::path::{Path, PathBuf};
use std::ffi::CStr;
use std::io;
use std::time::Duration;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use anyhow::Result;
use fuse_backend_rs::api::filesystem::{FileSystem, Entry, DirEntry, Context};
use fuse_backend_rs::abi::fuse_abi::{FsOptions, OpenOptions, statvfs64};
use libc::{stat as stat64, DT_DIR, DT_REG};

use crate::fuse::backend::BackendType;

pub struct MarkBaseFs {
    user_id: String,
    db_path: PathBuf,
    backend: BackendType,
}

struct QueryNodeResult {
    node_id: String,
    label: String,
    node_type: String,
    file_size: Option<i64>,
    parent_id: Option<String>,
    created_at: Option<i64>,
    updated_at: Option<i64>,
}

impl MarkBaseFs {
    pub fn new(user_id: String, db_path: PathBuf, backend: BackendType) -> Self {
        MarkBaseFs {
            user_id,
            db_path,
            backend,
        }
    }
    
    pub fn get_user_id(&self) -> &str {
        &self.user_id
    }
    
    pub fn get_backend(&self) -> &BackendType {
        &self.backend
    }
    
    pub fn get_db_path(&self) -> &Path {
        &self.db_path
    }
    
    pub fn mount(&self, mount_path: &Path) -> Result<()> {
        println!("=== Mounting MarkBase FUSE ===");
        println!("User: {}", self.user_id);
        println!("Database: {}", self.db_path.display());
        println!("Backend: {}", self.backend.name());
        println!("Mount path: {}", mount_path.display());
        
        Ok(())
    }
    
    pub fn uuid_to_ino(uuid: &str) -> u64 {
        let bytes = uuid.as_bytes();
        if bytes.len() >= 8 {
            u64::from_be_bytes([
                bytes[0], bytes[1], bytes[2], bytes[3],
                bytes[4], bytes[5], bytes[6], bytes[7],
            ])
        } else {
            0
        }
    }
    
    pub fn ino_to_uuid(ino: u64) -> String {
        let bytes = ino.to_be_bytes();
        format!("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", 
            bytes[0], bytes[1], bytes[2], bytes[3],
            bytes[4], bytes[5], bytes[6], bytes[7])
    }
    
    fn query_node(&self, uuid: &str) -> io::Result<QueryNodeResult> {
        use rusqlite::Connection;
        
        let conn = Connection::open(&self.db_path)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        
        conn.query_row(
            "SELECT node_id, label, node_type, file_size, parent_id, created_at, updated_at 
             FROM file_nodes 
             WHERE node_id = ?",
            [uuid],
            |row| {
                Ok(QueryNodeResult {
                    node_id: row.get::<_, String>(0)?,
                    label: row.get::<_, String>(1)?,
                    node_type: row.get::<_, String>(2)?,
                    file_size: row.get::<_, Option<i64>>(3)?,
                    parent_id: row.get::<_, Option<String>>(4)?,
                    created_at: row.get::<_, Option<i64>>(5)?,
                    updated_at: row.get::<_, Option<i64>>(6)?,
                })
            }
        ).map_err(|e| io::Error::new(io::ErrorKind::NotFound, e.to_string()))
    }
    
    fn query_children(&self, parent_uuid: &str) -> io::Result<Vec<QueryNodeResult>> {
        use rusqlite::Connection;
        
        let conn = Connection::open(&self.db_path)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        
        let mut stmt = conn.prepare(
            "SELECT node_id, label, node_type, file_size, parent_id, created_at, updated_at 
             FROM file_nodes 
             WHERE parent_id = ? 
             ORDER BY sort_order, label"
        ).map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        
        let rows = stmt.query_map([parent_uuid], |row| {
            Ok(QueryNodeResult {
                node_id: row.get::<_, String>(0)?,
                label: row.get::<_, String>(1)?,
                node_type: row.get::<_, String>(2)?,
                file_size: row.get::<_, Option<i64>>(3)?,
                parent_id: row.get::<_, Option<String>>(4)?,
                created_at: row.get::<_, Option<i64>>(5)?,
                updated_at: row.get::<_, Option<i64>>(6)?,
            })
        }).map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        
        let mut children = Vec::new();
        for row in rows {
            children.push(row.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?);
        }
        
        Ok(children)
    }
    
    fn get_file_path(&self, uuid: &str) -> io::Result<PathBuf> {
        use rusqlite::Connection;
        
        let conn = Connection::open(&self.db_path)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        
        conn.query_row(
            "SELECT location FROM file_locations WHERE file_uuid = ?",
            [uuid],
            |row| row.get::<_, String>(0)
        ).map(PathBuf::from).map_err(|e| io::Error::new(io::ErrorKind::NotFound, e.to_string()))
    }
}

impl FileSystem for MarkBaseFs {
    type Inode = u64;
    type Handle = u64;
    
    fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
        println!("MarkBaseFs::init() called - filesystem ready");
        println!("Database: {}", self.db_path.display());
        println!("User: {}", self.user_id);
        println!("Backend: {}", self.backend.name());
        Ok(FsOptions::empty())
    }
    
    fn lookup(&self, _ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<Entry> {
        let parent_uuid = Self::ino_to_uuid(parent);
        let name_str = name.to_string_lossy();
        
        let children = self.query_children(&parent_uuid)?;
        
        for child in children {
            if child.label == name_str {
                let child_ino = Self::uuid_to_ino(&child.node_id);
                let is_dir = child.node_type == "folder";
                
                let mut stat: stat64 = unsafe { std::mem::zeroed() };
                stat.st_ino = child_ino;
                stat.st_mode = if is_dir { 0o755 | libc::S_IFDIR } else { 0o644 | libc::S_IFREG };
                stat.st_nlink = if is_dir { 2 } else { 1 };
                stat.st_size = child.file_size.unwrap_or(0) as i64;
                stat.st_mtime = child.updated_at.unwrap_or(0);
                stat.st_ctime = child.created_at.unwrap_or(0);
                
                return Ok(Entry {
                    inode: child_ino,
                    generation: 0,
                    attr: stat,
                    attr_flags: 0,
                    attr_timeout: Duration::from_secs(60),
                    entry_timeout: Duration::from_secs(60),
                });
            }
        }
        
        Err(io::Error::from_raw_os_error(libc::ENOENT))
    }
    
    fn getattr(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _handle: Option<Self::Handle>,
    ) -> io::Result<(stat64, Duration)> {
        let uuid = Self::ino_to_uuid(inode);
        let node = self.query_node(&uuid)?;
        
        let is_dir = node.node_type == "folder";
        
        let mut stat: stat64 = unsafe { std::mem::zeroed() };
        stat.st_ino = inode;
        stat.st_mode = if is_dir { 0o755 | libc::S_IFDIR } else { 0o644 | libc::S_IFREG };
        stat.st_nlink = if is_dir { 2 } else { 1 };
        stat.st_size = node.file_size.unwrap_or(0) as i64;
        stat.st_mtime = node.updated_at.unwrap_or(0);
        stat.st_ctime = node.created_at.unwrap_or(0);
        
        Ok((stat, Duration::from_secs(60)))
    }
    
    fn opendir(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _flags: u32,
    ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
        Ok((Some(inode), OpenOptions::empty()))
    }
    
    fn readdir(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _handle: Self::Handle,
        _size: u32,
        offset: u64,
        add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
    ) -> io::Result<()> {
        let uuid = Self::ino_to_uuid(inode);
        let children = self.query_children(&uuid)?;
        
        for (idx, child) in children.iter().enumerate().skip(offset as usize) {
            let child_ino = Self::uuid_to_ino(&child.node_id);
            let type_ = if child.node_type == "folder" { DT_DIR } else { DT_REG };
            let name_bytes = child.label.as_bytes();
            
            let entry = DirEntry {
                ino: child_ino,
                offset: (idx + 1) as u64,
                type_: type_ as u32,
                name: name_bytes,
            };
            
            match add_entry(entry) {
                Ok(0) => break,
                Ok(_) => continue,
                Err(e) => return Err(e),
            }
        }
        
        Ok(())
    }
    
    fn releasedir(
        &self,
        _ctx: &Context,
        _inode: Self::Inode,
        _flags: u32,
        _handle: Self::Handle,
    ) -> io::Result<()> {
        Ok(())
    }
    
    fn open(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _flags: u32,
        _fuse_flags: u32,
    ) -> io::Result<(Option<Self::Handle>, OpenOptions, Option<u32>)> {
        Ok((Some(inode), OpenOptions::empty(), None))
    }
    
    fn read(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _handle: Self::Handle,
        w: &mut dyn fuse_backend_rs::api::filesystem::ZeroCopyWriter,
        size: u32,
        offset: u64,
        _lock_owner: Option<u64>,
        _flags: u32,
    ) -> io::Result<usize> {
        let uuid = Self::ino_to_uuid(inode);
        let path = self.get_file_path(&uuid)?;
        
        if !path.exists() {
            return Err(io::Error::from_raw_os_error(libc::ENOENT));
        }
        
        let mut file = File::open(&path)?;
        file.seek(SeekFrom::Start(offset))?;
        
        let mut buffer = vec![0u8; size as usize];
        let bytes_read = file.read(&mut buffer)?;
        
        w.write_all(&buffer[..bytes_read])?;
        
        Ok(bytes_read)
    }
    
    fn write(
        &self,
        _ctx: &Context,
        inode: Self::Inode,
        _handle: Self::Handle,
        r: &mut dyn fuse_backend_rs::api::filesystem::ZeroCopyReader,
        size: u32,
        offset: u64,
        _lock_owner: Option<u64>,
        _delayed_write: bool,
        _flags: u32,
        _fuse_flags: u32,
    ) -> io::Result<usize> {
        let uuid = Self::ino_to_uuid(inode);
        let path = self.get_file_path(&uuid)?;
        
        let mut file = File::create(&path)?;
        file.seek(SeekFrom::Start(offset))?;
        
        let mut buffer = vec![0u8; size as usize];
        let bytes_read = r.read(&mut buffer)?;
        
        file.write_all(&buffer[..bytes_read])?;
        
        Ok(bytes_read)
    }
    
    fn release(
        &self,
        _ctx: &Context,
        _inode: Self::Inode,
        _flags: u32,
        _handle: Self::Handle,
        _flush: bool,
        _flock_release: bool,
        _lock_owner: Option<u64>,
    ) -> io::Result<()> {
        Ok(())
    }
    
    fn statfs(&self, _ctx: &Context, _inode: Self::Inode) -> io::Result<statvfs64> {
        let mut stat: statvfs64 = unsafe { std::mem::zeroed() };
        stat.f_bsize = 4096;
        stat.f_frsize = 4096;
        stat.f_blocks = 1000000;
        stat.f_bfree = 500000;
        stat.f_bavail = 500000;
        stat.f_files = 12659;
        stat.f_ffree = 50000;
        stat.f_favail = 50000;
        Ok(stat)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_markbase_fs_creation() {
        let db_path = PathBuf::from("/tmp/test.sqlite");
        let fs = MarkBaseFs::new("test_user".to_string(), db_path, BackendType::Fskit);
        
        assert_eq!(fs.get_user_id(), "test_user");
        assert_eq!(fs.get_backend(), &BackendType::Fskit);
    }
    
    #[test]
    fn test_uuid_to_ino_conversion() {
        let uuid = "8b1ede3cd6970f02fa85b8e34b682caf";
        let ino = MarkBaseFs::uuid_to_ino(uuid);
        
        let ino2 = MarkBaseFs::uuid_to_ino(uuid);
        assert_eq!(ino, ino2);
        
        assert!(ino > 0);
    }
    
    #[test]
    fn test_mount_placeholder() {
        let db_path = PathBuf::from("/tmp/test.sqlite");
        let fs = MarkBaseFs::new("test_user".to_string(), db_path, BackendType::Nfs4);
        
        let mount_path = Path::new("/tmp/mount_test");
        let result = fs.mount(mount_path);
        
        assert!(result.is_ok());
    }
}