use std::fs;

use std::io::{self, Write};

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

use std::process::ExitCode;

 

fn main() -> ExitCode {

  match run() {

      Ok(()) => ExitCode::SUCCESS,

      Err(error) => {

          eprintln!("Error: {error}");

          eprintln!("Processing stopped. Previously saved files remain changed.");

          ExitCode::FAILURE

       }

   }

}

 

fn run() -> Result<(), String> {

  print!("Enter folder path (or exit): ");

  io::stdout().flush().map_err(|e| e.to_string())?;

 

  let mut input = String::new();

  io::stdin().read_line(&mut input).map_err(|e| e.to_string())?;

  let input = input.trim();

  if input.is_empty() || input.eq_ignore_ascii_case("exit") {

      return Ok(());

   }

 

  // Accept paths copied with surrounding double quotes.

  let folder = Path::new(input.trim_matches('"'));

  let mut files: Vec<PathBuf> = Vec::new();

  let entries = fs::read_dir(folder)

      .map_err(|e| format!("Cannot open folder '{}': {e}", folder.display()))?;

 

  // Collect regular XML files only; do not enter subdirectories.

  for entry in entries {

      let entry = entry.map_err(|e| format!("Cannot read folder entry: {e}"))?;

      let path = entry.path();

      let file_type = entry.file_type()

          .map_err(|e| format!("Cannot inspect '{}': {e}", path.display()))?;

      let is_xml = path.extension().and_then(|s| s.to_str())

          .is_some_and(|s| s.eq_ignore_ascii_case("xml"));

      if file_type.is_file() && is_xml {

          files.push(path);

       }

   }

 

  // Sort alphabetically, ignoring case. Exact spelling breaks ties.

  files.sort_by_cached_key(|path| {

       (path.file_name().unwrap_or_default().to_string_lossy().to_lowercase(), path.clone())

   });

  if files.is_empty() {

      return Err("No XML files found in the selected folder.".into());

   }

 

  // Read, change and save each file before processing the next one.

  for (index, path) in files.iter().enumerate() {

      let mut text = fs::read_to_string(path)

          .map_err(|e| format!("Cannot read '{}': {e}", path.display()))?;

      if !replace_number(&mut text, index + 1) {

          println!("Skipped (no >digits< pattern): {}", path.display());

          continue;

       }

      fs::write(path, text.as_bytes())

          .map_err(|e| format!("Cannot save '{}': {e}", path.display()))?;

      println!("Saved: {} -> {}", path.display(), index + 1);

   }

 

  println!("Done. {} XML file(s) processed.", files.len());

  Ok(())

}

 

/// Replace the first >digits< pattern, for example >6< or >123<.

/// Return false if no matching pattern exists. All other bytes stay unchanged.

fn replace_number(text: &mut String, number: usize) -> bool {

  let bytes = text.as_bytes();

 

  for (position, byte) in bytes.iter().enumerate() {

      if *byte != b'>' {

          continue;

       }

 

      let start = position + 1;

      let mut end = start;

      while end < bytes.len() && bytes[end].is_ascii_digit() {

          end += 1;

       }

 

      // Require at least one digit followed immediately by '<'.

      if end > start && bytes.get(end) == Some(&b'<') {

          text.replace_range(start..end, &number.to_string());

          return true;

       }

   }

 

  false

}