package dialectsfcrm

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type reportWireAdapter struct{}

func NewReportWireAdapter() reportWireAdapter {
	return reportWireAdapter{}
}

func (reportWireAdapter) ProjectReportMetadata(def *models.ReportDefinition, _ *models.ObjectMetadata) any {
	return reportDefinitionToSalesforceMetadata(def)
}

func (reportWireAdapter) ProjectReportDescribeResponse(def *models.ReportDefinition, schema *models.ObjectMetadata) any {
	return map[string]any{
		"reportMetadata": reportDefinitionToSalesforceMetadata(def),
	}
}

func (reportWireAdapter) ProjectReportResult(result *models.ReportResult, schema *models.ObjectMetadata) any {
	return projectSalesforceReportResult(result, schema)
}

func reportDefinitionToSalesforceMetadata(def *models.ReportDefinition) map[string]any {
	if def == nil {
		return map[string]any{}
	}
	normalized := *def
	normalized.NormalizeBoundaryAliases()
	rtType := normalized.EffectiveSourceRef()
	isFormatName := strings.EqualFold(rtType, "Summary") || strings.EqualFold(rtType, "Tabular") || strings.EqualFold(rtType, "Matrix")
	if normalized.SourceObject != "" && (rtType == "" || isFormatName || strings.EqualFold(rtType, normalized.SourceObject)) {
		rtType = normalized.SourceObject + "List"
	}
	meta := map[string]any{
		"id":           normalized.ID,
		"name":         normalized.Name,
		"reportFormat": salesforceReportFormat(normalized.EffectiveDisplayMode()),
		"reportType":   map[string]any{"type": rtType, "label": normalized.SourceObject},
	}
	if normalized.Scope != "" {
		meta["scope"] = normalized.Scope
	}
	if normalized.SourceObject != "" {
		resolved := normalized.SourceObject
		if i := strings.Index(resolved, "With"); i > 0 {
			resolved = resolved[:i]
		}
		for _, suffix := range []string{"FieldAuditHistory", "AuditHistory", "Activity", "List"} {
			if strings.HasSuffix(resolved, suffix) {
				resolved = strings.TrimSuffix(resolved, suffix)
				break
			}
		}
		meta["sourceObject"] = resolved
	}
	if len(normalized.Columns) > 0 {
		meta["detailColumns"] = normalized.Columns
	}
	if len(normalized.Filters) > 0 {
		meta["reportFilters"] = salesforceReportFiltersFromModel(normalized.Filters)
	}
	if len(normalized.Grouping) > 0 {
		groups := make([]map[string]any, len(normalized.Grouping))
		for i, g := range normalized.Grouping {
			groups[i] = map[string]any{"name": g, "sortOrder": "Asc", "dateGranularity": "NONE"}
		}
		meta["groupingsDown"] = groups
	}
	if df := normalized.EffectiveDateFilter(); df != nil {
		meta["standardDateFilter"] = salesforceStandardDateFilterFromModel(df)
	}
	return meta
}

func salesforceReportFormat(displayMode string) string {
	return strings.ToUpper(strings.TrimSpace(displayMode))
}

func projectSalesforceReportResult(result *models.ReportResult, schema *models.ObjectMetadata) map[string]any {
	if result == nil {
		return map[string]any{}
	}
	def := &result.ReportMetadata

	reportFormat := strings.ToUpper(def.EffectiveDisplayMode())
	if reportFormat == "" {
		if strings.EqualFold(def.EffectiveSourceRef(), "summary") {
			reportFormat = "SUMMARY"
		} else {
			reportFormat = "TABULAR"
		}
	}

	isSummaryExec := reportFormat == "SUMMARY" && len(def.Grouping) == 1 &&
		looksLikeSalesforceAggregatedRows(result.Rows)

	isMultiGroupSummary := reportFormat == "SUMMARY" && len(def.Grouping) > 0 &&
		!isSummaryExec

	meta := reportDefinitionToSalesforceMetadata(def)
	meta["reportFormat"] = reportFormat

	aggKeys := buildSalesforceAggregateKeys(def.Aggregates)
	meta["aggregates"] = aggKeys

	cols := effectiveSalesforceReportColumns(def, result.Rows, isSummaryExec)
	if len(cols) > 0 {
		meta["detailColumns"] = cols
	}

	extended := buildSalesforceExtendedMetadata(cols, def.Grouping, def.Aggregates, schema)
	if len(result.Warnings) > 0 {
		extended["warnings"] = result.Warnings
	}
	meta["reportExtendedMetadata"] = extended

	totalCount := result.TotalCount
	if totalCount == 0 {
		totalCount = len(result.Rows)
	}

	if isSummaryExec {
		return buildSalesforceSummaryResponse(meta, result.Rows, def, totalCount)
	}
	if isMultiGroupSummary {
		return buildSalesforceMultiGroupSummaryResponse(meta, result.Rows, def, cols, totalCount, result.AggregateDedupColumn, schema)
	}
	return buildSalesforceTabularResponse(meta, result.Rows, cols, totalCount, schema)
}

func buildSalesforceTabularResponse(meta map[string]any, rows []models.SObject, cols []string, totalCount int, schema *models.ObjectMetadata) map[string]any {
	sfRows := make([]map[string]any, 0, len(rows))
	for _, row := range rows {
		cells := make([]map[string]any, len(cols))
		for j, col := range cols {
			val := resolveSalesforceReportColumnValue(row, col, schema)
			cells[j] = map[string]any{"label": formatSalesforceCellValue(val), "value": val}
		}
		sfRows = append(sfRows, map[string]any{"dataCells": cells})
	}

	return map[string]any{
		"reportMetadata": meta,
		"factMap": map[string]any{
			"T!T": map[string]any{
				"rows":       sfRows,
				"aggregates": []map[string]any{{"label": fmt.Sprintf("%d", totalCount), "value": totalCount}},
			},
		},
		"groupingsDown": map[string]any{"groupings": []map[string]any{}},
		"hasDetailRows": len(rows) > 0,
		"allData":       len(rows) >= totalCount,
	}
}

func buildSalesforceSummaryResponse(meta map[string]any, rows []models.SObject, def *models.ReportDefinition, totalCount int) map[string]any {
	groupings := make([]map[string]any, 0, len(rows))
	factMap := make(map[string]any, len(rows)+1)
	var grandTotal float64

	for i, row := range rows {
		name := formatSalesforceCellValue(row["name"])
		val, _ := reportToFloat64(row["val"])
		key := fmt.Sprintf("%d", i)

		groupings = append(groupings, map[string]any{"label": name, "value": name, "key": key})
		factMap[key+"!T"] = map[string]any{
			"aggregates": []map[string]any{{"label": formatSalesforceNumber(val), "value": val}},
			"rows":       []map[string]any{},
		}
		grandTotal += val
	}

	grandAggs := []map[string]any{{"label": fmt.Sprintf("%d", totalCount), "value": totalCount}}
	if len(def.Aggregates) > 0 {
		grandAggs = append(grandAggs, map[string]any{"label": formatSalesforceNumber(grandTotal), "value": grandTotal})
	}
	factMap["T!T"] = map[string]any{"aggregates": grandAggs, "rows": []map[string]any{}}

	return map[string]any{
		"reportMetadata": meta,
		"factMap":        factMap,
		"groupingsDown":  map[string]any{"groupings": groupings},
		"hasDetailRows":  false,
	}
}

func buildSalesforceMultiGroupSummaryResponse(meta map[string]any, rows []models.SObject, def *models.ReportDefinition, cols []string, totalCount int, dedupByAgg map[string]string, schema *models.ObjectMetadata) map[string]any {
	factMap := make(map[string]any)
	grandAggs := computeSalesforceAggregatesWithDedup(rows, def.Aggregates, dedupByAgg, schema)
	overrideSalesforceRowCountAggregate(grandAggs, def.Aggregates, totalCount)
	factMap["T!T"] = map[string]any{"aggregates": grandAggs, "rows": []map[string]any{}}
	groupings := buildSalesforceSummaryGroupingLevel(rows, def, cols, factMap, 0, "", schema)

	return map[string]any{
		"reportMetadata": meta,
		"factMap":        factMap,
		"groupingsDown":  map[string]any{"groupings": groupings},
		"hasDetailRows":  len(rows) > 0,
	}
}

type salesforceSummaryGroupBucket struct {
	label string
	rows  []models.SObject
}

func buildSalesforceSummaryGroupingLevel(rows []models.SObject, def *models.ReportDefinition, cols []string, factMap map[string]any, level int, keyPrefix string, schema *models.ObjectMetadata) []map[string]any {
	if level >= len(def.Grouping) {
		return []map[string]any{}
	}

	groupField := resolveSalesforceGroupingFieldName(def.Grouping[level], schema)
	bucketMap := make(map[string]*salesforceSummaryGroupBucket)
	bucketOrder := make([]string, 0)

	for _, row := range rows {
		label := formatSalesforceCellValue(resolveSalesforceReportColumnValue(row, groupField, schema))
		if label == "" {
			label = "(blank)"
		}
		bucket, ok := bucketMap[label]
		if !ok {
			bucket = &salesforceSummaryGroupBucket{label: label}
			bucketMap[label] = bucket
			bucketOrder = append(bucketOrder, label)
		}
		bucket.rows = append(bucket.rows, row)
	}

	groupings := make([]map[string]any, 0, len(bucketOrder))
	isLeafLevel := level == len(def.Grouping)-1

	for index, label := range bucketOrder {
		bucket := bucketMap[label]
		key := fmt.Sprintf("%d", index)
		if keyPrefix != "" {
			key = keyPrefix + "_" + key
		}

		factEntry := map[string]any{
			"aggregates": computeSalesforceAggregates(bucket.rows, def.Aggregates, schema),
			"rows":       []map[string]any{},
		}
		if isLeafLevel {
			factEntry["rows"] = buildSalesforceDetailRows(bucket.rows, cols, schema)
		}
		factMap[key+"!T"] = factEntry

		grouping := map[string]any{
			"key":   key,
			"label": fmt.Sprintf("%s (%d)", label, len(bucket.rows)),
			"value": label,
		}
		if !isLeafLevel {
			grouping["groupings"] = buildSalesforceSummaryGroupingLevel(bucket.rows, def, cols, factMap, level+1, key, schema)
		}
		groupings = append(groupings, grouping)
	}

	return groupings
}

func buildSalesforceDetailRows(rows []models.SObject, cols []string, schema *models.ObjectMetadata) []map[string]any {
	sfRows := make([]map[string]any, 0, len(rows))
	for _, row := range rows {
		cells := make([]map[string]any, len(cols))
		for j, col := range cols {
			val := resolveSalesforceReportColumnValue(row, col, schema)
			cells[j] = map[string]any{"label": formatSalesforceCellValue(val), "value": val}
		}
		sfRows = append(sfRows, map[string]any{"dataCells": cells})
	}
	return sfRows
}

func overrideSalesforceRowCountAggregate(aggregates []map[string]any, aggDefs []models.ReportAggregate, totalCount int) {
	if len(aggregates) == 0 {
		return
	}
	rowCountValue := map[string]any{"label": fmt.Sprintf("%d", totalCount), "value": totalCount}

	hasRowCount := false
	rowCountIdx := -1
	for i, a := range aggDefs {
		if a.Field == "" && strings.EqualFold(a.Type, "RowCount") {
			hasRowCount = true
			rowCountIdx = i
			break
		}
	}
	if !hasRowCount {
		aggregates[0] = rowCountValue
		return
	}
	if rowCountIdx >= 0 && rowCountIdx < len(aggregates) {
		aggregates[rowCountIdx] = rowCountValue
	}
}

func computeSalesforceAggregates(rows []models.SObject, aggDefs []models.ReportAggregate, schema *models.ObjectMetadata) []map[string]any {
	return computeSalesforceAggregatesWithDedup(rows, aggDefs, nil, schema)
}

func computeSalesforceAggregatesWithDedup(rows []models.SObject, aggDefs []models.ReportAggregate, dedupByAgg map[string]string, schema *models.ObjectMetadata) []map[string]any {
	hasRowCount := false
	for _, a := range aggDefs {
		if a.Field == "" && strings.EqualFold(a.Type, "RowCount") {
			hasRowCount = true
			break
		}
	}

	ordered := make([]models.ReportAggregate, 0, len(aggDefs)+1)
	if !hasRowCount {
		ordered = append(ordered, models.ReportAggregate{Field: "", Type: "RowCount"})
	}
	ordered = append(ordered, aggDefs...)

	result := make([]map[string]any, 0, len(ordered))
	for _, agg := range ordered {
		if agg.Field == "" && strings.EqualFold(agg.Type, "RowCount") {
			result = append(result, map[string]any{
				"label": fmt.Sprintf("%d", len(rows)),
				"value": len(rows),
			})
			continue
		}

		field := resolveSalesforceAggregateField(agg.Field, schema)
		dedupKey := ""
		if dedupByAgg != nil {
			if k, ok := dedupByAgg[agg.Field]; ok {
				dedupKey = k
			}
		}
		var total float64
		if dedupKey != "" {
			seen := make(map[string]struct{}, len(rows))
			for _, row := range rows {
				id, _ := row[dedupKey].(string)
				if id == "" {
					f, _ := reportToFloat64(resolveSalesforceReportColumnValue(row, field, schema))
					total += f
					continue
				}
				if _, dup := seen[id]; dup {
					continue
				}
				seen[id] = struct{}{}
				f, _ := reportToFloat64(resolveSalesforceReportColumnValue(row, field, schema))
				total += f
			}
		} else {
			for _, row := range rows {
				val := resolveSalesforceReportColumnValue(row, field, schema)
				f, _ := reportToFloat64(val)
				total += f
			}
		}

		result = append(result, map[string]any{
			"label": formatSalesforceNumber(total),
			"value": total,
		})
	}
	return result
}

func resolveSalesforceAggregateField(field string, schema *models.ObjectMetadata) string {
	field = strings.TrimSuffix(field, ".CONVERT")
	if idx := strings.Index(field, "."); idx >= 0 {
		field = field[idx+1:]
	}
	if alias := resolveSalesforceColumnAlias(field, schema); alias != "" {
		return alias
	}
	pascal := snakeToPascal(field)
	if pascal != field {
		return pascal
	}
	return field
}

func resolveSalesforceGroupingFieldName(field string, schema *models.ObjectMetadata) string {
	return resolveSalesforceAggregateField(field, schema)
}

func buildSalesforceAggregateKeys(aggs []models.ReportAggregate) []string {
	keys := make([]string, 0, len(aggs)+1)
	hasRowCount := false
	for _, a := range aggs {
		k := salesforceAggregateKey(a)
		keys = append(keys, k)
		if k == "RowCount" {
			hasRowCount = true
		}
	}
	if !hasRowCount {
		keys = append([]string{"RowCount"}, keys...)
	}
	return keys
}

func salesforceAggregateKey(agg models.ReportAggregate) string {
	if agg.Field == "" {
		return agg.Type
	}
	switch strings.ToUpper(agg.Type) {
	case "SUM":
		return "s!" + agg.Field
	case "AVG":
		return "a!" + agg.Field
	case "MAX":
		return "mx!" + agg.Field
	case "MIN":
		return "mn!" + agg.Field
	default:
		return agg.Type
	}
}

func buildSalesforceExtendedMetadata(cols []string, grouping []string, aggs []models.ReportAggregate, schema *models.ObjectMetadata) map[string]any {
	dci := make(map[string]any, len(cols))
	for _, col := range cols {
		dci[col] = map[string]any{"label": resolveSalesforceFieldLabel(col, schema), "dataType": resolveSalesforceFieldDataType(col, schema)}
	}

	gci := make(map[string]any, len(grouping))
	for _, group := range grouping {
		gci[group] = map[string]any{"label": resolveSalesforceFieldLabel(group, schema), "dataType": resolveSalesforceFieldDataType(group, schema)}
	}

	aci := map[string]any{"RowCount": map[string]any{"label": "Record Count", "dataType": "int"}}
	for _, a := range aggs {
		k := salesforceAggregateKey(a)
		if k != "RowCount" {
			aci[k] = map[string]any{"label": friendlySalesforceAggregateLabel(a, schema), "dataType": resolveSalesforceFieldDataType(a.Field, schema)}
		}
	}

	return map[string]any{"detailColumnInfo": dci, "groupingColumnInfo": gci, "aggregateColumnInfo": aci}
}

func resolveSalesforceFieldLabel(col string, schema *models.ObjectMetadata) string {
	if fm := lookupSalesforceFieldMetadata(col, schema); fm != nil {
		return fm.Label
	}
	return fallbackSalesforceColumnLabel(col)
}

func resolveSalesforceFieldDataType(col string, schema *models.ObjectMetadata) string {
	if fm := lookupSalesforceFieldMetadata(col, schema); fm != nil {
		// Reuse the canonical describe field-type mapper: it resolves
		// formula/rollup to their scalar result type and normalizes every numeric
		// alias (number->double, integer->int, ...), then collapse that to the
		// narrower report dataType vocabulary. Routing through the one mapper keeps
		// report dataTypes consistent with describe and stops numeric computed
		// fields from falling through to "string".
		return sfTypeToReportDataType(mapFieldTypeToSF(*fm))
	}
	return "string"
}

func resolveSalesforceColumnAlias(col string, schema *models.ObjectMetadata) string {
	apiName := salesforceAnalyticsColumnCandidate(col)
	if schema != nil && schema.FieldsMap != nil {
		if fm, ok := schema.FieldsMap[apiName]; ok {
			if constants.IsReferenceType(fm.Type) {
				return apiName + "_Name"
			}
		}
	}
	return apiName
}

func lookupSalesforceFieldMetadata(col string, schema *models.ObjectMetadata) *models.FieldMetadata {
	if schema == nil || schema.FieldsMap == nil {
		return nil
	}
	base := strings.TrimSuffix(col, ".CONVERT")
	if fm, ok := schema.FieldsMap[col]; ok {
		return fm
	}
	if fm, ok := schema.FieldsMap[base]; ok {
		return fm
	}
	if idx := strings.LastIndex(base, "."); idx >= 0 {
		field := base[idx+1:]
		if fm, ok := schema.FieldsMap[field]; ok {
			return fm
		}
	}
	if candidate := salesforceAnalyticsColumnCandidate(base); candidate != base {
		if fm, ok := schema.FieldsMap[candidate]; ok {
			return fm
		}
	}
	return nil
}

func salesforceAnalyticsColumnCandidate(col string) string {
	if idx := strings.LastIndex(col, "."); idx >= 0 {
		col = col[idx+1:]
	}
	col = strings.TrimSuffix(col, ".CONVERT")
	if !strings.Contains(col, "_") {
		return col
	}
	return snakeToPascal(col)
}

// sfTypeToReportDataType collapses a canonical describe field type (from
// mapFieldTypeToSF) into the narrower Salesforce analytics/report dataType
// vocabulary: datetime is reported as date, and every non-numeric/non-temporal
// type (text, reference, picklist, ...) as string. Numeric types — including
// COUNT rollups (int) and number/currency/percent computed fields — keep their
// scalar dataType because mapFieldTypeToSF has already normalized the aliases.
func sfTypeToReportDataType(sfType string) string {
	switch sfType {
	case "date", "datetime":
		return "date"
	case "currency":
		return "currency"
	case "percent":
		return "percent"
	case "boolean":
		return "boolean"
	case "double":
		return "double"
	case "int":
		return "int"
	default:
		return "string"
	}
}

func fallbackSalesforceColumnLabel(col string) string {
	name := col
	if idx := strings.Index(name, "."); idx >= 0 {
		name = name[idx+1:]
	}
	name = strings.TrimSuffix(name, ".CONVERT")
	name = strings.TrimSuffix(name, "__c")
	name = strings.ReplaceAll(name, "_", " ")
	name = splitSalesforcePascalCase(name)
	return titleASCIIWords(name)
}

func splitSalesforcePascalCase(s string) string {
	var b strings.Builder
	b.Grow(len(s) + 4)
	for i, r := range s {
		if i > 0 && r >= 'A' && r <= 'Z' {
			prev := s[i-1]
			if prev >= 'a' && prev <= 'z' {
				b.WriteByte(' ')
			}
		}
		b.WriteRune(r)
	}
	return b.String()
}

func friendlySalesforceAggregateLabel(a models.ReportAggregate, schema *models.ObjectMetadata) string {
	if fm := lookupSalesforceFieldMetadata(a.Field, schema); fm != nil {
		return fm.Label
	}
	field := a.Field
	field = strings.TrimSuffix(field, ".CONVERT")
	if idx := strings.Index(field, "."); idx >= 0 {
		field = field[idx+1:]
	}
	field = strings.TrimSuffix(field, "__c")
	field = strings.ReplaceAll(field, "_", " ")
	return titleASCIIWords(field)
}

func effectiveSalesforceReportColumns(def *models.ReportDefinition, rows []models.SObject, isSummary bool) []string {
	if isSummary {
		return nil
	}
	if len(def.Columns) > 0 {
		return def.Columns
	}
	if len(rows) == 0 {
		return nil
	}
	cols := make([]string, 0, len(rows[0]))
	for k := range rows[0] {
		if !models.IsRecordMetadataKey(k) && k != "_metadata" {
			cols = append(cols, k)
		}
	}
	sort.Strings(cols)
	return cols
}

func resolveSalesforceReportColumnValue(row models.SObject, col string, schema *models.ObjectMetadata) any {
	if v, ok := row[col]; ok {
		return v
	}
	if strings.Contains(col, ".") {
		underscored := strings.ReplaceAll(col, ".", "_")
		if v, ok := row[underscored]; ok {
			return v
		}
	}
	base := strings.TrimSuffix(col, ".CONVERT")
	if base != col {
		if v := resolveSalesforceReportColumnValue(row, base, schema); v != nil {
			return v
		}
	}
	if mapped := resolveSalesforceColumnAlias(col, schema); mapped != "" {
		if v, ok := row[mapped]; ok {
			return v
		}
		if v, ok := row[mapped+"_Name"]; ok {
			return v
		}
	}
	if mapped := resolveSalesforceColumnAlias(base, schema); base != col && mapped != "" {
		if v, ok := row[mapped]; ok {
			return v
		}
		if v, ok := row[mapped+"_Name"]; ok {
			return v
		}
	}
	if idx := strings.LastIndex(col, "."); idx >= 0 {
		field := col[idx+1:]
		if v, ok := row[field]; ok {
			return v
		}
		field = strings.TrimSuffix(field, ".CONVERT")
		if v, ok := row[field]; ok {
			return v
		}
	}
	pascal := snakeToPascal(col)
	if pascal != col {
		if v, ok := row[pascal]; ok {
			return v
		}
	}
	if v, ok := row[col+"_Name"]; ok {
		return v
	}
	lower := strings.ToLower(strings.TrimSuffix(col, ".CONVERT"))
	for k, v := range row {
		if strings.ToLower(k) == lower {
			return v
		}
	}
	return nil
}

func looksLikeSalesforceAggregatedRows(rows []models.SObject) bool {
	if len(rows) == 0 {
		return false
	}
	_, hasName := rows[0]["name"]
	_, hasVal := rows[0]["val"]
	return hasName && hasVal
}

func formatSalesforceCellValue(v any) string {
	if v == nil {
		return ""
	}
	return fmt.Sprintf("%v", v)
}

func formatSalesforceNumber(v float64) string {
	if v == float64(int64(v)) {
		return fmt.Sprintf("%d", int64(v))
	}
	return fmt.Sprintf("%.2f", v)
}

func snakeToPascal(s string) string {
	if !strings.ContainsAny(s, "_- ") {
		return s
	}
	parts := strings.FieldsFunc(strings.ToLower(s), func(r rune) bool {
		return r == '_' || r == '-' || r == ' '
	})
	if len(parts) == 0 {
		return s
	}
	var b strings.Builder
	for _, part := range parts {
		if part == "" {
			continue
		}
		b.WriteString(strings.ToUpper(part[:1]))
		if len(part) > 1 {
			b.WriteString(part[1:])
		}
	}
	if b.Len() == 0 {
		return s
	}
	return b.String()
}

func titleASCIIWords(s string) string {
	parts := strings.Fields(s)
	for i, part := range parts {
		if part == "" {
			continue
		}
		if part == strings.ToUpper(part) {
			continue
		}
		parts[i] = strings.ToUpper(part[:1]) + strings.ToLower(part[1:])
	}
	return strings.Join(parts, " ")
}

func reportToFloat64(v any) (float64, bool) {
	switch n := v.(type) {
	case float64:
		return n, true
	case float32:
		return float64(n), true
	case int:
		return float64(n), true
	case int8:
		return float64(n), true
	case int16:
		return float64(n), true
	case int32:
		return float64(n), true
	case int64:
		return float64(n), true
	case uint:
		return float64(n), true
	case uint8:
		return float64(n), true
	case uint16:
		return float64(n), true
	case uint32:
		return float64(n), true
	case uint64:
		return float64(n), true
	case string:
		f, err := strconv.ParseFloat(strings.TrimSpace(n), 64)
		return f, err == nil
	default:
		return 0, false
	}
}
