package report import ( "encoding/json" "fmt" "io" "strings" "time" "arcline-audit/internal/types" ) const sectionWidth = 55 // Terminal renders the audit result to the given writer in a human-readable terminal format. func Terminal(w io.Writer, result types.AuditResult) { fmt.Fprintf(w, "\n") printSection(w, "SSL", result.SSL.Checks, func() { if result.SSL.Expiry != (time.Time{}) { fmt.Fprintf(w, " Issuer %s\n", result.SSL.Issuer) fmt.Fprintf(w, " Expires %s (%d days)\n", result.SSL.Expiry.Format("2006-01-02"), result.SSL.DaysLeft) if result.SSL.TLSVersion != "" { fmt.Fprintf(w, " TLS %s\n", result.SSL.TLSVersion) } } }) printSection(w, "HTTP", result.HTTP.Checks, func() { if result.HTTP.FinalURL != "" { fmt.Fprintf(w, " Final URL %s\n", result.HTTP.FinalURL) } fmt.Fprintf(w, " Response %dms\n", result.HTTP.ResponseTime.Milliseconds()) }) printSection(w, "DNS", result.DNS.Checks, nil) printSection(w, "Infrastructure", result.Infra.Checks, func() { if result.Infra.CDN != "" { fmt.Fprintf(w, " CDN %s\n", result.Infra.CDN) } if result.Infra.ASN != "" { fmt.Fprintf(w, " ASN %s\n", result.Infra.ASN) } if result.Infra.Org != "" { fmt.Fprintf(w, " Org %s\n", result.Infra.Org) } }) fmt.Fprintf(w, "\n") } // JSON renders the audit result as indented JSON. func JSON(w io.Writer, result types.AuditResult) { enc := json.NewEncoder(w) enc.SetIndent("", " ") enc.Encode(result) } // PlainText renders the audit result as plain text (no ANSI codes). func PlainText(w io.Writer, result types.AuditResult) { sections := []struct { name string checks []types.CheckResult }{ {"SSL", result.SSL.Checks}, {"HTTP", result.HTTP.Checks}, {"DNS", result.DNS.Checks}, {"Infrastructure", result.Infra.Checks}, } fmt.Fprintf(w, "Audit for %s — %s\n\n", result.Domain, result.Time.Format(time.RFC3339)) for _, sec := range sections { fmt.Fprintf(w, "── %s %s\n", sec.name, strings.Repeat("─", sectionWidth-5-len(sec.name))) for _, c := range sec.checks { fmt.Fprintf(w, "[%s] %s\n", c.Status, c.Message) } fmt.Fprintf(w, "\n") } } func printSection(w io.Writer, name string, checks []types.CheckResult, extra func()) { header := fmt.Sprintf("── %s ", name) fmt.Fprintf(w, " %s%s\n", header, strings.Repeat("─", sectionWidth-len(header)-2)) for _, c := range checks { prefix := statusPrefix(c.Status) fmt.Fprintf(w, " %s %s\n", prefix, c.Message) } if extra != nil { extra() } } func statusPrefix(s types.Status) string { switch s { case types.StatusOK: return "[OK] " case types.StatusWarn: return "[WARN]" case types.StatusFail: return "[FAIL]" case types.StatusInfo: return "[INFO]" default: return "[????]" } }