package utils

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

const (
	Byte     = 1
	Kilobyte = Byte * 1000
	Megabyte = Kilobyte * 1000
	Gigabyte = Megabyte * 1000
	Terabyte = Gigabyte * 1000
)

// StringToBps converts a string to a bandwidth value in bytes per second.
// E.g. "100 Mbps", "512 kbps", "1g" are all valid.
func StringToBps(s string) (uint64, error) {
	s = strings.ToLower(strings.TrimSpace(s))
	spl := 0
	for i, c := range s {
		if c < '0' || c > '9' {
			spl = i
			break
		}
	}
	if spl == 0 {
		// No unit or no value
		return 0, errors.New("invalid format")
	}
	v, err := strconv.ParseUint(s[:spl], 10, 64)
	if err != nil {
		return 0, err
	}
	unit := strings.TrimSpace(s[spl:])

	switch strings.ToLower(unit) {
	case "b", "bps":
		return v * Byte / 8, nil
	case "k", "kb", "kbps":
		return v * Kilobyte / 8, nil
	case "m", "mb", "mbps":
		return v * Megabyte / 8, nil
	case "g", "gb", "gbps":
		return v * Gigabyte / 8, nil
	case "t", "tb", "tbps":
		return v * Terabyte / 8, nil
	default:
		return 0, errors.New("unsupported unit")
	}
}

// ConvBandwidth handles both string and int types for bandwidth.
// When using string, it will be parsed as a bandwidth string with units.
// When using int, it will be parsed as a raw bandwidth in bytes per second.
// It does NOT support float types.
func ConvBandwidth(bw interface{}) (uint64, error) {
	switch bwT := bw.(type) {
	case string:
		return StringToBps(bwT)
	case int:
		return uint64(bwT), nil
	default:
		return 0, fmt.Errorf("invalid type %T for bandwidth", bwT)
	}
}
