package query

import (
	"database/sql"

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

// ScanRowsToMaps scans SQL rows into neutral field-value maps.
func ScanRowsToMaps(rows *sql.Rows) ([]map[string]any, error) {
	columns, err := rows.Columns()
	if err != nil {
		return nil, err
	}

	results := make([]map[string]any, 0)
	for rows.Next() {
		values := make([]interface{}, len(columns))
		valuePtrs := make([]interface{}, len(columns))
		for i := range values {
			valuePtrs[i] = &values[i]
		}

		if err := rows.Scan(valuePtrs...); err != nil {
			return nil, err
		}

		record := make(map[string]any)
		for i, col := range columns {
			record[col] = scannedStorageValue(values[i])
		}

		results = append(results, record)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	return results, nil
}

// ScanRowsToSObjects preserves the historical record-facing scanner while
// neutral storage and policy paths consume ScanRowsToMaps directly.
func ScanRowsToSObjects(rows *sql.Rows) ([]models.SObject, error) {
	values, err := ScanRowsToMaps(rows)
	if err != nil {
		return nil, err
	}
	results := make([]models.SObject, len(values))
	for i := range values {
		results[i] = models.SObject(values[i])
	}
	return results, nil
}

func scannedStorageValue(value interface{}) interface{} {
	if bytes, ok := value.([]byte); ok {
		// Keep the driver's text byte-for-byte. DECIMAL scale and trailing
		// coefficient zeroes are semantic input to metadata-aware hydration;
		// the scanner has no schema authority to discard them. This also avoids
		// rewriting numeric-looking VARCHAR values.
		return string(bytes)
	}
	return value
}
