package handlers import ( "fmt" "log" "net/http" "strconv" "strings" "time" "tankstopp/internal/currency" "tankstopp/internal/models" "tankstopp/internal/views/pages" "github.com/gorilla/mux" ) // AddFuelStopHandler handles adding new fuel stops func (h *Handler) AddFuelStopHandler(w http.ResponseWriter, r *http.Request) { userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } switch r.Method { case "GET": // Get user for default currency user, err := h.db.GetUserByID(userID) if err != nil { log.Printf("Error getting user: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } // Get vehicles for the user vehicles, err := h.db.GetVehicles(userID) if err != nil { log.Printf("Error getting vehicles: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } // Render add fuel stop form using templ currencies := currency.SupportedCurrencies() component := pages.AddFuelStopPage(user, user.Username, vehicles, currencies) w.Header().Set("Content-Type", "text/html") err = component.Render(r.Context(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } case "POST": h.handleAddFuelStop(w, r, userID) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } // handleAddFuelStop processes the form submission for adding fuel stops func (h *Handler) handleAddFuelStop(w http.ResponseWriter, r *http.Request, userID uint) { err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) http.Error(w, "Bad request", http.StatusBadRequest) return } // Parse form data form := models.FuelStopForm{ Date: strings.TrimSpace(r.FormValue("date")), VehicleID: parseUint(r.FormValue("vehicle_id")), StationName: strings.TrimSpace(r.FormValue("station_name")), Location: strings.TrimSpace(r.FormValue("location")), FuelType: r.FormValue("fuel_type"), Liters: parseFloat(r.FormValue("amount")), PricePerL: parseFloat(r.FormValue("price_per_liter")), TotalPrice: parseFloat(r.FormValue("total_cost")), Currency: r.FormValue("currency"), Odometer: parseInt(r.FormValue("odometer")), TripLength: parseFloat(r.FormValue("trip_length")), Notes: r.FormValue("notes"), } // Validate form if err := h.validateFuelStopForm(&form); err != nil { log.Printf("Validation error: %v", err) http.Redirect(w, r, "/add?error="+err.Error(), http.StatusSeeOther) return } // Convert form to fuel stop fuelStop, err := form.ToFuelStop(userID) if err != nil { log.Printf("Error converting form to fuel stop: %v", err) http.Redirect(w, r, "/add?error=Invalid+date+format", http.StatusSeeOther) return } // Use station name as location if location is empty if fuelStop.Location == "" { fuelStop.Location = fuelStop.StationName } // Save to database err = h.db.CreateFuelStop(fuelStop) if err != nil { log.Printf("Error creating fuel stop: %v", err) http.Redirect(w, r, "/add?error=Failed+to+save+fuel+stop", http.StatusSeeOther) return } // Redirect to dashboard with success message http.Redirect(w, r, "/dashboard?success=Fuel+stop+added+successfully", http.StatusSeeOther) } // EditFuelStopHandler handles editing existing fuel stops func (h *Handler) EditFuelStopHandler(w http.ResponseWriter, r *http.Request) { userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Get the user object user, err := h.db.GetUserByID(userID) if err != nil { log.Printf("Error getting user: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } vars := mux.Vars(r) idInt, err := strconv.Atoi(vars["id"]) if err != nil { http.Error(w, "Invalid ID", http.StatusBadRequest) return } id := uint(idInt) switch r.Method { case "GET": stop, err := h.db.GetFuelStopByID(id, userID) if err != nil { log.Printf("Error getting fuel stop: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } if stop == nil { http.Error(w, "Fuel stop not found", http.StatusNotFound) return } // Get vehicles for the user vehicles, err := h.db.GetVehicles(userID) if err != nil { log.Printf("Error getting vehicles: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } // Render edit fuel stop form using templ currencies := currency.SupportedCurrencies() component := pages.EditFuelStopPage(user, user.Username, stop, vehicles, currencies) w.Header().Set("Content-Type", "text/html") err = component.Render(r.Context(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } case "POST": h.handleEditFuelStop(w, r, id, userID) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } // handleEditFuelStop processes the form submission for editing fuel stops func (h *Handler) handleEditFuelStop(w http.ResponseWriter, r *http.Request, id, userID uint) { err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) http.Error(w, "Bad request", http.StatusBadRequest) return } // Get existing fuel stop existingStop, err := h.db.GetFuelStopByID(id, userID) if err != nil { log.Printf("Error getting fuel stop: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } if existingStop == nil { http.Error(w, "Fuel stop not found", http.StatusNotFound) return } // Parse form data form := models.FuelStopForm{ Date: strings.TrimSpace(r.FormValue("date")), VehicleID: parseUint(r.FormValue("vehicle_id")), StationName: strings.TrimSpace(r.FormValue("station_name")), Location: strings.TrimSpace(r.FormValue("location")), FuelType: r.FormValue("fuel_type"), Liters: parseFloat(r.FormValue("amount")), PricePerL: parseFloat(r.FormValue("price_per_liter")), TotalPrice: parseFloat(r.FormValue("total_cost")), Currency: r.FormValue("currency"), Odometer: parseInt(r.FormValue("odometer")), TripLength: parseFloat(r.FormValue("trip_length")), Notes: r.FormValue("notes"), } // Validate form if err := h.validateFuelStopForm(&form); err != nil { log.Printf("Validation error: %v", err) http.Redirect(w, r, fmt.Sprintf("/edit/%d?error=%s", id, err.Error()), http.StatusSeeOther) return } // Convert form to fuel stop updatedStop, err := form.ToFuelStop(userID) if err != nil { log.Printf("Error converting form to fuel stop: %v", err) http.Redirect(w, r, fmt.Sprintf("/edit/%d?error=Invalid+date+format", id), http.StatusSeeOther) return } // Set the ID to update existing record updatedStop.ID = id // Use station name as location if location is empty if updatedStop.Location == "" { updatedStop.Location = updatedStop.StationName } // Update in database err = h.db.UpdateFuelStop(updatedStop) if err != nil { log.Printf("Error updating fuel stop: %v", err) http.Redirect(w, r, fmt.Sprintf("/edit/%d?error=Failed+to+update+fuel+stop", id), http.StatusSeeOther) return } // Redirect to dashboard with success message http.Redirect(w, r, "/dashboard?success=Fuel+stop+updated+successfully", http.StatusSeeOther) } // DeleteFuelStopHandler handles deleting fuel stops func (h *Handler) DeleteFuelStopHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } vars := mux.Vars(r) idInt, err := strconv.Atoi(vars["id"]) if err != nil { http.Error(w, "Invalid ID", http.StatusBadRequest) return } id := uint(idInt) // Verify fuel stop exists and belongs to user fuelStop, err := h.db.GetFuelStopByID(id, userID) if err != nil { log.Printf("Error getting fuel stop: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } if fuelStop == nil { http.Error(w, "Fuel stop not found", http.StatusNotFound) return } // Delete fuel stop err = h.db.DeleteFuelStop(id, userID) if err != nil { log.Printf("Error deleting fuel stop: %v", err) http.Redirect(w, r, "/dashboard?error=Failed+to+delete+fuel+stop", http.StatusSeeOther) return } // Redirect to dashboard with success message http.Redirect(w, r, "/dashboard?success=Fuel+stop+deleted+successfully", http.StatusSeeOther) } // validateFuelStopForm validates the fuel stop form data func (h *Handler) validateFuelStopForm(form *models.FuelStopForm) error { if form.Date == "" { return fmt.Errorf("Date is required") } // Validate date format _, err := time.Parse("2006-01-02", form.Date) if err != nil { return fmt.Errorf("Invalid date format") } if form.VehicleID == 0 { return fmt.Errorf("Vehicle is required") } if form.StationName == "" && form.Location == "" { return fmt.Errorf("Station name or location is required") } if form.FuelType == "" { return fmt.Errorf("Fuel type is required") } if form.Liters <= 0 { return fmt.Errorf("Amount must be greater than 0") } if form.PricePerL <= 0 { return fmt.Errorf("Price per liter must be greater than 0") } if form.TotalPrice <= 0 { return fmt.Errorf("Total price must be greater than 0") } if form.Currency != "" && !currency.IsValidCurrency(form.Currency) { return fmt.Errorf("Invalid currency") } if form.Odometer < 0 { return fmt.Errorf("Odometer reading cannot be negative") } if form.TripLength < 0 { return fmt.Errorf("Trip length cannot be negative") } if form.TripLength > 2000 { return fmt.Errorf("Trip length cannot exceed 2000 km") } // Validate consumption if both trip length and amount are provided if form.TripLength > 0 && form.Liters > 0 { consumption := (form.Liters / form.TripLength) * 100 if consumption > 50 { return fmt.Errorf("Fuel consumption %.1f L/100km seems unrealistic. Please check trip length and amount", consumption) } if consumption < 1 { return fmt.Errorf("Fuel consumption %.1f L/100km seems too low. Please check trip length and amount", consumption) } } return nil } // Helper functions for parsing form values func parseFloat(s string) float64 { if s == "" { return 0 } f, err := strconv.ParseFloat(s, 64) if err != nil { return 0 } return f } func parseInt(s string) int { if s == "" { return 0 } i, err := strconv.Atoi(s) if err != nil { return 0 } return i } func parseUint(s string) uint { if s == "" { return 0 } i, err := strconv.ParseUint(s, 10, 32) if err != nil { return 0 } return uint(i) }