package handlers import ( "encoding/json" "io" "net/http" "mgit.msbls.de/m/KanzlAI-mGMT/internal/auth" "mgit.msbls.de/m/KanzlAI-mGMT/internal/services" ) type AIHandler struct { ai *services.AIService } func NewAIHandler(ai *services.AIService) *AIHandler { return &AIHandler{ai: ai} } // ExtractDeadlines handles POST /api/ai/extract-deadlines // Accepts either multipart/form-data with a "file" PDF field, or JSON {"text": "..."}. func (h *AIHandler) ExtractDeadlines(w http.ResponseWriter, r *http.Request) { contentType := r.Header.Get("Content-Type") var pdfData []byte var text string // Check if multipart (PDF upload) if len(contentType) >= 9 && contentType[:9] == "multipart" { if err := r.ParseMultipartForm(32 << 20); err != nil { // 32MB max writeError(w, http.StatusBadRequest, "failed to parse multipart form") return } file, _, err := r.FormFile("file") if err != nil { writeError(w, http.StatusBadRequest, "missing 'file' field in multipart form") return } defer file.Close() pdfData, err = io.ReadAll(file) if err != nil { writeError(w, http.StatusBadRequest, "failed to read uploaded file") return } } else { // Assume JSON body var body struct { Text string `json:"text"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } text = body.Text } if len(pdfData) == 0 && text == "" { writeError(w, http.StatusBadRequest, "provide either a PDF file or text") return } if len(text) > maxDescriptionLen { writeError(w, http.StatusBadRequest, "text exceeds maximum length") return } deadlines, err := h.ai.ExtractDeadlines(r.Context(), pdfData, text) if err != nil { internalError(w, "AI deadline extraction failed", err) return } writeJSON(w, http.StatusOK, map[string]any{ "deadlines": deadlines, "count": len(deadlines), }) } // SummarizeCase handles POST /api/ai/summarize-case // Accepts JSON {"case_id": "uuid"}. func (h *AIHandler) SummarizeCase(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } var body struct { CaseID string `json:"case_id"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if body.CaseID == "" { writeError(w, http.StatusBadRequest, "case_id is required") return } caseID, err := parseUUID(body.CaseID) if err != nil { writeError(w, http.StatusBadRequest, "invalid case_id") return } summary, err := h.ai.SummarizeCase(r.Context(), tenantID, caseID) if err != nil { internalError(w, "AI case summarization failed", err) return } writeJSON(w, http.StatusOK, map[string]string{ "case_id": caseID.String(), "summary": summary, }) }