package handlers import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/breakpilot/ai-compliance-sdk/internal/rbac" "github.com/breakpilot/ai-compliance-sdk/internal/ucca" ) // ExportMemo exports the obligations overview as a C-Level memo // POST /sdk/v1/ucca/obligations/export/memo func (h *ObligationsHandlers) ExportMemo(c *gin.Context) { tenantID := rbac.GetTenantID(c) var req ucca.ExportMemoRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } if h.store == nil { c.JSON(http.StatusNotImplemented, gin.H{"error": "Persistence not configured"}) return } id, err := uuid.Parse(req.AssessmentID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"}) return } assessment, err := h.store.GetAssessment(c.Request.Context(), tenantID, id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"}) return } exporter := ucca.NewPDFExporter(req.Language) var response *ucca.ExportMemoResponse switch req.Format { case "pdf": response, err = exporter.ExportManagementMemo(assessment.Overview) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate PDF", "details": err.Error()}) return } case "markdown", "": response, err = exporter.ExportMarkdown(assessment.Overview) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate Markdown", "details": err.Error()}) return } default: c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid format. Use 'markdown' or 'pdf'"}) return } c.JSON(http.StatusOK, response) } // ExportMemoFromOverview exports an overview directly (without persistence) // POST /sdk/v1/ucca/obligations/export/direct func (h *ObligationsHandlers) ExportMemoFromOverview(c *gin.Context) { var req struct { Overview *ucca.ManagementObligationsOverview `json:"overview"` Format string `json:"format"` // "markdown" or "pdf" Language string `json:"language,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } if req.Overview == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Overview is required"}) return } exporter := ucca.NewPDFExporter(req.Language) var response *ucca.ExportMemoResponse var err error switch req.Format { case "pdf": response, err = exporter.ExportManagementMemo(req.Overview) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate PDF", "details": err.Error()}) return } case "markdown", "": response, err = exporter.ExportMarkdown(req.Overview) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate Markdown", "details": err.Error()}) return } default: c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid format. Use 'markdown' or 'pdf'"}) return } c.JSON(http.StatusOK, response) } // ============================================================================ // Helper Functions // ============================================================================ func generateMemoMarkdown(overview *ucca.ManagementObligationsOverview) string { content := "# Pflichten-Übersicht für die Geschäftsführung\n\n" content += "**Datum:** " + overview.AssessmentDate.Format("02.01.2006") + "\n" if overview.OrganizationName != "" { content += "**Organisation:** " + overview.OrganizationName + "\n" } content += "\n---\n\n" content += "## Executive Summary\n\n" content += "| Kennzahl | Wert |\n" content += "|----------|------|\n" content += "| Anwendbare Regulierungen | " + itoa(overview.ExecutiveSummary.TotalRegulations) + " |\n" content += "| Gesamtzahl Pflichten | " + itoa(overview.ExecutiveSummary.TotalObligations) + " |\n" content += "| Kritische Pflichten | " + itoa(overview.ExecutiveSummary.CriticalObligations) + " |\n" content += "| Überfällige Pflichten | " + itoa(overview.ExecutiveSummary.OverdueObligations) + " |\n" content += "| Anstehende Fristen (30 Tage) | " + itoa(overview.ExecutiveSummary.UpcomingDeadlines) + " |\n" content += "\n" if len(overview.ExecutiveSummary.KeyRisks) > 0 { content += "### Hauptrisiken\n\n" for _, risk := range overview.ExecutiveSummary.KeyRisks { content += "- ⚠️ " + risk + "\n" } content += "\n" } if len(overview.ExecutiveSummary.RecommendedActions) > 0 { content += "### Empfohlene Maßnahmen\n\n" for i, action := range overview.ExecutiveSummary.RecommendedActions { content += itoa(i+1) + ". " + action + "\n" } content += "\n" } content += "## Anwendbare Regulierungen\n\n" for _, reg := range overview.ApplicableRegulations { content += "### " + reg.Name + "\n\n" content += "- **Klassifizierung:** " + reg.Classification + "\n" content += "- **Begründung:** " + reg.Reason + "\n" content += "- **Anzahl Pflichten:** " + itoa(reg.ObligationCount) + "\n" content += "\n" } content += "## Sanktionsrisiken\n\n" content += overview.SanctionsSummary.Summary + "\n\n" if overview.SanctionsSummary.MaxFinancialRisk != "" { content += "- **Maximales Bußgeld:** " + overview.SanctionsSummary.MaxFinancialRisk + "\n" } if overview.SanctionsSummary.PersonalLiabilityRisk { content += "- **Persönliche Haftung:** Ja ⚠️\n" } content += "\n" content += "## Kritische Pflichten\n\n" for _, obl := range overview.Obligations { if obl.Priority == ucca.PriorityCritical { content += "### " + obl.ID + ": " + obl.Title + "\n\n" content += obl.Description + "\n\n" content += "- **Verantwortlich:** " + string(obl.Responsible) + "\n" if obl.Deadline != nil { if obl.Deadline.Date != nil { content += "- **Frist:** " + obl.Deadline.Date.Format("02.01.2006") + "\n" } else if obl.Deadline.Duration != "" { content += "- **Frist:** " + obl.Deadline.Duration + "\n" } } if obl.Sanctions != nil && obl.Sanctions.MaxFine != "" { content += "- **Sanktion:** " + obl.Sanctions.MaxFine + "\n" } content += "\n" } } if len(overview.IncidentDeadlines) > 0 { content += "## Meldepflichten bei Sicherheitsvorfällen\n\n" content += "| Phase | Frist | Empfänger |\n" content += "|-------|-------|-----------|\n" for _, deadline := range overview.IncidentDeadlines { content += "| " + deadline.Phase + " | " + deadline.Deadline + " | " + deadline.Recipient + " |\n" } content += "\n" } content += "---\n\n" content += "*Dieses Dokument wurde automatisch generiert und ersetzt keine Rechtsberatung.*\n" return content } func isEUCountry(country string) bool { euCountries := map[string]bool{ "DE": true, "AT": true, "BE": true, "BG": true, "HR": true, "CY": true, "CZ": true, "DK": true, "EE": true, "FI": true, "FR": true, "GR": true, "HU": true, "IE": true, "IT": true, "LV": true, "LT": true, "LU": true, "MT": true, "NL": true, "PL": true, "PT": true, "RO": true, "SK": true, "SI": true, "ES": true, "SE": true, } return euCountries[country] } func itoa(i int) string { return strconv.Itoa(i) }