//! File tree data structure for hierarchical file browser

use std::path::{
    Path,
    PathBuf,
};

/// A node in the file tree - either a file or a folder
#[derive(Debug, Clone)]
pub enum FileTreeNode {
    File {
        path: PathBuf,
        name: String,
    },
    Folder {
        name: String,
        path: String,
        children: Vec<FileTreeNode>,
    },
}

/// Build a hierarchical file tree from a list of file paths
pub fn build_tree(root: &Path, files: &[PathBuf]) -> Vec<FileTreeNode> {
    let mut root_nodes = Vec::new();

    for file_path in files {
        let relative_path = file_path.strip_prefix(root).unwrap_or(file_path);
        let components: Vec<String> = relative_path
            .iter()
            .map(|s| s.to_str().unwrap_or("").to_string())
            .collect();

        insert_path(&mut root_nodes, &components, file_path.clone(), Vec::new());
    }

    // Sort nodes: folders first, then files, both alphabetically
    sort_nodes(&mut root_nodes);
    root_nodes
}

fn insert_path(
    nodes: &mut Vec<FileTreeNode>,
    components: &[String],
    full_path: PathBuf,
    path_so_far: Vec<String>,
) {
    if components.is_empty() {
        return;
    }

    let component = &components[0];
    let is_last = components.len() == 1;

    if is_last {
        // It's a file
        nodes.push(FileTreeNode::File {
            path: full_path,
            name: component.clone(),
        });
    } else {
        // It's a folder - find or create it
        let folder_path = if path_so_far.is_empty() {
            component.clone()
        } else {
            format!("{}/{}", path_so_far.join("/"), component)
        };

        if let Some(folder_node) = nodes
            .iter_mut()
            .find(|n| matches!(n, FileTreeNode::Folder { name, .. } if name == component))
        {
            // Folder exists, insert into its children
            if let FileTreeNode::Folder { children, .. } = folder_node {
                let mut new_path = path_so_far.clone();
                new_path.push(component.clone());
                insert_path(children, &components[1..], full_path, new_path);
            }
        } else {
            // Create new folder
            let mut children = Vec::new();
            let mut new_path = path_so_far.clone();
            new_path.push(component.clone());
            insert_path(&mut children, &components[1..], full_path, new_path.clone());

            nodes.push(FileTreeNode::Folder {
                name: component.clone(),
                path: folder_path,
                children,
            });
        }
    }
}

fn sort_nodes(nodes: &mut Vec<FileTreeNode>) {
    nodes.sort_by(|a, b| match (a, b) {
        | (
            FileTreeNode::Folder { name: a_name, .. },
            FileTreeNode::Folder { name: b_name, .. },
        ) => a_name.cmp(b_name),
        | (FileTreeNode::File { name: a_name, .. }, FileTreeNode::File { name: b_name, .. }) => {
            a_name.cmp(b_name)
        },
        | (FileTreeNode::Folder { .. }, FileTreeNode::File { .. }) => std::cmp::Ordering::Less,
        | (FileTreeNode::File { .. }, FileTreeNode::Folder { .. }) => std::cmp::Ordering::Greater,
    });

    // Recursively sort children
    for node in nodes.iter_mut() {
        if let FileTreeNode::Folder { children, .. } = node {
            sort_nodes(children);
        }
    }
}
