// Verwende nun die Module
mod cli;
mod config;
mod sanitizer;

use anyhow::{Context, Result};
use clap::Parser;
use cli::Cli;
use colored::*;
use config::Config;
use glob::Pattern;
use indicatif::{ProgressBar, ProgressStyle};
use log::{debug, error, info};
use rayon::prelude::*;
use sanitizer::{clean_filename, is_excluded, is_safe_rename};
use std::fs;
use std::io::IsTerminal;
use std::path::PathBuf;
use walkdir::WalkDir;

// Standard-Ausschlussmuster (ähnlich wie detox)
const DEFAULT_EXCLUDES: &[&str] = &[
    ".git",
    ".git/**",
    ".svn",
    ".svn/**",
    "node_modules",
    "node_modules/**",
    ".cache",
    ".cache/**",
    "__pycache__",
    "__pycache__/**",
];

// Schwellwert für parallele Verarbeitung (bei weniger Dateien lohnt sich Overhead nicht)
const PARALLEL_THRESHOLD: usize = 100;

/// Repräsentiert eine geplante Umbenennungsoperation
#[derive(Debug)]
struct RenameOperation {
    old_path: PathBuf,
    new_path: PathBuf,
}

/// Prüft ob farbige Ausgabe aktiviert sein soll
fn should_use_color(no_color_flag: bool) -> bool {
    !no_color_flag && std::io::stdout().is_terminal()
}

/// Startpunkt des Programms
fn main() -> Result<()> {
    // Initialisiere Logger
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();

    // Argumente parsen
    let args = Cli::parse();

    // Farben konfigurieren
    if !should_use_color(args.no_color) {
        colored::control::set_override(false);
    }

    // Optional Konfigurationsdatei laden
    let config = Config::from_default_locations(args.verbose)?;
    // let config = Config::load(".NameToUnix.conf", args.verbose)?;

    // Ausschlussmuster (Glob-Patterns) vorbereiten - Default-Excludes + User-Excludes
    let mut all_excludes = DEFAULT_EXCLUDES
        .iter()
        .map(|s| s.to_string())
        .collect::<Vec<_>>();
    all_excludes.extend(args.exclude.clone());

    let exclude_patterns = all_excludes
        .iter()
        .map(|pattern| {
            Pattern::new(pattern)
                .with_context(|| format!("Ungültiges Ausschlussmuster: {}", pattern))
        })
        .collect::<Result<Vec<_>>>()?;

    if args.verbose && !exclude_patterns.is_empty() {
        debug!("Folgende Exclude-Pattern werden genutzt:");
        for p in &exclude_patterns {
            debug!("  {}", p.as_str());
        }
    }

    // Für alle angegebenen Pfade
    for path in &args.paths {
        // Alle Einträge sammeln, damit zuerst die tiefsten umbenannt werden
        let mut entries = Vec::new();
        for entry_result in WalkDir::new(path)
            .into_iter()
            .filter_entry(|e| !is_excluded(e, &exclude_patterns))
        {
            if let Ok(entry) = entry_result {
                entries.push(entry);
            } else if let Err(e) = entry_result {
                error!("{}", format!("Fehler beim Durchlaufen von {}: {}", path.display(), e).red());
            }
        }

        // Aufsteigend nach Tiefe sortieren, dann umkehren => tiefste Einträge zuerst
        entries.sort_by_key(|e| e.depth());
        entries.reverse();

        // Fortschrittsbalken bei größeren Dateimengen
        let progress_bar = if !args.quiet && entries.len() > 50 {
            let bar = ProgressBar::new(entries.len() as u64);
            bar.set_style(ProgressStyle::default_bar()
                .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) {msg}")?
                .progress_chars("#>-"));
            Some(bar)
        } else {
            None
        };

        // Berechne Umbenennungen (parallel bei vielen Dateien)
        let rename_ops: Vec<RenameOperation> = if entries.len() >= PARALLEL_THRESHOLD {
            // Parallel mit rayon
            entries
                .par_iter()
                .filter_map(|entry| {
                    let old_path = entry.path();

                    // Ebenentiefe 0 -> überspringen
                    if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
                        return None;
                    }

                    // Special Files nur mit --special
                    let file_type = entry.file_type();
                    if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
                        return None;
                    }

                    // Dateiname ermitteln und bereinigen
                    let filename = old_path.file_name()?;
                    let new_name = clean_filename(filename, &config, false)?;
                    let new_path = old_path.with_file_name(&new_name);

                    Some(RenameOperation {
                        old_path: old_path.to_path_buf(),
                        new_path,
                    })
                })
                .collect()
        } else {
            // Sequenziell bei wenigen Dateien
            entries
                .iter()
                .filter_map(|entry| {
                    let old_path = entry.path();

                    if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
                        return None;
                    }

                    let file_type = entry.file_type();
                    if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
                        return None;
                    }

                    let filename = old_path.file_name()?;
                    let new_name = clean_filename(filename, &config, false)?;
                    let new_path = old_path.with_file_name(&new_name);

                    Some(RenameOperation {
                        old_path: old_path.to_path_buf(),
                        new_path,
                    })
                })
                .collect()
        };

        // Statistiken
        let total_processed = entries.len();
        let total_planned = rename_ops.len();
        let mut renamed_count = 0;
        let mut skipped_count = 0;

        // Umbenennungen sequenziell ausführen
        for op in rename_ops {
            if let Some(bar) = &progress_bar {
                bar.inc(1);
            }

            if !args.quiet {
                if args.dry_run {
                    info!("{} {} {}",
                        op.old_path.display().to_string().dimmed(),
                        "->".yellow(),
                        op.new_path.display().to_string().yellow()
                    );
                } else {
                    info!("{} {} {}",
                        op.old_path.display().to_string().dimmed(),
                        "->".green(),
                        op.new_path.display().to_string().green()
                    );
                }
            }

            if args.verbose {
                debug!("Rename: {:?} -> {:?}", op.old_path, op.new_path);
            }

            if !args.dry_run {
                if is_safe_rename(&op.old_path, &op.new_path, args.force) {
                    match fs::rename(&op.old_path, &op.new_path) {
                        Ok(_) => renamed_count += 1,
                        Err(e) => {
                            error!("{}",
                                format!(
                                    "Fehler beim Umbenennen: {} -> {}: {}",
                                    op.old_path.display(),
                                    op.new_path.display(),
                                    e
                                ).red()
                            );
                            skipped_count += 1;
                        }
                    }
                } else {
                    skipped_count += 1;
                }
            }
        }

        if let Some(bar) = &progress_bar {
            bar.finish_with_message("Verarbeitung abgeschlossen");
        }

        // Zusammenfassung ausgeben (außer im quiet mode)
        if !args.quiet {
            info!("");
            info!("{}", format!("=== Zusammenfassung für {} ===", path.display()).cyan().bold());
            info!("Verarbeitete Dateien/Verzeichnisse: {}", total_processed.to_string().cyan());
            info!("Umbenennungen geplant:              {}", total_planned.to_string().cyan());
            if args.dry_run {
                info!("Modus:                               {}", "Dry-run (keine Änderungen)".yellow());
            } else {
                info!("Erfolgreich umbenannt:               {}", renamed_count.to_string().green().bold());
                if skipped_count > 0 {
                    info!("Übersprungen/Fehler:                 {}", skipped_count.to_string().red());
                }
            }
        }
    }

    Ok(())
}
