package handlers import ( "fmt" "gestion/db" "log" "net/http" "strconv" "github.com/gin-gonic/gin" ) // ============================================ // CONSTANTES DE CONFIGURATION // ============================================ const ( // Distance maximale en mètres pour valider une livraison MAX_DELIVERY_VALIDATION_DISTANCE_METERS = 100 // 100 mètres // Distance maximale en kilomètres MAX_DELIVERY_VALIDATION_DISTANCE_KM = 0.1 // 100 mètres = 0.1 km ) // ============================================ // 3️⃣ DÉMARRER UNE LIVRAISON (PASSER EN IN_ROUTE) // ============================================ // StartDelivery permet au livreur de démarrer une livraison (passage en in_transit) // POST /api/v1/deliveries/:id/start func StartDelivery(c *gin.Context) { database := c.MustGet("database").(*db.Database) username, exists := c.Get("username") if !exists || c.GetString("role") != "livreur" { c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"}) return } usernameStr := username.(string) commandID, err := strconv.Atoi(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"}) return } var req struct { Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Coordonnées GPS requises", }) return } log.Printf("🚗 [START] %s démarre livraison cmd %d", usernameStr, commandID) command, err := database.GetCommandByID(commandID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"}) return } // Vérifier propriété livreurAssign, _ := command["livreur_assign"].(string) if livreurAssign != usernameStr { c.JSON(http.StatusForbidden, gin.H{ "error": "Cette commande ne vous est pas assignée", }) return } // Vérifier le statut actuel currentStatus, _ := command["status"].(string) if currentStatus != "assigned" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Impossible de démarrer cette livraison", "current_status": currentStatus, "message": "La commande doit être en statut 'assigned'", }) return } // Mettre à jour le statut en "en_route" if err := database.UpdateCommandStatus(commandID, "en_route"); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur mise à jour statut", }) return } // Mettre à jour la position du livreur database.UpdateLivreurPosition(usernameStr, req.Latitude, req.Longitude, "busy") // Mettre à jour le statut du livreur database.SetDeliveryPersonStatus(usernameStr, "busy", commandID) // Ajouter un log database.AddCommandLog(commandID, "en_route", fmt.Sprintf("Livraison démarrée par %s", usernameStr), usernameStr) // Notifier le client if clientUsername, _ := command["username"].(string); clientUsername != "" { var msg string etaMinutes := 0 if etaData, err := database.GetCommandETA(commandID); err == nil { if v, ok := etaData["total_eta_minutes"]; ok { if n, err2 := strconv.Atoi(v); err2 == nil && n > 0 { etaMinutes = n } } } if etaMinutes == 0 && req.Latitude != 0 && req.Longitude != 0 { destLat, _ := command["dest_latitude"].(float64) destLon, _ := command["dest_longitude"].(float64) if destLat != 0 && destLon != 0 { etaMinutes = database.CalculateETAForDeliveryman(usernameStr, destLat, destLon) } } if etaMinutes > 0 { var etaStr string if etaMinutes >= 60 { h := etaMinutes / 60 m := etaMinutes % 60 if m > 0 { etaStr = fmt.Sprintf("%dh%02d", h, m) } else { etaStr = fmt.Sprintf("%dh", h) } } else { etaStr = fmt.Sprintf("%d min", etaMinutes) } msg = fmt.Sprintf("Un livreur vient de prendre en charge ta commande #%d, il est en route (~%s)", database.GetClientOrderID(commandID), etaStr) } else { msg = fmt.Sprintf("Un livreur vient de prendre en charge ta commande #%d, il est en route.", database.GetClientOrderID(commandID)) } database.NotifyClient(clientUsername, commandID, "en_route", msg) } log.Printf("✅ [START] Livraison %d démarrée", commandID) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Livraison démarrée", "command_id": commandID, "status": "en_route", }) }