use ruff_source_file::LineColumn;

use crate::diagnostic::{Diagnostic, DisplayDiagnosticConfig, Severity};

use super::FileResolver;

pub(super) struct AzureRenderer<'a> {
    resolver: &'a dyn FileResolver,
    config: &'a DisplayDiagnosticConfig,
}

impl<'a> AzureRenderer<'a> {
    pub(super) fn new(resolver: &'a dyn FileResolver, config: &'a DisplayDiagnosticConfig) -> Self {
        Self { resolver, config }
    }
}

impl AzureRenderer<'_> {
    pub(super) fn render(
        &self,
        f: &mut std::fmt::Formatter,
        diagnostics: &[Diagnostic],
    ) -> std::fmt::Result {
        for diag in diagnostics {
            let severity = match diag.severity() {
                Severity::Info | Severity::Warning => "warning",
                Severity::Error | Severity::Fatal => "error",
            };
            write!(f, "##vso[task.logissue type={severity};")?;
            if let Some(span) = diag.primary_span() {
                let filename = span.file().path(self.resolver);
                write!(f, "sourcepath={filename};")?;
                if let Some(range) = span.range() {
                    let location = if self.resolver.notebook_index(span.file()).is_some() {
                        // We can't give a reasonable location for the structured formats,
                        // so we show one that's clearly a fallback
                        LineColumn::default()
                    } else {
                        span.file()
                            .diagnostic_source(self.resolver)
                            .as_source_code()
                            .line_column(range.start())
                    };
                    write!(
                        f,
                        "linenumber={line};columnnumber={col};",
                        line = location.line,
                        col = location.column,
                    )?;
                }
            }
            let code = if self.config.preview && !self.config.prefer_rule_codes {
                diag.id().as_str()
            } else {
                diag.secondary_code_or_id()
            };
            writeln!(f, "code={code};]{body}", body = diag.concise_message(),)?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::diagnostic::{
        DiagnosticFormat,
        render::tests::{create_diagnostics, create_syntax_error_diagnostics},
    };

    #[test]
    fn output() {
        let (env, diagnostics) = create_diagnostics(DiagnosticFormat::Azure);
        insta::assert_snapshot!(env.render_diagnostics(&diagnostics));
    }

    #[test]
    fn syntax_errors() {
        let (env, diagnostics) = create_syntax_error_diagnostics(DiagnosticFormat::Azure);
        insta::assert_snapshot!(env.render_diagnostics(&diagnostics));
    }
}
