lla 0.2.9

A lightweight ls replacement
mod args;
mod config;
mod error;
mod filter;
mod formatter;
mod installer;
mod lister;
mod plugin;
mod sorter;
mod utils;

use args::{Args, Command, ConfigAction, InstallSource};
use config::{initialize_config, Config};
use error::{LlaError, Result};
use filter::{ExtensionFilter, FileFilter, PatternFilter};
use formatter::{DefaultFormatter, FileFormatter, LongFormatter, TreeFormatter};
use installer::PluginInstaller;
use lister::{BasicLister, FileLister, RecursiveLister};
use lla_plugin_interface::DecoratedEntry;
use plugin::PluginManager;
use sorter::{AlphabeticalSorter, DateSorter, FileSorter, SizeSorter};

use rayon::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;

fn main() -> Result<()> {
    let (config, config_error) = load_config()?;
    let args = Args::parse(&config);

    let mut plugin_manager = initialize_plugin_manager(&args, &config)?;
    plugin_manager.handle_plugin_args(&args.plugin_args);

    match args.command {
        Some(Command::Install(source)) => {
            let plugin_installer = PluginInstaller::new(&args.plugins_dir);
            match source {
                InstallSource::GitHub(url) => install_plugin_from_git(&plugin_installer, &url),
                InstallSource::LocalDir(dir) => {
                    install_plugin_from_directory(&plugin_installer, &dir)
                }
            }
        }
        Some(Command::ListPlugins) => list_plugins(&plugin_manager),
        Some(Command::InitConfig) => initialize_config(),
        Some(Command::Config(action)) => match action {
            Some(ConfigAction::View) => config::view_config(),
            Some(ConfigAction::Set(key, value)) => config::update_config(&key, &value),
            None => config::view_config(),
        },
        Some(Command::PluginAction(plugin_name, action, action_args)) => {
            plugin_manager.perform_plugin_action(&plugin_name, &action, &action_args)
        }
        None => {
            if let Some(error) = config_error {
                eprintln!("Warning: {}", error);
            }

            for plugin in &args.enable_plugin {
                if let Err(e) = plugin_manager.enable_plugin(plugin) {
                    eprintln!("Failed to enable plugin '{}': {}", plugin, e);
                }
            }

            for plugin in &args.disable_plugin {
                if let Err(e) = plugin_manager.disable_plugin(plugin) {
                    eprintln!("Failed to disable plugin '{}': {}", plugin, e);
                }
            }

            let lister = create_lister(&args);
            let sorter = create_sorter(&args);
            let filter = create_filter(&args);
            let formatter = create_formatter(&args);

            let files = list_files(&args, &lister)?;
            let sorted_files = sort_files(files, &sorter)?;
            let filtered_files = filter_files(sorted_files, &filter);

            let mut decorated_files: Vec<DecoratedEntry> = filtered_files
                .into_iter()
                .filter_map(|path| {
                    path.metadata().ok().map(|metadata| DecoratedEntry {
                        path,
                        metadata,
                        custom_fields: std::collections::HashMap::new(),
                    })
                })
                .collect();

            let format = if args.long_format {
                "long"
            } else if args.tree_format {
                "tree"
            } else {
                "default"
            };

            for entry in &mut decorated_files {
                plugin_manager.decorate_entry(entry, format);
            }

            let formatted_output =
                formatter.format_files(&decorated_files, &plugin_manager, args.depth)?;

            println!("{}", formatted_output);

            Ok(())
        }
    }
}
fn load_config() -> Result<(Config, Option<LlaError>)> {
    match Config::load(&Config::get_config_path()) {
        Ok(config) => Ok((config, None)),
        Err(e) => {
            let error = LlaError::Config(format!("Failed to load config: {}", e));
            Ok((Config::default(), Some(error)))
        }
    }
}

fn initialize_plugin_manager(args: &Args, config: &Config) -> Result<PluginManager> {
    let mut plugin_manager = PluginManager::new(config.clone());
    plugin_manager.discover_plugins(&args.plugins_dir)?;
    Ok(plugin_manager)
}

fn list_plugins(plugin_manager: &PluginManager) -> Result<()> {
    println!("Available plugins:");
    for (name, version, description) in plugin_manager.list_plugins() {
        println!("  {} v{} - {}", name, version, description);
    }
    Ok(())
}

fn create_lister(args: &Args) -> Arc<dyn FileLister + Send + Sync> {
    if args.recursive {
        Arc::new(RecursiveLister)
    } else {
        Arc::new(BasicLister)
    }
}

fn create_sorter(args: &Args) -> Arc<dyn FileSorter + Send + Sync> {
    match args.sort_by.as_str() {
        "name" => Arc::new(AlphabeticalSorter),
        "size" => Arc::new(SizeSorter),
        "date" => Arc::new(DateSorter),
        _ => Arc::new(AlphabeticalSorter),
    }
}

fn create_filter(args: &Args) -> Arc<dyn FileFilter + Send + Sync> {
    match &args.filter {
        Some(filter_str) if filter_str.starts_with('.') => {
            Arc::new(ExtensionFilter::new(filter_str[1..].to_string()))
        }
        Some(filter_str) => Arc::new(PatternFilter::new(filter_str.clone())),
        None => Arc::new(PatternFilter::new("".to_string())),
    }
}

fn create_formatter(args: &Args) -> Arc<dyn FileFormatter + Send + Sync> {
    if args.long_format {
        Arc::new(LongFormatter)
    } else if args.tree_format {
        Arc::new(TreeFormatter)
    } else {
        Arc::new(DefaultFormatter)
    }
}

fn list_files(args: &Args, lister: &Arc<dyn FileLister + Send + Sync>) -> Result<Vec<PathBuf>> {
    lister.list_files(&args.directory, args.recursive, args.depth)
}

fn sort_files(
    mut files: Vec<PathBuf>,
    sorter: &Arc<dyn FileSorter + Send + Sync>,
) -> Result<Vec<PathBuf>> {
    sorter.sort_files(&mut files)?;
    Ok(files)
}

fn filter_files(files: Vec<PathBuf>, filter: &Arc<dyn FileFilter + Send + Sync>) -> Vec<PathBuf> {
    files
        .into_par_iter()
        .filter(|file| {
            filter
                .filter_files(&[file.clone()])
                .map(|v| !v.is_empty())
                .unwrap_or(false)
        })
        .collect()
}

fn install_plugin_from_git(installer: &PluginInstaller, url: &str) -> Result<()> {
    installer.install_from_git(url)?;
    println!("Plugin(s) installed successfully from GitHub");
    Ok(())
}

fn install_plugin_from_directory(installer: &PluginInstaller, dir: &str) -> Result<()> {
    installer.install_from_directory(dir)?;
    println!("Plugin(s) installed successfully from local directory");
    Ok(())
}