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", 50000), 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", 50000)) 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", 50000)) 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", 50000), ) 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", 50000)) 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", 50000)) 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", 50000)) 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", 50000)) 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", 50000), ) 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", 50000), ) 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() mode := strings.TrimSpace(r.URL.Query().Get("mode")) mainGroup := strings.TrimSpace(r.URL.Query().Get("urun_ana_grubu")) groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels")) limit := intQuery(r, "limit", 50000) expanded := map[string]bool{} if r.Method == http.MethodPost { var body struct { Mode string `json:"mode"` GroupLevels []string `json:"group_levels"` ExpandedKeys []string `json:"expanded_keys"` Limit int `json:"limit"` MainGroup string `json:"urun_ana_grubu"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "gecersiz grup istegi: "+err.Error(), http.StatusBadRequest) return } if strings.TrimSpace(body.Mode) != "" { mode = strings.TrimSpace(body.Mode) } if strings.TrimSpace(body.MainGroup) != "" { mainGroup = strings.TrimSpace(body.MainGroup) } if len(body.GroupLevels) > 0 { groupLevels = body.GroupLevels } if body.Limit > 0 { limit = body.Limit } for _, key := range body.ExpandedKeys { key = strings.TrimSpace(key) if key != "" { expanded[key] = true } } } else { 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: mode, GroupLevels: groupLevels, ExpandedKeys: expanded, Limit: limit, MainGroup: mainGroup, }) 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" }