package handler import ( "database/sql" "fmt" "log" "net/http" "floorvisualizer/internal/model" "floorvisualizer/internal/repository" "floorvisualizer/internal/service" ) type RecommendResponse struct { Source model.Product `json:"source"` Products []model.Product `json:"products"` Scores []string `json:"scores"` Total int `json:"total"` } var productService *service.ProductService func initProductService(db *sql.DB) { if productService != nil { return } products, err := repository.QueryAllProducts(db) if err != nil { log.Printf("Failed to load products: %v", err) return } log.Printf("Loaded %d products for recommendation engine", len(products)) productService = service.NewProductService(products, model.DefaultEngineConfig()) } func handleRecommend(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { sku := r.URL.Query().Get("sku") if sku == "" { writeJSON(w, 400, map[string]string{"error": "sku is required"}) return } initProductService(db) if productService == nil { writeJSON(w, 500, map[string]string{"error": "Engine not initialized"}) return } source, err := repository.GetProductBySKU(db, sku) if err != nil { writeJSON(w, 404, map[string]string{"error": "product not found"}) return } // Load variants & build specs for price-aware matching if variants, _ := repository.GetVariantsByStyle(db, sku); len(variants) > 0 { source.Variants = variants allVariants := append([]model.Product{*source}, variants...) for _, v := range allVariants { source.Specs = append(source.Specs, model.ProductSpec{ SKU: v.SKU, SizeLabel: v.SizeLabel, WidthIn: v.WidthIn, LengthIn: v.LengthIn, Finish: v.Finish, PricePerSqft: v.PricePerSqft, PriceTier: v.PriceTier, CoverageSqftPerBox: v.CoverageSqftPerBox, MainImageURL: v.MainImageURL, }) } } results := productService.FindMatches(*source) prods := make([]model.Product, len(results)) scores := make([]string, len(results)) for i, r := range results { prods[i] = r.Product scores[i] = fmt.Sprintf("%.0f%%", r.Score) } writeJSON(w, 200, RecommendResponse{ Source: *source, Products: prods, Scores: scores, Total: len(prods), }) } }