package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"bytes"
	"database/sql"
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/jung-kurt/gofpdf"
)

type agingScreenPDFRow struct {
	Cari8            string
	CariDetay        string
	FaturaCari       string
	OdemeCari        string
	FaturaRef        string
	OdemeRef         string
	FaturaTarihi     string
	OdemeTarihi      string
	OdemeDocDate     string
	EslesenTutar     float64
	UsdTutar         float64
	CurrencyUsdRate  float64
	GunSayisi        float64
	GunSayisiDocDate float64
	Aciklama         string
	DocCurrencyCode  string
}

func ExportStatementAgingScreenPDFHandler(_ *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		claims, ok := auth.GetClaimsFromContext(r.Context())
		if !ok || claims == nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		selectedDate := strings.TrimSpace(r.URL.Query().Get("enddate"))
		if selectedDate == "" {
			selectedDate = strings.TrimSpace(r.URL.Query().Get("selected_date"))
		}
		if selectedDate == "" {
			selectedDate = time.Now().Format("2006-01-02")
		}

		params := models.StatementAgingParams{
			AccountCode: strings.TrimSpace(r.URL.Query().Get("accountcode")),
			EndDate:     selectedDate,
			Parislemler: r.URL.Query()["parislemler"],
		}

		if err := queries.RebuildStatementAgingCache(r.Context()); err != nil {
			http.Error(w, "Error rebuilding aging cache: "+err.Error(), http.StatusInternalServerError)
			return
		}

		rawRows, err := queries.GetStatementAging(params)
		if err != nil {
			http.Error(w, "Error fetching aging statement: "+err.Error(), http.StatusInternalServerError)
			return
		}

		rows := make([]agingScreenPDFRow, 0, len(rawRows))
		for _, r := range rawRows {
			rows = append(rows, agingScreenPDFRow{
				Cari8:            pickString(r, "Cari8", "cari8"),
				CariDetay:        pickString(r, "CariDetay", "cari_detay"),
				FaturaCari:       pickString(r, "FaturaCari", "fatura_cari"),
				OdemeCari:        pickString(r, "OdemeCari", "odeme_cari"),
				FaturaRef:        pickString(r, "FaturaRef", "fatura_ref"),
				OdemeRef:         pickString(r, "OdemeRef", "odeme_ref"),
				FaturaTarihi:     pickString(r, "FaturaTarihi", "fatura_tarihi"),
				OdemeTarihi:      pickString(r, "OdemeTarihi", "odeme_tarihi"),
				OdemeDocDate:     pickString(r, "OdemeDocDate", "odeme_doc_date"),
				EslesenTutar:     pickFloat(r, "EslesenTutar", "eslesen_tutar"),
				UsdTutar:         pickFloat(r, "UsdTutar", "usd_tutar"),
				CurrencyUsdRate:  pickFloat(r, "CurrencyUsdRate", "currency_usd_rate", "CurrencyTryRate", "currency_try_rate"),
				GunSayisi:        pickFloat(r, "GunSayisi", "gun_sayisi"),
				GunSayisiDocDate: pickFloat(r, "GunSayisi_DocDate", "gun_sayisi_docdate"),
				Aciklama:         pickString(r, "Aciklama", "aciklama"),
				DocCurrencyCode:  pickString(r, "DocCurrencyCode", "doc_currency_code"),
			})
		}

		sortBy := strings.TrimSpace(r.URL.Query().Get("sort_by"))
		sortDesc := parseBoolQuery(r.URL.Query().Get("sort_desc"))

		pdf := gofpdf.New("L", "mm", "A3", "")
		pdf.SetMargins(8, 8, 8)
		pdf.SetAutoPageBreak(false, 10)
		if err := registerDejavuFonts(pdf, "dejavu"); err != nil {
			http.Error(w, "pdf font error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		drawStatementAgingScreenPDF(pdf, selectedDate, params.AccountCode, rows, sortBy, sortDesc)

		if err := pdf.Error(); err != nil {
			http.Error(w, "pdf render error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		var buf bytes.Buffer
		if err := pdf.Output(&buf); err != nil {
			http.Error(w, "pdf output error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/pdf")
		w.Header().Set("Content-Disposition", `inline; filename="account-aging-screen.pdf"`)
		_, _ = w.Write(buf.Bytes())
	}
}

type agingScreenMasterPDF struct {
	GroupKey          string
	Cari8             string
	CariDetay         string
	SatirSayisi       int
	ToplamUSD         float64
	NormalUSD         float64
	AcikKalemUSD      float64
	KurWeightedBase   float64
	KurWeightedSum    float64
	KurFallback       float64
	WeightedBase      float64
	WeightedGunSum    float64
	WeightedGunDocSum float64
}

type agingScreenCurrencyPDF struct {
	GroupKey          string
	MasterKey         string
	DocCurrencyCode   string
	SatirSayisi       int
	ToplamTutar       float64
	ToplamUSD         float64
	NormalTutar       float64
	AcikKalemTutar    float64
	KurWeightedBase   float64
	KurWeightedSum    float64
	KurFallback       float64
	WeightedBase      float64
	WeightedGunSum    float64
	WeightedGunDocSum float64
}

func drawStatementAgingScreenPDF(pdf *gofpdf.Fpdf, selectedDate, accountCode string, rows []agingScreenPDFRow, sortBy string, sortDesc bool) {
	masters, currenciesByMaster, detailsByCurrency := buildStatementAgingScreenPDFData(rows, sortBy, sortDesc)

	pageW, pageH := pdf.GetPageSize()
	marginL, marginT, marginR, marginB := 8.0, 8.0, 8.0, 10.0
	tableW := pageW - marginL - marginR

	masterCols := []string{"Ana Cari Kod", "Ana Cari Detay", "Satir", "Toplam USD", "Normal USD", "Acik Kalem USD", "Ort. Gun", "Ort. Gun (DocDate)", "Kur"}
	masterW := normalizeWidths([]float64{22, 40, 12, 18, 18, 20, 14, 18, 12}, tableW)

	currencyCols := []string{"Doviz", "Satir", "Toplam Tutar", "Toplam USD", "Normal", "Acik Kalem", "Kur", "Ort. Gun", "Ort. Gun (DocDate)"}
	currencyW := normalizeWidths([]float64{16, 12, 20, 18, 18, 18, 12, 14, 18}, tableW)

	detailCols := []string{"Fatura Cari", "Odeme Cari", "Fatura Ref", "Odeme Ref", "Fatura Tarihi", "Odeme Vade", "Odeme DocDate", "Eslesen Tutar", "USD Tutar", "Kur", "Gun", "Gun (DocDate)", "Aciklama", "Doviz"}
	detailW := normalizeWidths([]float64{18, 18, 22, 22, 16, 16, 18, 18, 16, 12, 10, 13, 30, 11}, tableW)

	header := func() {
		pdf.AddPage()
		pdf.SetFont("dejavu", "B", 14)
		pdf.SetTextColor(149, 113, 22)
		pdf.SetXY(marginL, marginT)
		pdf.CellFormat(150, 6, "Cari Yaslandirmali Ekstre", "", 0, "L", false, 0, "")

		pdf.SetFont("dejavu", "", 8.5)
		pdf.SetTextColor(30, 30, 30)
		pdf.SetXY(pageW-marginR-90, marginT+0.5)
		pdf.CellFormat(90, 4.8, "Son Tarih: "+formatDateTR(selectedDate), "", 0, "R", false, 0, "")
		if strings.TrimSpace(accountCode) != "" {
			pdf.SetXY(pageW-marginR-90, marginT+5)
			pdf.CellFormat(90, 4.8, "Cari: "+accountCode, "", 0, "R", false, 0, "")
		}

		pdf.SetDrawColor(149, 113, 22)
		pdf.Line(marginL, marginT+10.5, pageW-marginR, marginT+10.5)
		pdf.SetDrawColor(200, 200, 200)
		pdf.SetY(marginT + 13)
	}

	needPage := func(needH float64) bool {
		return pdf.GetY()+needH+marginB > pageH
	}

	drawHeaderRow := func(cols []string, widths []float64, h float64, r, g, b int, fontSize float64) {
		pdf.SetFont("dejavu", "B", fontSize)
		pdf.SetTextColor(255, 255, 255)
		pdf.SetFillColor(r, g, b)
		y := pdf.GetY()
		x := marginL
		for i, c := range cols {
			pdf.Rect(x, y, widths[i], h, "DF")
			pdf.SetXY(x+0.8, y+0.9)
			pdf.CellFormat(widths[i]-1.6, h-1.8, c, "", 0, "C", false, 0, "")
			x += widths[i]
		}
		pdf.SetY(y + h)
	}
	setDataTextStyle := func(size float64, r, g, b int) {
		pdf.SetFont("dejavu", "", size)
		pdf.SetTextColor(r, g, b)
	}

	header()
	drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
	setDataTextStyle(7, 25, 25, 25)

	for _, m := range masters {
		masterLine := []string{
			m.Cari8,
			m.CariDetay,
			strconv.Itoa(m.SatirSayisi),
			formatMoneyPDF(m.ToplamUSD),
			formatMoneyPDF(m.NormalUSD),
			formatMoneyPDF(m.AcikKalemUSD),
			fmt.Sprintf("%.0f", statementAgingAvg(m.WeightedGunSum, m.WeightedBase)),
			fmt.Sprintf("%.0f", statementAgingAvg(m.WeightedGunDocSum, m.WeightedBase)),
			formatMoneyPDF(statementAgingAvg(m.KurWeightedSum, m.KurWeightedBase, m.KurFallback)),
		}
		rowH := calcPDFRowHeight(pdf, masterLine, masterW, map[int]bool{1: true}, 5.8, 3.3)
		if needPage(rowH) {
			header()
			drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
			setDataTextStyle(7, 25, 25, 25)
		}
		setDataTextStyle(7, 25, 25, 25)

		y := pdf.GetY()
		x := marginL
		for i, v := range masterLine {
			pdf.Rect(x, y, masterW[i], rowH, "")
			align := "L"
			if i >= 2 {
				align = "R"
			}
			if i == 6 || i == 7 {
				align = "C"
			}
			drawPDFCellWrapped(pdf, v, x, y, masterW[i], rowH, align, 3.3)
			x += masterW[i]
		}
		pdf.SetY(y + rowH)

		for _, c := range currenciesByMaster[m.GroupKey] {
			if needPage(11.2) {
				header()
				drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
				setDataTextStyle(7, 25, 25, 25)
			}

			pdf.SetFont("dejavu", "B", 7)
			drawHeaderRow(currencyCols, currencyW, 5.6, 76, 95, 122, 6.8)

			setDataTextStyle(6.8, 35, 35, 35)
			currencyLine := []string{
				c.DocCurrencyCode,
				strconv.Itoa(c.SatirSayisi),
				formatMoneyPDF(c.ToplamTutar),
				formatMoneyPDF(c.ToplamUSD),
				formatMoneyPDF(c.NormalTutar),
				formatMoneyPDF(c.AcikKalemTutar),
				formatMoneyPDF(statementAgingAvg(c.KurWeightedSum, c.KurWeightedBase, c.KurFallback)),
				fmt.Sprintf("%.0f", statementAgingAvg(c.WeightedGunSum, c.WeightedBase)),
				fmt.Sprintf("%.0f", statementAgingAvg(c.WeightedGunDocSum, c.WeightedBase)),
			}

			cRowH := 5.4
			y := pdf.GetY()
			x := marginL
			for i, v := range currencyLine {
				pdf.Rect(x, y, currencyW[i], cRowH, "")
				align := "R"
				if i == 0 {
					align = "L"
				}
				if i == 7 || i == 8 {
					align = "C"
				}
				drawPDFCellWrapped(pdf, v, x, y, currencyW[i], cRowH, align, 3.2)
				x += currencyW[i]
			}
			pdf.SetY(y + cRowH)

			drawHeaderRow(detailCols, detailW, 5.6, 31, 59, 91, 6.8)
			setDataTextStyle(6.6, 30, 30, 30)

			for _, d := range detailsByCurrency[c.GroupKey] {
				line := []string{
					d.FaturaCari,
					d.OdemeCari,
					d.FaturaRef,
					d.OdemeRef,
					formatDateTR(d.FaturaTarihi),
					formatDateTR(d.OdemeTarihi),
					formatDateTR(d.OdemeDocDate),
					formatMoneyPDF(d.EslesenTutar),
					formatMoneyPDF(d.UsdTutar),
					formatMoneyPDF(d.CurrencyUsdRate),
					fmt.Sprintf("%.0f", d.GunSayisi),
					fmt.Sprintf("%.0f", d.GunSayisiDocDate),
					d.Aciklama,
					d.DocCurrencyCode,
				}
				rowH := calcPDFRowHeight(pdf, line, detailW, map[int]bool{0: true, 1: true, 2: true, 3: true, 12: true}, 5.4, 3.1)
				if needPage(rowH) {
					header()
					drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
					pdf.SetFont("dejavu", "B", 7)
					drawHeaderRow(currencyCols, currencyW, 5.6, 76, 95, 122, 6.8)
					setDataTextStyle(6.8, 35, 35, 35)
					y = pdf.GetY()
					x = marginL
					for i, v := range currencyLine {
						pdf.Rect(x, y, currencyW[i], cRowH, "")
						align := "R"
						if i == 0 {
							align = "L"
						}
						if i == 7 || i == 8 {
							align = "C"
						}
						drawPDFCellWrapped(pdf, v, x, y, currencyW[i], cRowH, align, 3.2)
						x += currencyW[i]
					}
					pdf.SetY(y + cRowH)
					drawHeaderRow(detailCols, detailW, 5.6, 31, 59, 91, 6.8)
					setDataTextStyle(6.6, 30, 30, 30)
				}

				rowY := pdf.GetY()
				rowX := marginL
				for i, v := range line {
					pdf.Rect(rowX, rowY, detailW[i], rowH, "")
					align := "L"
					if i >= 7 && i <= 9 {
						align = "R"
					}
					if i == 10 || i == 11 {
						align = "C"
					}
					drawPDFCellWrapped(pdf, v, rowX, rowY, detailW[i], rowH, align, 3.1)
					rowX += detailW[i]
				}
				pdf.SetY(rowY + rowH)
			}
			pdf.Ln(1)
		}
		pdf.Ln(1.2)
	}
}

func statementAgingAvg(sum, base float64, fallback ...float64) float64 {
	if base > 0 {
		return sum / base
	}
	if len(fallback) > 0 {
		return fallback[0]
	}
	return 0
}

func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow, sortBy string, sortDesc bool) ([]agingScreenMasterPDF, map[string][]agingScreenCurrencyPDF, map[string][]agingScreenPDFRow) {
	masterMap := map[string]*agingScreenMasterPDF{}
	currencyMap := map[string]*agingScreenCurrencyPDF{}
	detailsByCurrency := map[string][]agingScreenPDFRow{}

	for _, row := range rows {
		masterKey := strings.TrimSpace(row.Cari8)
		if masterKey == "" {
			continue
		}
		curr := strings.ToUpper(strings.TrimSpace(row.DocCurrencyCode))
		if curr == "" {
			curr = "N/A"
		}
		currencyKey := masterKey + "|" + curr
		aciklama := strings.ToUpper(strings.TrimSpace(row.Aciklama))
		absUsd := absFloatExcel(row.UsdTutar)

		m := masterMap[masterKey]
		if m == nil {
			m = &agingScreenMasterPDF{
				GroupKey:  masterKey,
				Cari8:     masterKey,
				CariDetay: strings.TrimSpace(row.CariDetay),
			}
			masterMap[masterKey] = m
		}
		if m.CariDetay == "" {
			m.CariDetay = strings.TrimSpace(row.CariDetay)
		}

		c := currencyMap[currencyKey]
		if c == nil {
			c = &agingScreenCurrencyPDF{
				GroupKey:        currencyKey,
				MasterKey:       masterKey,
				DocCurrencyCode: curr,
			}
			currencyMap[currencyKey] = c
		}

		m.SatirSayisi++
		m.ToplamUSD += row.UsdTutar
		if aciklama == "ACIKKALEM" {
			m.AcikKalemUSD += row.UsdTutar
		} else {
			m.NormalUSD += row.UsdTutar
		}
		if absUsd > 0 {
			m.WeightedBase += absUsd
			m.WeightedGunSum += absUsd * row.GunSayisi
			m.WeightedGunDocSum += absUsd * row.GunSayisiDocDate
			if row.CurrencyUsdRate > 0 {
				m.KurWeightedBase += absUsd
				m.KurWeightedSum += absUsd * row.CurrencyUsdRate
			}
		}
		if row.CurrencyUsdRate > 0 {
			m.KurFallback = row.CurrencyUsdRate
		}

		c.SatirSayisi++
		c.ToplamTutar += row.EslesenTutar
		c.ToplamUSD += row.UsdTutar
		if aciklama == "ACIKKALEM" {
			c.AcikKalemTutar += row.EslesenTutar
		} else {
			c.NormalTutar += row.EslesenTutar
		}
		if absUsd > 0 {
			c.WeightedBase += absUsd
			c.WeightedGunSum += absUsd * row.GunSayisi
			c.WeightedGunDocSum += absUsd * row.GunSayisiDocDate
			if row.CurrencyUsdRate > 0 {
				c.KurWeightedBase += absUsd
				c.KurWeightedSum += absUsd * row.CurrencyUsdRate
			}
		}
		if row.CurrencyUsdRate > 0 {
			c.KurFallback = row.CurrencyUsdRate
		}

		detailsByCurrency[currencyKey] = append(detailsByCurrency[currencyKey], row)
	}

	masters := make([]agingScreenMasterPDF, 0, len(masterMap))
	currenciesByMaster := make(map[string][]agingScreenCurrencyPDF, len(masterMap))
	for _, m := range masterMap {
		masters = append(masters, *m)
	}

	sortAgingScreenMastersForPDF(masters, sortBy, sortDesc)

	for _, c := range currencyMap {
		currenciesByMaster[c.MasterKey] = append(currenciesByMaster[c.MasterKey], *c)
	}
	for mk := range currenciesByMaster {
		sort.SliceStable(currenciesByMaster[mk], func(i, j int) bool {
			return strings.ToUpper(currenciesByMaster[mk][i].DocCurrencyCode) < strings.ToUpper(currenciesByMaster[mk][j].DocCurrencyCode)
		})
	}

	for k := range detailsByCurrency {
		sort.SliceStable(detailsByCurrency[k], func(i, j int) bool {
			a := detailsByCurrency[k][i]
			b := detailsByCurrency[k][j]
			aOpen := strings.EqualFold(strings.TrimSpace(a.Aciklama), "ACIKKALEM")
			bOpen := strings.EqualFold(strings.TrimSpace(b.Aciklama), "ACIKKALEM")
			if aOpen != bOpen {
				return aOpen
			}

			aDate := parseAgingSortDate(a.OdemeDocDate, a.OdemeTarihi, a.FaturaTarihi)
			bDate := parseAgingSortDate(b.OdemeDocDate, b.OdemeTarihi, b.FaturaTarihi)
			if aDate != bDate {
				return aDate.After(bDate)
			}

			if strings.TrimSpace(a.FaturaCari) == strings.TrimSpace(b.FaturaCari) {
				if strings.TrimSpace(a.OdemeCari) == strings.TrimSpace(b.OdemeCari) {
					if strings.TrimSpace(a.FaturaRef) == strings.TrimSpace(b.FaturaRef) {
						return strings.TrimSpace(a.OdemeRef) < strings.TrimSpace(b.OdemeRef)
					}
					return strings.TrimSpace(a.FaturaRef) < strings.TrimSpace(b.FaturaRef)
				}
				return strings.TrimSpace(a.OdemeCari) < strings.TrimSpace(b.OdemeCari)
			}
			return strings.TrimSpace(a.FaturaCari) < strings.TrimSpace(b.FaturaCari)
		})
	}

	return masters, currenciesByMaster, detailsByCurrency
}

func sortAgingScreenMastersForPDF(masters []agingScreenMasterPDF, sortBy string, descending bool) {
	if len(masters) <= 1 {
		return
	}
	key := strings.TrimSpace(sortBy)
	if key == "" {
		key = "cari8"
	}

	textCmp := func(a, b string) int {
		return strings.Compare(strings.ToUpper(strings.TrimSpace(a)), strings.ToUpper(strings.TrimSpace(b)))
	}
	numCmp := func(a, b float64) int {
		if a < b {
			return -1
		}
		if a > b {
			return 1
		}
		return 0
	}
	intCmp := func(a, b int) int {
		if a < b {
			return -1
		}
		if a > b {
			return 1
		}
		return 0
	}

	sort.SliceStable(masters, func(i, j int) bool {
		a := masters[i]
		b := masters[j]
		cmp := 0

		switch key {
		case "cari8":
			cmp = textCmp(a.Cari8, b.Cari8)
		case "cari_detay":
			cmp = textCmp(a.CariDetay, b.CariDetay)
		case "satir_sayisi":
			cmp = intCmp(a.SatirSayisi, b.SatirSayisi)
		case "toplam_usd":
			cmp = numCmp(a.ToplamUSD, b.ToplamUSD)
		case "normal_usd":
			cmp = numCmp(a.NormalUSD, b.NormalUSD)
		case "acik_kalem_usd":
			cmp = numCmp(a.AcikKalemUSD, b.AcikKalemUSD)
		case "ortalama_gun":
			cmp = numCmp(statementAgingAvg(a.WeightedGunSum, a.WeightedBase), statementAgingAvg(b.WeightedGunSum, b.WeightedBase))
		case "ortalama_gun_docdate":
			cmp = numCmp(statementAgingAvg(a.WeightedGunDocSum, a.WeightedBase), statementAgingAvg(b.WeightedGunDocSum, b.WeightedBase))
		case "kur":
			cmp = numCmp(statementAgingAvg(a.KurWeightedSum, a.KurWeightedBase, a.KurFallback), statementAgingAvg(b.KurWeightedSum, b.KurWeightedBase, b.KurFallback))
		default:
			cmp = textCmp(a.Cari8, b.Cari8)
		}

		if cmp == 0 {
			cmp = textCmp(a.Cari8, b.Cari8)
		}
		if descending {
			return cmp > 0
		}
		return cmp < 0
	})
}

func parseAgingSortDate(values ...string) time.Time {
	layouts := []string{
		time.RFC3339,
		"2006-01-02",
		"2006-01-02 15:04:05",
		"02.01.2006",
		"02.01.2006 15:04:05",
	}
	for _, raw := range values {
		s := strings.TrimSpace(raw)
		if s == "" {
			continue
		}
		for _, l := range layouts {
			if t, err := time.Parse(l, s); err == nil {
				return t
			}
		}
	}
	return time.Time{}
}

func pickString(m map[string]interface{}, keys ...string) string {
	for _, k := range keys {
		if v, ok := m[k]; ok {
			return strings.TrimSpace(toStringValue(v))
		}
	}
	return ""
}

func pickFloat(m map[string]interface{}, keys ...string) float64 {
	for _, k := range keys {
		if v, ok := m[k]; ok {
			return toFloat64Value(v)
		}
	}
	return 0
}

func toStringValue(v interface{}) string {
	switch x := v.(type) {
	case nil:
		return ""
	case string:
		return x
	case []byte:
		return string(x)
	default:
		return fmt.Sprint(x)
	}
}

func toFloat64Value(v interface{}) float64 {
	switch x := v.(type) {
	case nil:
		return 0
	case float64:
		return x
	case float32:
		return float64(x)
	case int:
		return float64(x)
	case int64:
		return float64(x)
	case int32:
		return float64(x)
	case string:
		return parseFloatValue(x)
	case []byte:
		return parseFloatValue(string(x))
	default:
		return parseFloatValue(fmt.Sprint(x))
	}
}

func parseFloatValue(s string) float64 {
	s = strings.TrimSpace(s)
	if s == "" {
		return 0
	}

	hasComma := strings.Contains(s, ",")
	hasDot := strings.Contains(s, ".")
	if hasComma && hasDot {
		if strings.LastIndex(s, ",") > strings.LastIndex(s, ".") {
			s = strings.ReplaceAll(s, ".", "")
			s = strings.Replace(s, ",", ".", 1)
		} else {
			s = strings.ReplaceAll(s, ",", "")
		}
	} else if hasComma {
		s = strings.ReplaceAll(s, ".", "")
		s = strings.Replace(s, ",", ".", 1)
	}

	n, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return 0
	}
	return n
}
