//! Formats and persists correction suggestions for the user.

use crate::learn::detector::CorrectionRule;
use anyhow::Result;
use std::collections::HashMap;
use std::fs;
use std::path::Path;

pub fn format_console_report(
    rules: &[CorrectionRule],
    total_corrections: usize,
    sessions: usize,
    days: u64,
) -> String {
    let mut output = String::new();

    output.push_str(&format!(
        "RTK Learn -- {} rules from {} corrections ({} sessions, {} days)\n",
        rules.len(),
        total_corrections,
        sessions,
        days
    ));

    if rules.is_empty() {
        output.push_str("\nNo CLI corrections detected.\n");
        return output;
    }

    output.push('\n');

    for rule in rules {
        let count_marker = if rule.occurrences > 1 {
            format!("[{}x] ", rule.occurrences)
        } else {
            "     ".to_string()
        };

        output.push_str(&format!(
            "{}{}  →  {}\n",
            count_marker, rule.wrong_pattern, rule.right_pattern
        ));

        // Show error snippet (first line only)
        let error_line = rule.example_error.lines().next().unwrap_or("").trim();
        if !error_line.is_empty() {
            output.push_str(&format!("     Error: {}\n", error_line));
        }
    }

    output
}

pub fn write_rules_file(rules: &[CorrectionRule], path: &str) -> Result<()> {
    let path_obj = Path::new(path);

    // Create parent directory if it doesn't exist
    if let Some(parent) = path_obj.parent() {
        fs::create_dir_all(parent)?;
    }

    let mut content = String::new();
    content.push_str("# CLI Corrections (auto-generated by rtk learn)\n");
    content.push_str("# Run `rtk learn --write-rules` to update\n\n");

    if rules.is_empty() {
        content.push_str("No CLI corrections detected yet.\n");
        fs::write(path, content)?;
        return Ok(());
    }

    // Group by base command
    let mut grouped: HashMap<String, Vec<&CorrectionRule>> = HashMap::new();
    for rule in rules {
        grouped
            .entry(rule.base_command.clone())
            .or_default()
            .push(rule);
    }

    // Sort base commands alphabetically
    let mut base_commands: Vec<String> = grouped.keys().cloned().collect();
    base_commands.sort();

    for base_cmd in base_commands {
        let rules_for_cmd = grouped.get(&base_cmd).unwrap();

        // Capitalize first letter for section header
        let section_header = capitalize_first(&base_cmd);
        content.push_str(&format!("## {}\n", section_header));

        for rule in rules_for_cmd {
            let occurrence_note = if rule.occurrences > 1 {
                format!(" (seen {}x)", rule.occurrences)
            } else {
                String::new()
            };

            content.push_str(&format!(
                "- Use `{}` not `{}`{}\n",
                rule.right_pattern, rule.wrong_pattern, occurrence_note
            ));
        }

        content.push('\n');
    }

    fs::write(path, content)?;
    Ok(())
}

fn capitalize_first(s: &str) -> String {
    let mut chars = s.chars();
    match chars.next() {
        None => String::new(),
        Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::learn::detector::ErrorType;

    #[test]
    fn test_format_console_report_empty() {
        let report = format_console_report(&[], 0, 0, 30);
        assert!(report.contains("0 rules"));
        assert!(report.contains("0 corrections"));
        assert!(report.contains("No CLI corrections detected"));
    }

    #[test]
    fn test_format_console_report_with_rules() {
        let rules = vec![
            CorrectionRule {
                wrong_pattern: "git commit --ammend".to_string(),
                right_pattern: "git commit --amend".to_string(),
                error_type: ErrorType::UnknownFlag,
                occurrences: 3,
                base_command: "git commit".to_string(),
                example_error: "error: unexpected argument '--ammend'".to_string(),
            },
            CorrectionRule {
                wrong_pattern: "gh pr edit -t".to_string(),
                right_pattern: "gh pr edit --title".to_string(),
                error_type: ErrorType::UnknownFlag,
                occurrences: 1,
                base_command: "gh pr".to_string(),
                example_error: "unknown flag: -t".to_string(),
            },
        ];

        let report = format_console_report(&rules, 4, 10, 30);
        assert!(report.contains("2 rules"));
        assert!(report.contains("4 corrections"));
        assert!(report.contains("[3x]"));
        assert!(report.contains("--ammend"));
        assert!(report.contains("--amend"));
        assert!(report.contains("Error: error: unexpected argument"));
    }

    #[test]
    fn test_write_rules_file_markdown() {
        let rules = vec![CorrectionRule {
            wrong_pattern: "git commit --ammend".to_string(),
            right_pattern: "git commit --amend".to_string(),
            error_type: ErrorType::UnknownFlag,
            occurrences: 3,
            base_command: "git commit".to_string(),
            example_error: "error: unexpected argument '--ammend'".to_string(),
        }];

        let temp_dir = tempfile::tempdir().unwrap();
        let path = temp_dir.path().join("cli-corrections.md");
        let path_str = path.to_str().unwrap();

        write_rules_file(&rules, path_str).unwrap();

        let content = fs::read_to_string(&path).unwrap();
        assert!(content.contains("# CLI Corrections"));
        assert!(content.contains("## Git commit"));
        assert!(content.contains("Use `git commit --amend` not `git commit --ammend`"));
        assert!(content.contains("(seen 3x)"));
    }
}
