/*!
A simple rule that detects trailing whitespace at the end of lines.

Conventions:
- Export a `pub fn create() -> Box<dyn crate::rules::Rule>` factory function.
- Implement the `crate::rules::Rule` trait:

  fn id(&self) -> &'static str;
  fn description(&self) -> &'static str;
  fn run(&self, path: &std::path::Path, content: &str, fr: &mut crate::cmds::check::file::FileResult);

This rule flags any line that ends with one or more spaces or tabs.
*/

use regex::Regex;
use std::path::Path;
use std::sync::LazyLock;

use crate::cmds::check::file::FileResult;

/// Zero-sized rule type (no state required).
pub struct TrailingWhitespaceRule;

impl TrailingWhitespaceRule {
    fn pattern() -> &'static Regex {
        // Matches one or more spaces or tabs at the end of a line.
        static RE: LazyLock<Regex> = LazyLock::new(|| {
            Regex::new(r"[ \t]+$")
                .unwrap_or_else(|e| panic!("invalid trailing whitespace regex: {e}"))
        });
        &RE
    }
}

impl crate::rules::Rule for TrailingWhitespaceRule {
    fn id(&self) -> &'static str {
        "trailing_whitespace"
    }

    fn description(&self) -> &'static str {
        "Detect trailing spaces/tabs at the end of lines"
    }

    fn run(&self, _path: &Path, content: &str, fr: &mut FileResult) {
        for (i, line) in content.lines().enumerate() {
            if Self::pattern().is_match(line) {
                // Show the line content trimmed of trailing whitespace so the
                // snippet is readable in diagnostics.
                let snippet = line.trim_end_matches([' ', '\t']).to_string();
                let msg = format!("trailing whitespace detected (line {}): {}", i + 1, snippet);
                fr.warnings.push(msg);
            }
        }
    }

    /// Optional auto-fix: when `do_fix` is true, return a modified version
    /// of the file content with trailing spaces/tabs removed from each line.
    /// If no changes are necessary, return `None`.
    fn run_with_fix(
        &self,
        _path: &Path,
        content: &str,
        _fr: &mut FileResult,
        do_fix: bool,
    ) -> Option<String> {
        if !do_fix {
            return None;
        }

        // Normalize each line by trimming trailing spaces and tabs.
        // Preserve whether the original content ended with a newline.
        let ends_with_newline = content.ends_with('\n');
        let fixed_lines: Vec<String> = content
            .split('\n')
            .map(|line| line.trim_end_matches([' ', '\t']).to_string())
            .collect();
        let mut fixed = fixed_lines.join("\n");
        if ends_with_newline && !fixed.ends_with('\n') {
            fixed.push('\n');
        }

        if fixed == content { None } else { Some(fixed) }
    }
}

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'create'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[must_use]
pub fn create() -> Box<dyn crate::rules::Rule> {
    Box::new(TrailingWhitespaceRule)
}
