package routes import ( "bssapp-backend/queries" "bssapp-backend/utils" "context" "database/sql" "encoding/json" "net/http" "strconv" "strings" "time" ) func GetProductPerformanceHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second) defer cancel() f := queries.ProductPerformanceFilters{ Search: strings.TrimSpace(r.URL.Query().Get("q")), ProductCode: strings.TrimSpace(r.URL.Query().Get("product_code")), MarketKey: strings.TrimSpace(r.URL.Query().Get("market_key")), Kategori: strings.TrimSpace(r.URL.Query().Get("kategori")), Seri: strings.TrimSpace(r.URL.Query().Get("seri")), Bucket: strings.TrimSpace(r.URL.Query().Get("bucket")), SortBy: strings.TrimSpace(r.URL.Query().Get("sort_by")), Descending: boolQuery(r, "descending", true), Limit: intQuery(r, "limit", 100), Page: intQuery(r, "page", 1), } rows, total, err := queries.ListProductPerformance(ctx, pg, f) if err != nil { http.Error(w, "urun performans listesi alinamadi: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("X-Total-Count", strconv.Itoa(total)) _ = json.NewEncoder(w).Encode(map[string]any{ "rows": rows, "total_count": total, "page": f.Page, "limit": f.Limit, }) } } func GetProductPerformanceSalesDetailsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceSalesDetails( ctx, pg, strings.TrimSpace(r.URL.Query().Get("product_code")), strings.TrimSpace(r.URL.Query().Get("color_code")), strings.TrimSpace(r.URL.Query().Get("yaka_kodu")), intQuery(r, "limit", 100), ) if err != nil { http.Error(w, "urun satis detayi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceStockSizesHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) defer cancel() rows, err := queries.ListProductPerformanceStockSizes( ctx, strings.TrimSpace(r.URL.Query().Get("product_code")), strings.TrimSpace(r.URL.Query().Get("color_code")), strings.TrimSpace(r.URL.Query().Get("yaka_kodu")), ) if err != nil { http.Error(w, "urun beden stok detayi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceSummaryHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() summary, err := queries.GetProductPerformanceSummary(ctx, pg) if err != nil { http.Error(w, "urun performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(summary) } } func GetProductPerformanceGeneralHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second) defer cancel() rows, err := queries.ListProductPerformanceGeneral(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "genel urun performans KPI alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrdersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderAnalysis(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "siparis analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderGroupsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderGroups( ctx, pg, strings.TrimSpace(r.URL.Query().Get("breakdown")), intQuery(r, "limit", 500), ) if err != nil { http.Error(w, "siparis grup analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderProductCustomersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderProductCustomers(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "urun musteri siparis analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderMarketDetailsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderMarketDetails(ctx, pg, intQuery(r, "limit", 800)) if err != nil { http.Error(w, "piyasa siparis detay analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceMarketsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceMarkets(ctx, pg, intQuery(r, "limit", 100)) if err != nil { http.Error(w, "piyasa performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceCountriesHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceCountries(ctx, pg, intQuery(r, "limit", 100)) if err != nil { http.Error(w, "ulke performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceCustomersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceCustomers( ctx, pg, strings.TrimSpace(r.URL.Query().Get("breakdown")), intQuery(r, "limit", 100), ) if err != nil { http.Error(w, "musteri performans kirilimi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceSalesBreakdownHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 45*time.Second) defer cancel() rows, err := queries.ListProductPerformanceSalesBreakdown( ctx, pg, strings.TrimSpace(r.URL.Query().Get("mode")), intQuery(r, "limit", 500), ) if err != nil { http.Error(w, "satis kpi kirilimi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceGroupedHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() expanded := map[string]bool{} for _, key := range strings.Split(r.URL.Query().Get("expanded_keys"), ",") { key = strings.TrimSpace(key) if key != "" { expanded[key] = true } } rows, err := queries.ListProductPerformanceGrouped(ctx, pg, queries.ProductPerformanceGroupedRequest{ Mode: strings.TrimSpace(r.URL.Query().Get("mode")), GroupLevels: splitCSVQuery(r.URL.Query().Get("group_levels")), ExpandedKeys: expanded, Limit: intQuery(r, "limit", 50000), }) if err != nil { http.Error(w, "urun performans grup verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func splitCSVQuery(value string) []string { parts := strings.Split(value, ",") out := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(part) if part != "" { out = append(out, part) } } return out } func intQuery(r *http.Request, key string, fallback int) int { raw := strings.TrimSpace(r.URL.Query().Get(key)) if raw == "" { return fallback } n, err := strconv.Atoi(raw) if err != nil || n <= 0 { return fallback } return n } func boolQuery(r *http.Request, key string, fallback bool) bool { raw := strings.TrimSpace(strings.ToLower(r.URL.Query().Get(key))) if raw == "" { return fallback } return raw == "1" || raw == "true" || raw == "yes" || raw == "on" }