package admin import ( "crussell/db" "crussell/internal/validators" "crussell/mw" "database/sql" "encoding/json" "net/http" "github.com/go-chi/chi/v5" ) // PatchTest represents a patch test definition type PatchTest struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description,omitempty"` NoticeDurationHours int `json:"notice_duration_hours"` ExpiryMonths int `json:"expiry_months"` ServiceIDs []string `json:"service_ids"` } // CreatePatchTestRequest represents the request payload type CreatePatchTestRequest struct { Name string `json:"name"` Description *string `json:"description,omitempty"` NoticeDurationHours int `json:"notice_duration_hours"` ExpiryMonths int `json:"expiry_months"` ServiceIDs []string `json:"service_ids"` } // UpdatePatchTestRequest represents the request payload type UpdatePatchTestRequest struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` NoticeDurationHours *int `json:"notice_duration_hours,omitempty"` ExpiryMonths *int `json:"expiry_months,omitempty"` ServiceIDs []string `json:"service_ids,omitempty"` } // GetPatchTests handles GET /api/admin/patch-tests func GetPatchTests(w http.ResponseWriter, r *http.Request) { query := ` SELECT id, name, description, notice_duration_hours, expiry_months, service_ids FROM patch_tests ORDER BY name ` rows, err := db.DB.Query(r.Context(), query) if err != nil { http.Error(w, "Failed to fetch patch tests: "+err.Error(), http.StatusInternalServerError) return } defer rows.Close() var patchTests []PatchTest for rows.Next() { var pt PatchTest var desc sql.NullString err := rows.Scan(&pt.ID, &pt.Name, &desc, &pt.NoticeDurationHours, &pt.ExpiryMonths, &pt.ServiceIDs) if err != nil { http.Error(w, "Failed to read patch test data: "+err.Error(), http.StatusInternalServerError) return } if desc.Valid { pt.Description = &desc.String } patchTests = append(patchTests, pt) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(patchTests) } // CreatePatchTest handles POST /api/admin/patch-tests func CreatePatchTest(w http.ResponseWriter, r *http.Request) { var req CreatePatchTestRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } query := ` INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids) VALUES ($1, $2, $3, $4, $5) RETURNING id ` var id string err := db.DB.QueryRow(r.Context(), query, req.Name, req.Description, req.NoticeDurationHours, req.ExpiryMonths, req.ServiceIDs).Scan(&id) if err != nil { http.Error(w, "Failed to create patch test: "+err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]string{"id": id}) } // UpdatePatchTest handles PUT /api/admin/patch-tests/{id} func UpdatePatchTest(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if id == "" || !validators.IsValidID(id) { http.Error(w, "Patch test not found", http.StatusNotFound) return } var req UpdatePatchTestRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } query := "UPDATE patch_tests SET " args := []interface{}{} i := 1 if req.Name != nil { query += "name = $" + string(rune('0'+i)) + ", " args = append(args, *req.Name) i++ } if req.Description != nil { query += "description = $" + string(rune('0'+i)) + ", " args = append(args, *req.Description) i++ } if req.NoticeDurationHours != nil { query += "notice_duration_hours = $" + string(rune('0'+i)) + ", " args = append(args, *req.NoticeDurationHours) i++ } if req.ExpiryMonths != nil { query += "expiry_months = $" + string(rune('0'+i)) + ", " args = append(args, *req.ExpiryMonths) i++ } if req.ServiceIDs != nil { query += "service_ids = $" + string(rune('0'+i)) + ", " args = append(args, req.ServiceIDs) i++ } query = query[:len(query)-2] query += " WHERE id = $" + string(rune('0'+i)) args = append(args, id) _, err := db.DB.Exec(r.Context(), query, args...) if err != nil { http.Error(w, "Failed to update patch test: "+err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // DeletePatchTest handles DELETE /api/admin/patch-tests/{id} func DeletePatchTest(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if id == "" || !validators.IsValidID(id) { http.Error(w, "Patch test not found", http.StatusNotFound) return } _, err := db.DB.Exec(r.Context(), "DELETE FROM patch_tests WHERE id = $1", id) if err != nil { http.Error(w, "Failed to delete patch test: "+err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }