//! GitHub Actions annotation renderer.
//!
//! Outputs `::warning::` and `::error::` workflow commands that
//! GitHub Actions intercepts to create inline PR annotations.

use crate::models::finding::Finding;
use crate::output::OutputFormatter;
use crate::output::escape;
use std::fmt::Write;

/// GitHub Actions annotation renderer.
pub struct GithubFormatter;

impl OutputFormatter for GithubFormatter {
    fn format(&self, findings: &[Finding]) -> String {
        // ~150 bytes per finding annotation
        let mut output = String::with_capacity(findings.len() * 150 + 100);

        for finding in findings {
            let level = finding.severity.as_github_level();

            let _ = write!(
                output,
                "::{level} file={},line={}",
                finding.file, finding.line
            );
            if let Some(end) = finding.end_line {
                let _ = write!(output, ",endLine={end}");
            }
            let _ = write!(
                output,
                ",title={}",
                escape::github_annotation(&finding.title)
            );

            let _ = write!(output, "::{}", escape::github_annotation(&finding.message));
            if let Some(ref suggestion) = finding.suggestion {
                let _ = write!(
                    output,
                    " Suggestion: {}",
                    escape::github_annotation(suggestion)
                );
            }
            output.push('\n');
        }

        if !findings.is_empty() {
            let _ = writeln!(
                output,
                "::notice title=AI-Generated Analysis::{}",
                escape::github_annotation(crate::constants::AI_DISCLOSURE),
            );
        }

        output
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::finding::Severity;
    use crate::output::escape;

    #[test]
    fn render_error() {
        let renderer = GithubFormatter;
        let findings = vec![Finding {
            file: "src/auth.rs".into(),
            line: 42,
            end_line: Some(45),
            severity: Severity::Error,
            title: "SQL injection vulnerability".into(),
            message: "User input is interpolated directly into query string.".into(),
            suggestion: Some("Use parameterized queries.".into()),
            agent: "security".into(),
        }];

        let output = renderer.format(&findings);
        assert_eq!(
            output,
            concat!(
                "::error file=src/auth.rs,line=42,endLine=45,title=SQL injection vulnerability",
                "::User input is interpolated directly into query string.",
                " Suggestion: Use parameterized queries.\n",
                "::notice title=AI-Generated Analysis",
                "::Generated by nitpik — AI-powered analysis.",
                " Findings are advisory and may contain errors.\n",
            )
        );
    }

    #[test]
    fn render_warning() {
        let renderer = GithubFormatter;
        let findings = vec![Finding {
            file: "test.rs".into(),
            line: 1,
            end_line: None,
            severity: Severity::Warning,
            title: "Issue".into(),
            message: "Details".into(),
            suggestion: None,
            agent: "backend".into(),
        }];

        let output = renderer.format(&findings);
        assert!(output.starts_with("::warning "));
        assert!(output.contains("file=test.rs,line=1,"));
    }

    #[test]
    fn render_info_as_notice() {
        let renderer = GithubFormatter;
        let findings = vec![Finding {
            file: "test.rs".into(),
            line: 1,
            end_line: None,
            severity: Severity::Info,
            title: "Tip".into(),
            message: "Consider this".into(),
            suggestion: None,
            agent: "backend".into(),
        }];

        let output = renderer.format(&findings);
        assert!(output.starts_with("::notice "));
    }

    #[test]
    fn escape_newlines() {
        let s = "line1\nline2";
        assert_eq!(escape::github_annotation(s), "line1%0Aline2");
    }
}
