package routes

import (
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"bssapp-backend/utils"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/xuri/excelize/v2"
)

type productPerformanceExcelExportRequest struct {
	Filters    map[string][]string `json:"filters"`
	SortBy     string              `json:"sort_by"`
	Descending *bool               `json:"descending"`
}

type productPerformanceExcelColumn struct {
	Header string
	Kind   string
	Value  func(*productPerformanceExcelVariant, productPerformanceExcelStats) any
}

type productPerformanceExcelStats struct {
	AvgSalesUSD90    float64
	AvgSalesUSD180   float64
	AvgSalesUSD365   float64
	AvgSalesUSDTotal float64
}

type productPerformanceExcelVariant struct {
	KpiDate          string
	ProductCode      string
	ColorCode        string
	ColorDescription string
	YakaKodu         string
	ItemDescription  string
	Kategori         string
	Seri             string
	YasGrubu         string
	AskiliYan        string
	UrunIlkGrubu     string
	UrunAnaGrubu     string
	UrunAltGrubu     string
	StockQty         float64
	AvgStock90       float64
	AvgStock180      float64
	AvgStock365      float64
	AvgStockTotal    float64
	BasePriceUSD     float64
	CostPriceUSD     float64

	SalesQty90  float64
	SalesQty180 float64
	SalesQty365 float64
	SalesUSD90  float64
	SalesUSD180 float64
	SalesUSD365 float64

	SalesQtyTotalProduct float64
	SalesUSDTotalProduct float64
	SalesQtyTotalGeneral float64
	SalesUSDTotalGeneral float64
	HasGeneralTotal      bool

	MarketCount90      int
	CustomerCount90    int
	MarketCountTotal   int
	CustomerCountTotal int
	Markets90          map[string]bool
	MarketsTotal       map[string]bool
	MarketFiltered     bool

	PerformanceBucket string
	Recommendation    string
	LastSaleDate      string
	LastRefNumber     string
	UpdatedAt         string
}

func ExportProductPerformanceExcelHandler(pg *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		traceID := utils.TraceIDFromRequest(r)
		ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
		defer cancel()

		var req productPerformanceExcelExportRequest
		if r.Method == http.MethodPost {
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				http.Error(w, "gecersiz excel istegi: "+err.Error(), http.StatusBadRequest)
				return
			}
		}

		productRows, _, err := queries.ListProductPerformance(ctx, pg, queries.ProductPerformanceFilters{
			Limit:      50000,
			Page:       1,
			SortBy:     "product_code",
			Descending: false,
		})
		if err != nil {
			http.Error(w, "urun performans excel listesi alinamadi: "+err.Error(), http.StatusInternalServerError)
			return
		}

		generalRows, err := queries.ListProductPerformanceGeneral(ctx, pg, 50000)
		if err != nil {
			http.Error(w, "urun performans genel excel listesi alinamadi: "+err.Error(), http.StatusInternalServerError)
			return
		}

		rows := buildProductPerformanceExcelVariants(productRows, generalRows, req.Filters)
		sortProductPerformanceExcelVariants(rows, req.SortBy, req.Descending == nil || *req.Descending)
		stats := productPerformanceExcelStatsFor(rows)

		file, err := buildProductPerformanceExcelFile(rows, stats)
		if err != nil {
			http.Error(w, "urun performans excel dosyasi hazirlanamadi: "+err.Error(), http.StatusInternalServerError)
			return
		}
		defer func() { _ = file.Close() }()

		buf, err := file.WriteToBuffer()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		filename := fmt.Sprintf("product_performance_renk_yaka_%s.xlsx", time.Now().Format("20060102_150405"))
		w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
		w.Header().Set("Content-Disposition", "attachment; filename=\""+filename+"\"")
		w.Header().Set("Content-Length", fmt.Sprint(len(buf.Bytes())))
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(buf.Bytes())
	}
}

func buildProductPerformanceExcelVariants(productRows []models.ProductPerformanceRow, generalRows []models.ProductPerformanceGeneralRow, filters map[string][]string) []*productPerformanceExcelVariant {
	byKey := map[string]*productPerformanceExcelVariant{}
	marketFiltered := productPerformanceExcelHasFilter(filters, "market_key")
	for _, row := range productRows {
		if !productPerformanceExcelProductRowMatchesFilters(row, filters) {
			continue
		}
		key := productPerformanceExcelVariantKey(row.ProductCode, row.ColorCode, row.YakaKodu)
		v := byKey[key]
		if v == nil {
			v = &productPerformanceExcelVariant{Markets90: map[string]bool{}, MarketsTotal: map[string]bool{}, MarketFiltered: marketFiltered}
			byKey[key] = v
		}
		v.addProductRow(row)
	}
	for _, row := range generalRows {
		if !productPerformanceExcelGeneralRowMatchesFilters(row, filters) {
			continue
		}
		key := productPerformanceExcelVariantKey(row.ProductCode, row.ColorCode, row.YakaKodu)
		v := byKey[key]
		if v == nil {
			v = &productPerformanceExcelVariant{Markets90: map[string]bool{}, MarketsTotal: map[string]bool{}, MarketFiltered: marketFiltered}
			byKey[key] = v
		}
		v.addGeneralRow(row)
	}

	out := make([]*productPerformanceExcelVariant, 0, len(byKey))
	for _, row := range byKey {
		if productPerformanceExcelVariantMatchesPostFilters(row, filters) {
			out = append(out, row)
		}
	}
	return out
}

func (v *productPerformanceExcelVariant) addProductRow(row models.ProductPerformanceRow) {
	v.fillDimensions(
		row.KpiDate, row.ProductCode, row.ColorCode, row.ColorDescription, row.YakaKodu, row.ItemDescription,
		row.Kategori, row.Seri, row.YasGrubu, row.AskiliYan, row.UrunIlkGrubu, row.UrunAnaGrubu, row.UrunAltGrubu,
		row.BasePriceUSD, row.CostPriceUSD, row.StockQty, row.LastSaleDate, row.LastRefNumber, row.UpdatedAt,
	)
	v.SalesQty90 += row.SalesQty90
	v.SalesQty180 += row.SalesQty180
	v.SalesQty365 += row.SalesQty365
	v.SalesUSD90 += row.SalesUSD90
	v.SalesUSD180 += row.SalesUSD180
	v.SalesUSD365 += row.SalesUSD365
	v.AvgStock90 = productPerformanceExcelFirstNonZero(v.AvgStock90, row.AvgStock90)
	v.AvgStock180 = productPerformanceExcelFirstNonZero(v.AvgStock180, row.AvgStock180)
	v.AvgStock365 = productPerformanceExcelFirstNonZero(v.AvgStock365, row.AvgStock365)
	v.AvgStockTotal = productPerformanceExcelFirstNonZero(v.AvgStockTotal, row.AvgStockTotal)
	v.SalesQtyTotalProduct += row.SalesQtyTotal
	v.SalesUSDTotalProduct += row.SalesUSDTotal
	v.MarketCount90 = productPerformanceExcelMaxInt(v.MarketCount90, row.MarketCount90)
	if row.SalesUSD90 > 0 {
		v.CustomerCount90 += row.CustomerCount90
	}
	v.PerformanceBucket = productPerformanceExcelFirstNonEmpty(v.PerformanceBucket, row.PerformanceBucket)
	v.Recommendation = productPerformanceExcelFirstNonEmpty(v.Recommendation, row.Recommendation)
	v.addMarket(row.MarketKey, row.SalesQty90, row.SalesUSD90, row.SalesQtyTotal, row.SalesUSDTotal)
}

func (v *productPerformanceExcelVariant) addGeneralRow(row models.ProductPerformanceGeneralRow) {
	v.fillDimensions(
		productPerformanceExcelFirstNonEmpty(row.PeriodEnd, row.PeriodStart), row.ProductCode, row.ColorCode, row.ColorDescription, row.YakaKodu, row.ItemDescription,
		row.Kategori, row.Seri, row.YasGrubu, row.AskiliYan, row.UrunIlkGrubu, row.UrunAnaGrubu, row.UrunAltGrubu,
		row.BasePriceUSD, row.CostPriceUSD, row.StockQty, row.LastSaleDate, row.LastRefNumber, "",
	)
	v.SalesQtyTotalGeneral += row.SalesQtyTotal
	v.SalesUSDTotalGeneral += row.SalesUSDTotal
	v.HasGeneralTotal = true
	v.MarketCountTotal = productPerformanceExcelMaxInt(v.MarketCountTotal, row.MarketCountTotal)
	if row.SalesUSDTotal > 0 {
		v.CustomerCountTotal += row.CustomerCountTotal
	}
	v.PerformanceBucket = productPerformanceExcelFirstNonEmpty(v.PerformanceBucket, row.PerformanceBucket)
	v.Recommendation = productPerformanceExcelFirstNonEmpty(v.Recommendation, row.Recommendation)
	v.addMarket(row.MarketKey, 0, 0, row.SalesQtyTotal, row.SalesUSDTotal)
}

func (v *productPerformanceExcelVariant) fillDimensions(kpiDate, productCode, colorCode, colorDescription, yakaKodu, itemDescription, kategori, seri, yasGrubu, askiliYan, urunIlkGrubu, urunAnaGrubu, urunAltGrubu string, basePrice, costPrice, stockQty float64, lastSaleDate, lastRefNumber, updatedAt string) {
	v.KpiDate = productPerformanceExcelFirstNonEmpty(v.KpiDate, kpiDate)
	v.ProductCode = productPerformanceExcelFirstNonEmpty(v.ProductCode, productCode)
	v.ColorCode = productPerformanceExcelFirstNonEmpty(v.ColorCode, colorCode)
	v.ColorDescription = productPerformanceExcelFirstNonEmpty(v.ColorDescription, colorDescription)
	v.YakaKodu = productPerformanceExcelFirstNonEmpty(v.YakaKodu, yakaKodu)
	v.ItemDescription = productPerformanceExcelFirstNonEmpty(v.ItemDescription, itemDescription)
	v.Kategori = productPerformanceExcelFirstNonEmpty(v.Kategori, kategori)
	v.Seri = productPerformanceExcelFirstNonEmpty(v.Seri, seri)
	v.YasGrubu = productPerformanceExcelFirstNonEmpty(v.YasGrubu, yasGrubu)
	v.AskiliYan = productPerformanceExcelFirstNonEmpty(v.AskiliYan, productPerformanceExcelCleanOptional(askiliYan))
	v.UrunIlkGrubu = productPerformanceExcelFirstNonEmpty(v.UrunIlkGrubu, productPerformanceExcelCleanOptional(urunIlkGrubu))
	v.UrunAnaGrubu = productPerformanceExcelFirstNonEmpty(v.UrunAnaGrubu, urunAnaGrubu)
	v.UrunAltGrubu = productPerformanceExcelFirstNonEmpty(v.UrunAltGrubu, urunAltGrubu)
	v.BasePriceUSD = productPerformanceExcelFirstNonZero(v.BasePriceUSD, basePrice)
	v.CostPriceUSD = productPerformanceExcelFirstNonZero(v.CostPriceUSD, costPrice)
	v.CostPriceUSD, v.BasePriceUSD = productPerformanceExcelNormalizeCostPair(v.CostPriceUSD, v.BasePriceUSD)
	if stockQty > v.StockQty {
		v.StockQty = stockQty
	}
	if lastSaleDate > v.LastSaleDate {
		v.LastSaleDate = lastSaleDate
		v.LastRefNumber = lastRefNumber
	}
	if updatedAt > v.UpdatedAt {
		v.UpdatedAt = updatedAt
	}
}

func (v *productPerformanceExcelVariant) addMarket(marketKey string, salesQty90, salesUSD90, salesQtyTotal, salesUSDTotal float64) {
	market := productPerformanceExcelMarketName(marketKey)
	if market == "" || market == "STOK" {
		return
	}
	if salesUSD90 > 0 {
		v.Markets90[market] = true
	}
	if salesUSDTotal > 0 {
		v.MarketsTotal[market] = true
	}
}

func (v *productPerformanceExcelVariant) totalQty() float64 {
	if v.HasGeneralTotal {
		return v.SalesQtyTotalGeneral
	}
	return v.SalesQtyTotalProduct
}

func (v *productPerformanceExcelVariant) totalUSD() float64 {
	if v.HasGeneralTotal {
		return v.SalesUSDTotalGeneral
	}
	return v.SalesUSDTotalProduct
}

func (v *productPerformanceExcelVariant) marketCount90() int {
	if v.MarketFiltered {
		return len(v.Markets90)
	}
	return productPerformanceExcelMaxInt(v.MarketCount90, len(v.Markets90))
}

func (v *productPerformanceExcelVariant) marketCountTotal() int {
	if v.MarketFiltered {
		return len(v.MarketsTotal)
	}
	return productPerformanceExcelMaxInt(v.MarketCountTotal, len(v.MarketsTotal))
}

func (v *productPerformanceExcelVariant) marketsLabel() string {
	return productPerformanceExcelSortedSetLabel(v.MarketsTotal)
}

func buildProductPerformanceExcelFile(rows []*productPerformanceExcelVariant, stats productPerformanceExcelStats) (*excelize.File, error) {
	f := excelize.NewFile()
	sheet := "RenkYaka"
	f.SetSheetName("Sheet1", sheet)

	columns := productPerformanceExcelColumns()
	headerStyle, _ := f.NewStyle(&excelize.Style{
		Font:      &excelize.Font{Bold: true, Color: "FFFFFF"},
		Fill:      excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"1F4E78"}},
		Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
	})
	textStyle, _ := f.NewStyle(&excelize.Style{NumFmt: 49})
	intStyle, _ := f.NewStyle(&excelize.Style{NumFmt: 3})
	decimalStyle, _ := f.NewStyle(&excelize.Style{NumFmt: 4})
	percentStyle, _ := f.NewStyle(&excelize.Style{NumFmt: 10})

	for i, col := range columns {
		cell, _ := excelize.CoordinatesToCellName(i+1, 1)
		_ = f.SetCellStr(sheet, cell, col.Header)
	}
	if len(columns) > 0 {
		lastHeader, _ := excelize.CoordinatesToCellName(len(columns), 1)
		_ = f.SetCellStyle(sheet, "A1", lastHeader, headerStyle)
	}

	for rowIndex, row := range rows {
		excelRow := rowIndex + 2
		for colIndex, col := range columns {
			cell, _ := excelize.CoordinatesToCellName(colIndex+1, excelRow)
			value := col.Value(row, stats)
			if col.Kind == "text" {
				_ = f.SetCellStr(sheet, cell, strings.TrimSpace(fmt.Sprint(value)))
			} else {
				_ = f.SetCellValue(sheet, cell, productPerformanceExcelFloat(value))
			}
		}
	}

	lastRow := productPerformanceExcelMaxInt(len(rows)+1, 2)
	if len(columns) > 0 {
		lastFilterCell, _ := excelize.CoordinatesToCellName(len(columns), lastRow)
		_ = f.AutoFilter(sheet, "A1:"+lastFilterCell, []excelize.AutoFilterOptions{})
	}
	for i, col := range columns {
		colName, _ := excelize.ColumnNumberToName(i + 1)
		_ = f.SetColWidth(sheet, colName, colName, productPerformanceExcelColumnWidth(col.Header, col.Kind))
		style := decimalStyle
		switch col.Kind {
		case "text":
			style = textStyle
		case "int":
			style = intStyle
		case "percent":
			style = percentStyle
		}
		_ = f.SetCellStyle(sheet, fmt.Sprintf("%s2", colName), fmt.Sprintf("%s%d", colName, lastRow), style)
	}
	_ = f.SetPanes(sheet, &excelize.Panes{Freeze: true, YSplit: 1, TopLeftCell: "A2", ActivePane: "bottomLeft"})
	return f, nil
}

func productPerformanceExcelColumns() []productPerformanceExcelColumn {
	base := []productPerformanceExcelColumn{
		textExcelColumn("KPI Tarihi", func(v *productPerformanceExcelVariant) any { return v.KpiDate }),
		textExcelColumn("Ürün İlk Grubu", func(v *productPerformanceExcelVariant) any { return v.UrunIlkGrubu }),
		textExcelColumn("Askılı/Yan", func(v *productPerformanceExcelVariant) any { return v.AskiliYan }),
		textExcelColumn("Kategori", func(v *productPerformanceExcelVariant) any { return v.Kategori }),
		textExcelColumn("Ürün Ana Grubu", func(v *productPerformanceExcelVariant) any { return v.UrunAnaGrubu }),
		textExcelColumn("Ürün Alt Grubu", func(v *productPerformanceExcelVariant) any { return v.UrunAltGrubu }),
		textExcelColumn("Ürün", func(v *productPerformanceExcelVariant) any { return v.ProductCode }),
		textExcelColumn("Açıklama", func(v *productPerformanceExcelVariant) any { return v.ItemDescription }),
		textExcelColumn("Renk", func(v *productPerformanceExcelVariant) any { return v.ColorCode }),
		textExcelColumn("Renk Açıklama", func(v *productPerformanceExcelVariant) any { return v.ColorDescription }),
		textExcelColumn("Yaka", func(v *productPerformanceExcelVariant) any { return v.YakaKodu }),
		textExcelColumn("Renk/Yaka", func(v *productPerformanceExcelVariant) any {
			return productPerformanceExcelColorYaka(v.ColorCode, v.ColorDescription, v.YakaKodu)
		}),
		textExcelColumn("Piyasalar", func(v *productPerformanceExcelVariant) any { return v.marketsLabel() }),
		numberExcelColumn("Toplam Stok", "int", func(v *productPerformanceExcelVariant) any { return v.StockQty }),
	}
	base = append(base, productPerformanceExcelPeriodColumns("90G", "90d", func(v *productPerformanceExcelVariant) (float64, float64, int, int) {
		return v.SalesQty90, v.SalesUSD90, v.marketCount90(), v.CustomerCount90
	})...)
	base = append(base, productPerformanceExcelPeriodColumns("180G", "180d", func(v *productPerformanceExcelVariant) (float64, float64, int, int) {
		return v.SalesQty180, v.SalesUSD180, v.marketCount90(), v.CustomerCount90
	})...)
	base = append(base, productPerformanceExcelPeriodColumns("360G", "365d", func(v *productPerformanceExcelVariant) (float64, float64, int, int) {
		return v.SalesQty365, v.SalesUSD365, v.marketCount90(), v.CustomerCount90
	})...)
	base = append(base, productPerformanceExcelPeriodColumns("Genel", "total", func(v *productPerformanceExcelVariant) (float64, float64, int, int) {
		return v.totalQty(), v.totalUSD(), v.marketCountTotal(), v.CustomerCountTotal
	})...)
	base = append(base,
		textExcelColumn("Durum", func(v *productPerformanceExcelVariant) any { return productPerformanceExcelStatus(v) }),
		textExcelColumn("Öneri", func(v *productPerformanceExcelVariant) any { return productPerformanceExcelRecommendation(v) }),
		textExcelColumn("Son Satış", func(v *productPerformanceExcelVariant) any { return v.LastSaleDate }),
		textExcelColumn("Son Ref", func(v *productPerformanceExcelVariant) any { return v.LastRefNumber }),
		textExcelColumn("Güncelleme", func(v *productPerformanceExcelVariant) any { return v.UpdatedAt }),
	)
	return base
}

func productPerformanceExcelPeriodColumns(label, suffix string, values func(*productPerformanceExcelVariant) (float64, float64, int, int)) []productPerformanceExcelColumn {
	return []productPerformanceExcelColumn{
		numberExcelColumn(label+" Toplam Adet", "int", func(v *productPerformanceExcelVariant) any {
			qty, _, _, _ := values(v)
			return qty
		}),
		numberExcelColumn(label+" Toplam Ciro USD", "number", func(v *productPerformanceExcelVariant) any {
			_, usd, _, _ := values(v)
			return usd
		}),
		numberExcelColumn(label+" Ort. Satış Fiyatı USD", "number", func(v *productPerformanceExcelVariant) any {
			qty, usd, _, _ := values(v)
			return productPerformanceExcelAvgPrice(usd, qty)
		}),
		numberExcelColumn(label+" Yıllık Stok Devir", "number", func(v *productPerformanceExcelVariant) any {
			qty, _, _, _ := values(v)
			return productPerformanceExcelStockTurnover(qty, productPerformanceExcelAvgStock(v, suffix), productPerformanceExcelPeriodDays(v, suffix))
		}),
		numberExcelColumn(label+" Taban Maliyet USD", "number", func(v *productPerformanceExcelVariant) any { return v.BasePriceUSD }),
		numberExcelColumn(label+" Çıplak Maliyet USD", "number", func(v *productPerformanceExcelVariant) any { return v.CostPriceUSD }),
		numberExcelColumn(label+" Taban Toplam K/Z USD", "number", func(v *productPerformanceExcelVariant) any {
			qty, usd, _, _ := values(v)
			return productPerformanceExcelGrossProfit(usd, qty, v.BasePriceUSD)
		}),
		numberExcelColumn(label+" Çıplak Toplam K/Z USD", "number", func(v *productPerformanceExcelVariant) any {
			qty, usd, _, _ := values(v)
			return productPerformanceExcelGrossProfit(usd, qty, v.CostPriceUSD)
		}),
		numberExcelColumn(label+" Taban Marj", "percent", func(v *productPerformanceExcelVariant) any {
			qty, usd, _, _ := values(v)
			return productPerformanceExcelMargin(usd, qty, v.BasePriceUSD)
		}),
		numberExcelColumn(label+" Çıplak Marj", "percent", func(v *productPerformanceExcelVariant) any {
			qty, usd, _, _ := values(v)
			return productPerformanceExcelMargin(usd, qty, v.CostPriceUSD)
		}),
		numberExcelColumn(label+" Tekil Piyasa", "int", func(v *productPerformanceExcelVariant) any {
			_, _, markets, _ := values(v)
			return markets
		}),
		numberExcelColumn(label+" Tekil Müşteri", "int", func(v *productPerformanceExcelVariant) any {
			_, _, _, customers := values(v)
			return customers
		}),
		numberExcelColumn(label+" Ürün Skor", "number", func(v *productPerformanceExcelVariant) any {
			qty, usd, markets, customers := values(v)
			days := productPerformanceExcelPeriodDays(v, suffix)
			return productPerformanceExcelProductScore(suffix, usd, productPerformanceExcelStockTurnover(qty, productPerformanceExcelAvgStock(v, suffix), days), float64(markets), float64(customers), productPerformanceExcelMargin(usd, qty, v.CostPriceUSD), days)
		}),
	}
}

func textExcelColumn(header string, value func(*productPerformanceExcelVariant) any) productPerformanceExcelColumn {
	return productPerformanceExcelColumn{Header: header, Kind: "text", Value: func(v *productPerformanceExcelVariant, _ productPerformanceExcelStats) any { return value(v) }}
}

func numberExcelColumn(header, kind string, value func(*productPerformanceExcelVariant) any) productPerformanceExcelColumn {
	return productPerformanceExcelColumn{Header: header, Kind: kind, Value: func(v *productPerformanceExcelVariant, _ productPerformanceExcelStats) any { return value(v) }}
}

func productPerformanceExcelProductRowMatchesFilters(row models.ProductPerformanceRow, filters map[string][]string) bool {
	for field, selected := range productPerformanceExcelFilterSets(filters) {
		if field == "performance_bucket" {
			continue
		}
		if !productPerformanceExcelMatchesAny(selected, productPerformanceExcelProductFilterCandidates(row, field)) {
			return false
		}
	}
	return true
}

func productPerformanceExcelGeneralRowMatchesFilters(row models.ProductPerformanceGeneralRow, filters map[string][]string) bool {
	for field, selected := range productPerformanceExcelFilterSets(filters) {
		if field == "performance_bucket" {
			continue
		}
		if !productPerformanceExcelMatchesAny(selected, productPerformanceExcelGeneralFilterCandidates(row, field)) {
			return false
		}
	}
	return true
}

func productPerformanceExcelVariantMatchesPostFilters(row *productPerformanceExcelVariant, filters map[string][]string) bool {
	selected := productPerformanceExcelFilterSets(filters)["performance_bucket"]
	if len(selected) == 0 {
		return true
	}
	return productPerformanceExcelMatchesAny(selected, []string{row.PerformanceBucket, productPerformanceExcelStatus(row)})
}

func productPerformanceExcelFilterSets(filters map[string][]string) map[string]map[string]bool {
	out := map[string]map[string]bool{}
	for field, values := range filters {
		field = strings.TrimSpace(field)
		for _, value := range values {
			value = productPerformanceExcelNormalize(value)
			if value == "" {
				continue
			}
			if out[field] == nil {
				out[field] = map[string]bool{}
			}
			out[field][value] = true
		}
	}
	return out
}

func productPerformanceExcelHasFilter(filters map[string][]string, field string) bool {
	for _, value := range filters[strings.TrimSpace(field)] {
		if strings.TrimSpace(value) != "" {
			return true
		}
	}
	return false
}

func productPerformanceExcelProductFilterCandidates(row models.ProductPerformanceRow, field string) []string {
	switch strings.TrimSpace(field) {
	case "product_code":
		return []string{row.ProductCode}
	case "item_description":
		return []string{row.ItemDescription}
	case "color_code":
		return []string{row.ColorCode}
	case "yaka_kodu":
		return []string{row.YakaKodu}
	case "color_yaka":
		return []string{productPerformanceExcelColorYaka(row.ColorCode, row.ColorDescription, row.YakaKodu), row.ColorCode + "/" + row.YakaKodu}
	case "kategori":
		return []string{row.Kategori}
	case "askili_yan":
		return []string{productPerformanceExcelCleanOptional(row.AskiliYan)}
	case "urun_ilk_grubu":
		return []string{productPerformanceExcelCleanOptional(row.UrunIlkGrubu)}
	case "urun_ana_grubu":
		return []string{row.UrunAnaGrubu}
	case "urun_alt_grubu":
		return []string{row.UrunAltGrubu}
	case "market_key":
		return []string{row.MarketKey, productPerformanceExcelMarketName(row.MarketKey)}
	default:
		return nil
	}
}

func productPerformanceExcelGeneralFilterCandidates(row models.ProductPerformanceGeneralRow, field string) []string {
	switch strings.TrimSpace(field) {
	case "product_code":
		return []string{row.ProductCode}
	case "item_description":
		return []string{row.ItemDescription}
	case "color_code":
		return []string{row.ColorCode}
	case "yaka_kodu":
		return []string{row.YakaKodu}
	case "color_yaka":
		return []string{productPerformanceExcelColorYaka(row.ColorCode, row.ColorDescription, row.YakaKodu), row.ColorCode + "/" + row.YakaKodu}
	case "kategori":
		return []string{row.Kategori}
	case "askili_yan":
		return []string{productPerformanceExcelCleanOptional(row.AskiliYan)}
	case "urun_ilk_grubu":
		return []string{productPerformanceExcelCleanOptional(row.UrunIlkGrubu)}
	case "urun_ana_grubu":
		return []string{row.UrunAnaGrubu}
	case "urun_alt_grubu":
		return []string{row.UrunAltGrubu}
	case "market_key":
		return []string{row.MarketKey, productPerformanceExcelMarketName(row.MarketKey)}
	default:
		return nil
	}
}

func productPerformanceExcelMatchesAny(selected map[string]bool, candidates []string) bool {
	if len(selected) == 0 {
		return true
	}
	for _, candidate := range candidates {
		if selected[productPerformanceExcelNormalize(candidate)] {
			return true
		}
	}
	return false
}

func sortProductPerformanceExcelVariants(rows []*productPerformanceExcelVariant, sortBy string, desc bool) {
	sortBy = strings.TrimSpace(sortBy)
	if sortBy == "" || sortBy == "image" {
		sortBy = "product_code"
		desc = false
	}
	sort.SliceStable(rows, func(i, j int) bool {
		cmp := productPerformanceExcelCompare(rows[i], rows[j], sortBy)
		if cmp == 0 {
			cmp = strings.Compare(productPerformanceExcelHierarchySortKey(rows[i]), productPerformanceExcelHierarchySortKey(rows[j]))
		}
		if desc {
			return cmp > 0
		}
		return cmp < 0
	})
}

func productPerformanceExcelCompare(a, b *productPerformanceExcelVariant, sortBy string) int {
	if productPerformanceExcelIsNumericSort(sortBy) {
		av := productPerformanceExcelSortNumber(a, sortBy)
		bv := productPerformanceExcelSortNumber(b, sortBy)
		if av < bv {
			return -1
		}
		if av > bv {
			return 1
		}
		return 0
	}
	return strings.Compare(productPerformanceExcelNormalize(productPerformanceExcelSortText(a, sortBy)), productPerformanceExcelNormalize(productPerformanceExcelSortText(b, sortBy)))
}

func productPerformanceExcelIsNumericSort(sortBy string) bool {
	switch sortBy {
	case "stock_qty", "sales_qty_90d", "sales_qty_180d", "sales_qty_365d", "sales_qty_total",
		"sales_usd_90d", "sales_usd_180d", "sales_usd_365d", "sales_usd_total",
		"avg_price_usd_90d", "avg_price_usd_180d", "avg_price_usd_365d", "avg_price_usd_total",
		"stock_turnover_90d", "stock_turnover_180d", "stock_turnover_365d", "stock_turnover_total",
		"base_price_usd", "cost_price_usd", "market_count_90d", "customer_count_90d", "market_count_total", "customer_count_total",
		"performance_score", "performance_score_90d", "performance_score_total":
		return true
	default:
		return false
	}
}

func productPerformanceExcelSortNumber(row *productPerformanceExcelVariant, sortBy string) float64 {
	switch sortBy {
	case "stock_qty":
		return row.StockQty
	case "sales_qty_90d":
		return row.SalesQty90
	case "sales_qty_180d":
		return row.SalesQty180
	case "sales_qty_365d":
		return row.SalesQty365
	case "sales_qty_total":
		return row.totalQty()
	case "sales_usd_90d":
		return row.SalesUSD90
	case "sales_usd_180d":
		return row.SalesUSD180
	case "sales_usd_365d":
		return row.SalesUSD365
	case "sales_usd_total":
		return row.totalUSD()
	case "avg_price_usd_90d":
		return productPerformanceExcelAvgPrice(row.SalesUSD90, row.SalesQty90)
	case "avg_price_usd_180d":
		return productPerformanceExcelAvgPrice(row.SalesUSD180, row.SalesQty180)
	case "avg_price_usd_365d":
		return productPerformanceExcelAvgPrice(row.SalesUSD365, row.SalesQty365)
	case "avg_price_usd_total":
		return productPerformanceExcelAvgPrice(row.totalUSD(), row.totalQty())
	case "stock_turnover_90d":
		return productPerformanceExcelStockTurnover(row.SalesQty90, productPerformanceExcelAvgStock(row, "90d"), productPerformanceExcelPeriodDays(row, "90d"))
	case "stock_turnover_180d":
		return productPerformanceExcelStockTurnover(row.SalesQty180, productPerformanceExcelAvgStock(row, "180d"), productPerformanceExcelPeriodDays(row, "180d"))
	case "stock_turnover_365d":
		return productPerformanceExcelStockTurnover(row.SalesQty365, productPerformanceExcelAvgStock(row, "365d"), productPerformanceExcelPeriodDays(row, "365d"))
	case "stock_turnover_total":
		return productPerformanceExcelStockTurnover(row.totalQty(), productPerformanceExcelAvgStock(row, "total"), productPerformanceExcelPeriodDays(row, "total"))
	case "base_price_usd":
		return row.BasePriceUSD
	case "cost_price_usd":
		return row.CostPriceUSD
	case "market_count_90d":
		return float64(row.marketCount90())
	case "customer_count_90d":
		return float64(row.CustomerCount90)
	case "market_count_total":
		return float64(row.marketCountTotal())
	case "customer_count_total":
		return float64(row.CustomerCountTotal)
	case "performance_score", "performance_score_90d":
		days := productPerformanceExcelPeriodDays(row, "90d")
		return productPerformanceExcelProductScore("90d", row.SalesUSD90, productPerformanceExcelStockTurnover(row.SalesQty90, productPerformanceExcelAvgStock(row, "90d"), days), float64(row.marketCount90()), float64(row.CustomerCount90), productPerformanceExcelMargin(row.SalesUSD90, row.SalesQty90, row.CostPriceUSD), days)
	case "performance_score_total":
		days := productPerformanceExcelPeriodDays(row, "total")
		return productPerformanceExcelProductScore("total", row.totalUSD(), productPerformanceExcelStockTurnover(row.totalQty(), productPerformanceExcelAvgStock(row, "total"), days), float64(row.marketCountTotal()), float64(row.CustomerCountTotal), productPerformanceExcelMargin(row.totalUSD(), row.totalQty(), row.CostPriceUSD), days)
	default:
		return 0
	}
}

func productPerformanceExcelSortText(row *productPerformanceExcelVariant, sortBy string) string {
	switch sortBy {
	case "product_code":
		return row.ProductCode
	case "color_yaka":
		return productPerformanceExcelColorYaka(row.ColorCode, row.ColorDescription, row.YakaKodu)
	case "color_code":
		return row.ColorCode
	case "yaka_kodu":
		return row.YakaKodu
	case "item_description":
		return row.ItemDescription
	case "kategori":
		return row.Kategori
	case "askili_yan":
		return row.AskiliYan
	case "urun_ilk_grubu":
		return row.UrunIlkGrubu
	case "urun_ana_grubu":
		return row.UrunAnaGrubu
	case "urun_alt_grubu":
		return row.UrunAltGrubu
	case "market_key":
		return row.marketsLabel()
	case "performance_bucket":
		return productPerformanceExcelStatus(row)
	default:
		return productPerformanceExcelHierarchySortKey(row)
	}
}

func productPerformanceExcelStatsFor(rows []*productPerformanceExcelVariant) productPerformanceExcelStats {
	var stats productPerformanceExcelStats
	var c90, c180, c365, cTotal float64
	for _, row := range rows {
		if row.SalesUSD90 > 0 {
			stats.AvgSalesUSD90 += row.SalesUSD90
			c90++
		}
		if row.SalesUSD180 > 0 {
			stats.AvgSalesUSD180 += row.SalesUSD180
			c180++
		}
		if row.SalesUSD365 > 0 {
			stats.AvgSalesUSD365 += row.SalesUSD365
			c365++
		}
		if row.totalUSD() > 0 {
			stats.AvgSalesUSDTotal += row.totalUSD()
			cTotal++
		}
	}
	if c90 > 0 {
		stats.AvgSalesUSD90 /= c90
	}
	if c180 > 0 {
		stats.AvgSalesUSD180 /= c180
	}
	if c365 > 0 {
		stats.AvgSalesUSD365 /= c365
	}
	if cTotal > 0 {
		stats.AvgSalesUSDTotal /= cTotal
	}
	return stats
}

func productPerformanceExcelVariantKey(productCode, colorCode, yakaKodu string) string {
	return strings.ToUpper(strings.TrimSpace(productCode)) + "|" + strings.ToUpper(strings.TrimSpace(colorCode)) + "|" + strings.ToUpper(strings.TrimSpace(yakaKodu))
}

func productPerformanceExcelHierarchySortKey(row *productPerformanceExcelVariant) string {
	return strings.Join([]string{row.UrunIlkGrubu, row.AskiliYan, row.Kategori, row.UrunAnaGrubu, row.UrunAltGrubu, row.ProductCode, row.ColorCode, row.YakaKodu}, "|")
}

func productPerformanceExcelColorYaka(colorCode, colorDescription, yakaKodu string) string {
	color := strings.TrimSpace(colorCode)
	if desc := strings.TrimSpace(colorDescription); color != "" && desc != "" {
		color = color + "-" + strings.ToUpper(desc)
	}
	yaka := strings.TrimSpace(yakaKodu)
	parts := make([]string, 0, 2)
	if color != "" && color != "-" {
		parts = append(parts, color)
	}
	if yaka != "" && yaka != "-" {
		parts = append(parts, yaka)
	}
	return strings.Join(parts, "/")
}

func productPerformanceExcelMarketName(value string) string {
	parts := strings.Split(value, "|")
	for i := len(parts) - 1; i >= 0; i-- {
		part := strings.TrimSpace(parts[i])
		if part != "" {
			return part
		}
	}
	return ""
}

func productPerformanceExcelStatus(row *productPerformanceExcelVariant) string {
	switch {
	case row.totalQty() > 0 && row.totalUSD() <= 0:
		return "Ciro/Fiyat Kontrol"
	case row.totalQty() <= 0 && row.StockQty > 0:
		return "Stok Riski"
	case productPerformanceExcelMargin(row.totalUSD(), row.totalQty(), row.CostPriceUSD) < 0:
		return "Fiyat Baskısı"
	case productPerformanceExcelProductScore("total", row.totalUSD(), productPerformanceExcelStockTurnover(row.totalQty(), productPerformanceExcelAvgStock(row, "total"), productPerformanceExcelPeriodDays(row, "total")), float64(row.marketCountTotal()), float64(row.CustomerCountTotal), productPerformanceExcelMargin(row.totalUSD(), row.totalQty(), row.CostPriceUSD), productPerformanceExcelPeriodDays(row, "total")) >= 70:
		return "Yıldız Ürün"
	default:
		return productPerformanceExcelBucketLabel(row.PerformanceBucket)
	}
}

func productPerformanceExcelRecommendation(row *productPerformanceExcelVariant) string {
	switch {
	case row.totalQty() > 0 && row.totalUSD() <= 0:
		return "Satış adedi var ama USD ciro/fiyat yok. Satış tutarı aktarımı ve döviz dönüşümü kontrol edilmeli."
	case row.totalQty() <= 0 && row.StockQty > 0:
		return "Satış yok, stok duruyor. Piyasa/fiyat aksiyonu gerekli."
	case row.Recommendation != "":
		return row.Recommendation
	default:
		return "Düzenli takip."
	}
}

func productPerformanceExcelBucketLabel(value string) string {
	switch strings.TrimSpace(value) {
	case "YILDIZ_URUN":
		return "Yıldız Ürün"
	case "STOKSUZ_TALEP":
		return "Stoksuz Talep"
	case "STOK_RISKI":
		return "Stok Riski"
	case "FIYAT_BASKISI":
		return "Fiyat Baskısı"
	case "FIYAT_FIRSATI":
		return "Fiyat Fırsatı"
	case "MALIYET_YOK":
		return "Maliyet Yok"
	case "TAKIP":
		return "Takip"
	default:
		return strings.TrimSpace(value)
	}
}

func productPerformanceExcelCleanOptional(value string) string {
	value = strings.TrimSpace(value)
	if value == "-" {
		return ""
	}
	return value
}

func productPerformanceExcelNormalize(value string) string {
	return strings.ToUpper(strings.TrimSpace(value))
}

func productPerformanceExcelFirstNonEmpty(values ...string) string {
	for _, value := range values {
		value = strings.TrimSpace(value)
		if value != "" {
			return value
		}
	}
	return ""
}

func productPerformanceExcelFirstNonZero(values ...float64) float64 {
	for _, value := range values {
		if value != 0 && !math.IsNaN(value) && !math.IsInf(value, 0) {
			return value
		}
	}
	return 0
}

func productPerformanceExcelNormalizeCostPair(costPriceUSD, basePriceUSD float64) (float64, float64) {
	if costPriceUSD > 0 && basePriceUSD > 0 && costPriceUSD > basePriceUSD {
		return basePriceUSD, costPriceUSD
	}
	return costPriceUSD, basePriceUSD
}

func productPerformanceExcelAvgPrice(salesUSD, qty float64) float64 {
	if qty <= 0 {
		return 0
	}
	return salesUSD / qty
}

const productPerformanceExcelStockTurnoverYearDays = 360.0

func productPerformanceExcelAvgStock(row *productPerformanceExcelVariant, suffix string) float64 {
	switch suffix {
	case "90d":
		return productPerformanceExcelFirstNonZero(row.AvgStock90, row.StockQty)
	case "180d":
		return productPerformanceExcelFirstNonZero(row.AvgStock180, row.StockQty)
	case "365d":
		return productPerformanceExcelFirstNonZero(row.AvgStock365, row.StockQty)
	case "total":
		return productPerformanceExcelFirstNonZero(row.AvgStockTotal, row.StockQty)
	default:
		return row.StockQty
	}
}

func productPerformanceExcelPeriodDays(row *productPerformanceExcelVariant, suffix string) float64 {
	switch suffix {
	case "90d":
		return 90
	case "180d":
		return 180
	case "365d":
		return 360
	case "total":
		end, err := time.Parse("2006-01-02", strings.TrimSpace(row.KpiDate))
		if err != nil {
			return 0
		}
		start := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
		if end.Before(start) {
			return 0
		}
		return end.Sub(start).Hours()/24 + 1
	default:
		return 0
	}
}

func productPerformanceExcelStockTurnover(salesQty, avgStock, periodDays float64) float64 {
	if salesQty <= 0 || avgStock <= 0 {
		return 0
	}
	raw := salesQty / avgStock
	if periodDays <= 0 {
		return raw
	}
	return raw * productPerformanceExcelStockTurnoverYearDays / periodDays
}

func productPerformanceExcelGrossProfit(salesUSD, qty, unitCost float64) float64 {
	if qty <= 0 {
		return 0
	}
	return salesUSD - qty*unitCost
}

func productPerformanceExcelMargin(salesUSD, qty, unitCost float64) float64 {
	if salesUSD <= 0 {
		return 0
	}
	return productPerformanceExcelGrossProfit(salesUSD, qty, unitCost) / salesUSD
}

func productPerformanceExcelProductScore(suffix string, salesUSD, stockTurnover, marketCount, customerCount, margin float64, periodDays ...float64) float64 {
	if salesUSD <= 0 {
		return 1
	}
	days := productPerformanceExcelScorePeriodDays(suffix, periodDays...)
	revenueScore := productPerformanceExcelRatioScore(salesUSD, productPerformanceExcelProductRevenueTargetForDays(suffix, days))
	score := 0.30*productPerformanceExcelMarginScore(margin) +
		0.20*productPerformanceExcelRatioScore(stockTurnover, 4) +
		0.20*revenueScore +
		0.05*productPerformanceExcelRatioScore(marketCount, productPerformanceExcelMarketSpreadTarget(suffix)) +
		0.25*productPerformanceExcelRatioScore(customerCount, productPerformanceExcelCustomerSpreadTargetForDays(suffix, days))
	return productPerformanceExcelRoundScore(score)
}

func productPerformanceExcelMarketSpreadTarget(suffix string) float64 {
	switch suffix {
	case "90d":
		return 3
	case "180d":
		return 3
	case "365d":
		return 6
	default:
		return 6
	}
}

func productPerformanceExcelCustomerSpreadTarget(suffix string) float64 {
	return productPerformanceExcelCustomerSpreadTargetForDays(suffix, productPerformanceExcelScorePeriodDays(suffix))
}

func productPerformanceExcelCustomerSpreadTargetForDays(suffix string, periodDays float64) float64 {
	switch suffix {
	case "90d":
		return 8
	case "180d":
		return 20
	case "365d":
		return 50
	default:
		return 20 * productPerformanceExcelTotalPeriodMultiplier(periodDays)
	}
}

func productPerformanceExcelProductRevenueTarget(suffix string) float64 {
	return productPerformanceExcelProductRevenueTargetForDays(suffix, productPerformanceExcelScorePeriodDays(suffix))
}

func productPerformanceExcelProductRevenueTargetForDays(suffix string, periodDays float64) float64 {
	switch suffix {
	case "180d":
		return 50000
	case "365d":
		return 100000
	case "total":
		return 50000 * productPerformanceExcelTotalPeriodMultiplier(periodDays)
	default:
		return 25000
	}
}

func productPerformanceExcelScorePeriodDays(suffix string, periodDays ...float64) float64 {
	if len(periodDays) > 0 && periodDays[0] > 0 {
		return periodDays[0]
	}
	switch suffix {
	case "90d":
		return 90
	case "180d":
		return 180
	case "365d":
		return 360
	case "total":
		start := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
		now := time.Now()
		if now.Before(start) {
			return 0
		}
		return now.Sub(start).Hours()/24 + 1
	default:
		return 0
	}
}

func productPerformanceExcelTotalPeriodMultiplier(periodDays float64) float64 {
	if periodDays <= 0 {
		periodDays = 180
	}
	return math.Max(1, periodDays/180.0)
}

func productPerformanceExcelMarginScore(margin float64) float64 {
	if margin < 0 {
		margin = 0
	}
	return productPerformanceExcelRatioScore(margin, 0.45)
}

func productPerformanceExcelRatioScore(value, target float64) float64 {
	if target <= 0 || value <= 0 {
		return 0
	}
	score := value * 100 / target
	if score > 100 {
		return 100
	}
	if score < 0 {
		return 0
	}
	return score
}

func productPerformanceExcelRoundScore(score float64) float64 {
	if score < 0 {
		score = 0
	}
	if score > 100 {
		score = 100
	}
	return math.Round(score*10000) / 10000
}

func productPerformanceExcelFloat(value any) float64 {
	switch v := value.(type) {
	case float64:
		if math.IsNaN(v) || math.IsInf(v, 0) {
			return 0
		}
		return v
	case float32:
		return productPerformanceExcelFloat(float64(v))
	case int:
		return float64(v)
	case int64:
		return float64(v)
	case int32:
		return float64(v)
	case uint:
		return float64(v)
	case uint64:
		return float64(v)
	case uint32:
		return float64(v)
	default:
		return 0
	}
}

func productPerformanceExcelSortedSetLabel(values map[string]bool) string {
	out := make([]string, 0, len(values))
	for value := range values {
		if strings.TrimSpace(value) != "" {
			out = append(out, value)
		}
	}
	sort.Strings(out)
	return strings.Join(out, ", ")
}

func productPerformanceExcelColumnWidth(header, kind string) float64 {
	if kind == "text" {
		if strings.Contains(header, "Piyasalar") || strings.Contains(header, "Öneri") {
			return 34
		}
		if len([]rune(header)) > 14 {
			return 24
		}
		return 16
	}
	if strings.Contains(header, "Marj") || strings.Contains(header, "Skor") {
		return 14
	}
	return 16
}

func productPerformanceExcelMaxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}
