use std::collections::HashMap;
use std::io;
use std::path::PathBuf;
use std::sync::Mutex;

use rusqlite::Connection;
use vfs::{FileSystem, VfsMetadata, VfsResult, VfsFileType, SeekAndRead, SeekAndWrite};
use vfs::error::VfsErrorKind;

fn rusqlite_to_io_error(e: rusqlite::Error) -> io::Error {
    io::Error::new(io::ErrorKind::Other, e.to_string())
}

#[derive(Debug)]
pub struct MarkBaseFS {
    user_id: String,
    db_path: PathBuf,
    conn: Mutex<Connection>,
    path_cache: Mutex<HashMap<String, String>>,
}

struct FileNode {
    node_id: String,
    label: String,
    node_type: String,
    parent_id: Option<String>,
    aliases_json: Option<String>,
    file_size: Option<i64>,
}

impl MarkBaseFS {
    pub fn new(user_id: String, db_path: PathBuf) -> VfsResult<Self> {
        let conn = Connection::open(&db_path)
            .map_err(|e| VfsErrorKind::IoError(rusqlite_to_io_error(e)))?;
        
        Ok(MarkBaseFS {
            user_id,
            db_path,
            conn: Mutex::new(conn),
            path_cache: Mutex::new(HashMap::new()),
        })
    }
    
    fn resolve_path(&self, path: &str) -> VfsResult<FileNode> {
        if path == "" || path == "/" {
            return Ok(FileNode {
                node_id: "root".to_string(),
                label: "".to_string(),
                node_type: "folder".to_string(),
                parent_id: None,
                aliases_json: None,
                file_size: None,
            });
        }
        
        let conn = self.conn.lock()
            .map_err(|_| VfsErrorKind::Other("Failed to lock connection".to_string()))?;
        
        let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
        
        let mut current_parent: Option<String> = None;
        let mut current_node: Option<FileNode> = None;
        
        for part in parts {
            let query = if current_parent.is_none() {
                "SELECT node_id, label, node_type, parent_id, aliases_json, file_size 
                 FROM file_nodes 
                 WHERE parent_id IS NULL AND label = ?1"
            } else {
                "SELECT node_id, label, node_type, parent_id, aliases_json, file_size 
                 FROM file_nodes 
                 WHERE parent_id = ?1 AND label = ?2"
            };
            
            let mut stmt = conn.prepare(query)
                .map_err(|e| VfsErrorKind::IoError(rusqlite_to_io_error(e)))?;
            
            let node = if current_parent.is_none() {
                stmt.query_row([part], |row| {
                    Ok(FileNode {
                        node_id: row.get(0)?,
                        label: row.get(1)?,
                        node_type: row.get(2)?,
                        parent_id: row.get(3)?,
                        aliases_json: row.get(4)?,
                        file_size: row.get(5)?,
                    })
                }).map_err(|e| rusqlite_to_io_error(e))
            } else {
                let part_str = part.to_string();
                stmt.query_row([current_parent.clone().unwrap(), part_str], |row| {
                    Ok(FileNode {
                        node_id: row.get(0)?,
                        label: row.get(1)?,
                        node_type: row.get(2)?,
                        parent_id: row.get(3)?,
                        aliases_json: row.get(4)?,
                        file_size: row.get(5)?,
                    })
                }).map_err(|e| rusqlite_to_io_error(e))
            };
            
            match node {
                Ok(n) => {
                    current_parent = Some(n.node_id.clone());
                    current_node = Some(n);
                }
                Err(_) => return Err(VfsErrorKind::FileNotFound.into()),
            }
        }
        
        current_node.ok_or(VfsErrorKind::FileNotFound.into())
    }
}

impl FileSystem for MarkBaseFS {
    fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
        let conn = self.conn.lock()
            .map_err(|_| VfsErrorKind::Other("Failed to lock connection".to_string()))?;
        
        let parent_id = if path == "" || path == "/" {
            None
        } else {
            let node = self.resolve_path(path)?;
            Some(node.node_id)
        };
        
        let query = if parent_id.is_none() {
            "SELECT label FROM file_nodes WHERE parent_id IS NULL"
        } else {
            "SELECT label FROM file_nodes WHERE parent_id = ?1"
        };
        
        let mut stmt = conn.prepare(query)
            .map_err(|e| VfsErrorKind::IoError(rusqlite_to_io_error(e)))?;
        
        let children: Vec<String> = if parent_id.is_none() {
            stmt.query_map([], |row| row.get::<_, String>(0))
                .map_err(|e| rusqlite_to_io_error(e))?
                .collect::<Result<Vec<_>, _>>()
                .map_err(|e| rusqlite_to_io_error(e))?
        } else {
            stmt.query_map([parent_id.unwrap()], |row| row.get::<_, String>(0))
                .map_err(|e| rusqlite_to_io_error(e))?
                .collect::<Result<Vec<_>, _>>()
                .map_err(|e| rusqlite_to_io_error(e))?
        };
        
        Ok(Box::new(children.into_iter()))
    }
    
    fn create_dir(&self, _path: &str) -> VfsResult<()> {
        Err(VfsErrorKind::NotSupported.into())
    }
    
    fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
        let node = self.resolve_path(path)?;
        
        if node.node_type != "file" {
            return Err(VfsErrorKind::InvalidPath.into());
        }
        
        let aliases_json = node.aliases_json.ok_or(VfsErrorKind::FileNotFound)?;
        let aliases: serde_json::Value = serde_json::from_str(&aliases_json)
            .map_err(|e| VfsErrorKind::IoError(io::Error::new(io::ErrorKind::Other, e.to_string())))?;
        
        let file_path = aliases["path"].as_str().ok_or(VfsErrorKind::FileNotFound)?;
        
        let file = std::fs::File::open(file_path)?;
        
        Ok(Box::new(file))
    }
    
    fn create_file(&self, _path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
        Err(VfsErrorKind::NotSupported.into())
    }
    
    fn append_file(&self, _path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
        Err(VfsErrorKind::NotSupported.into())
    }
    
    fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
        let node = self.resolve_path(path)?;
        
        let file_type = if node.node_type == "folder" {
            VfsFileType::Directory
        } else {
            VfsFileType::File
        };
        
        let len = node.file_size.unwrap_or(0) as u64;
        
        Ok(VfsMetadata {
            file_type,
            len,
            created: None,
            modified: None,
            accessed: None,
        })
    }
    
    fn exists(&self, path: &str) -> VfsResult<bool> {
        match self.resolve_path(path) {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }
    
    fn remove_file(&self, _path: &str) -> VfsResult<()> {
        Err(VfsErrorKind::NotSupported.into())
    }
    
    fn remove_dir(&self, _path: &str) -> VfsResult<()> {
        Err(VfsErrorKind::NotSupported.into())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use vfs::FileSystem;
    
    #[test]
    fn test_markbase_fs_creation() {
        let fs = MarkBaseFS::new(
            "warren".to_string(),
            PathBuf::from("data/users/warren.sqlite"),
        );
        assert!(fs.is_ok());
    }
    
    #[test]
    fn test_resolve_root() {
        let fs = MarkBaseFS::new(
            "warren".to_string(),
            PathBuf::from("data/users/warren.sqlite"),
        ).unwrap();
        
        let node = fs.resolve_path("");
        assert!(node.is_ok());
        assert_eq!(node.unwrap().node_type, "folder");
    }
}