package handlers import ( "encoding/json" "fmt" "net/http" "github.com/google/uuid" "mgit.msbls.de/m/KanzlAI-mGMT/internal/auth" "mgit.msbls.de/m/KanzlAI-mGMT/internal/services" ) type NoteHandler struct { svc *services.NoteService } func NewNoteHandler(svc *services.NoteService) *NoteHandler { return &NoteHandler{svc: svc} } // List handles GET /api/notes?{parent_type}_id={id} func (h *NoteHandler) List(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "missing tenant") return } parentType, parentID, err := parseNoteParent(r) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } notes, err := h.svc.ListByParent(r.Context(), tenantID, parentType, parentID) if err != nil { writeError(w, http.StatusInternalServerError, "failed to list notes") return } writeJSON(w, http.StatusOK, notes) } // Create handles POST /api/notes func (h *NoteHandler) Create(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "missing tenant") return } userID, _ := auth.UserFromContext(r.Context()) var input services.CreateNoteInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if input.Content == "" { writeError(w, http.StatusBadRequest, "content is required") return } if msg := validateStringLength("content", input.Content, maxDescriptionLen); msg != "" { writeError(w, http.StatusBadRequest, msg) return } var createdBy *uuid.UUID if userID != uuid.Nil { createdBy = &userID } note, err := h.svc.Create(r.Context(), tenantID, createdBy, input) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create note") return } writeJSON(w, http.StatusCreated, note) } // Update handles PUT /api/notes/{id} func (h *NoteHandler) Update(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "missing tenant") return } noteID, err := uuid.Parse(r.PathValue("id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid note ID") return } var req struct { Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if req.Content == "" { writeError(w, http.StatusBadRequest, "content is required") return } if msg := validateStringLength("content", req.Content, maxDescriptionLen); msg != "" { writeError(w, http.StatusBadRequest, msg) return } note, err := h.svc.Update(r.Context(), tenantID, noteID, req.Content) if err != nil { writeError(w, http.StatusInternalServerError, "failed to update note") return } if note == nil { writeError(w, http.StatusNotFound, "note not found") return } writeJSON(w, http.StatusOK, note) } // Delete handles DELETE /api/notes/{id} func (h *NoteHandler) Delete(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "missing tenant") return } noteID, err := uuid.Parse(r.PathValue("id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid note ID") return } if err := h.svc.Delete(r.Context(), tenantID, noteID); err != nil { writeError(w, http.StatusNotFound, "note not found") return } w.WriteHeader(http.StatusNoContent) } // parseNoteParent extracts the parent type and ID from query parameters. func parseNoteParent(r *http.Request) (string, uuid.UUID, error) { params := map[string]string{ "case_id": "case", "deadline_id": "deadline", "appointment_id": "appointment", "case_event_id": "case_event", } for param, parentType := range params { if v := r.URL.Query().Get(param); v != "" { id, err := uuid.Parse(v) if err != nil { return "", uuid.Nil, fmt.Errorf("invalid %s", param) } return parentType, id, nil } } return "", uuid.Nil, fmt.Errorf("one of case_id, deadline_id, appointment_id, or case_event_id is required") }