package main import ( "encoding/json" "net/http" "strings" "github.com/go-redis/redis/v8" "trip-planner/internal/cache" "trip-planner/internal/routing" "trip-planner/internal/yandex" ) // HandlerContext holds the dependencies for API handlers. type HandlerContext struct { Cache cache.Cache Redis *redis.Client Router *routing.Graph Yandex *yandex.Client SearchCache *routing.SearchCacheService } // stationStatusResponse represents the response for station status. type stationStatusResponse struct { ID string `json:"id"` Name string `json:"name"` Status string `json:"status"` // "active" or "closed" Transport string `json:"transport"` // e.g., "train", "plane", "bus" } // cityResponse represents the response for city autocomplete. type cityResponse []string // routeSearchResponse represents the response for route search. type routeSearchResponse struct { Routes []interface{} `json:"routes"` Count int `json:"count"` } // routeGeoJSONResponse represents the response for route GeoJSON. type routeGeoJSONResponse struct { Type string `json:"type"` } // CityAutocomplete handles GET /v1/cities?query=. func CityAutocomplete(hc *HandlerContext, w http.ResponseWriter, r *http.Request) { query := r.URL.Query().Get("query") if query == "" { http.Error(w, "missing query parameter", http.StatusBadRequest) return } // In a full implementation, would query Postgres for city matches // For now, return a simple JSON response resp := []cityResponse{{query + "-result1", query + "-result2"}} w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } // CityStations handles GET /v1/cities/{id}/stations. func CityStations(hc *HandlerContext, w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) < 4 { http.Error(w, "invalid city ID", http.StatusBadRequest) return } _ = parts[3] // city ID captured for future use // In a full implementation, would look up city and its stations from Postgres // For now, return a simple JSON response resp := cityResponse{"station1", "station2"} w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } // RouteSearch handles POST /v1/routes/search. func RouteSearch(hc *HandlerContext, w http.ResponseWriter, r *http.Request) { var req struct { FromCityID string `json:"from_city_id"` ToCityID string `json:"to_city_id"` Date string `json:"date"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body", http.StatusBadRequest) return } // In a full implementation, would search routes using the graph and Yandex API // For now, return a simple JSON response resp := routeSearchResponse{ Routes: []interface{}{}, Count: 0, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } // RouteGeoJSON handles GET /v1/routes/{search_id}/{route_id}/geojson. func RouteGeoJSON(hc *HandlerContext, w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) < 4 { http.Error(w, "invalid route ID", http.StatusBadRequest) return } // In a full implementation, would convert route to GeoJSON // For now, return a simple JSON response resp := routeGeoJSONResponse{Type: "FeatureCollection"} w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } // StationStatus handles GET /v1/stations/{id}/status. func StationStatus(hc *HandlerContext, w http.ResponseWriter, r *http.Request) { // Extract station ID from path: /v1/stations/{id}/status parts := strings.Split(r.URL.Path, "/") // Expected: /v1/stations/{id}/status if len(parts) < 4 { http.Error(w, "invalid station ID", http.StatusBadRequest) return } stationID := parts[3] // Find the station node in the graph station := hc.Router.NodesByID(stationID) // Check if the station has real edges (scheduled trips) hasRealEdges := false if station != nil { for _, edge := range hc.Router.Edges() { if edge.From.ID == stationID || edge.To.ID == stationID { if edge.Kind == routing.EdgeKindReal { hasRealEdges = true break } } } } status := "active" if !hasRealEdges { status = "closed" } name := "" if station != nil { name = station.Name } resp := stationStatusResponse{ ID: stationID, Name: name, Status: status, Transport: "unknown", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } // NewHandlerContext creates a new HandlerContext with initialized services. func NewHandlerContext(redisClient *redis.Client, router *routing.Graph, yandex *yandex.Client) *HandlerContext { cacheStore := cache.NewCacheStore(redisClient) return &HandlerContext{ Cache: cacheStore, Redis: redisClient, Router: router, Yandex: yandex, SearchCache: routing.NewSearchCacheService(cache.NewCacheAside(cacheStore), yandex), } }