// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package cast

import (
	"encoding/json"
	"fmt"
	"html/template"
	"strconv"
	"time"
)

// ToBoolE casts any value to a bool type.
func ToBoolE(i any) (bool, error) {
	i, _ = indirect(i)

	switch b := i.(type) {
	case bool:
		return b, nil
	case nil:
		return false, nil
	case int:
		return b != 0, nil
	case int8:
		return b != 0, nil
	case int16:
		return b != 0, nil
	case int32:
		return b != 0, nil
	case int64:
		return b != 0, nil
	case uint:
		return b != 0, nil
	case uint8:
		return b != 0, nil
	case uint16:
		return b != 0, nil
	case uint32:
		return b != 0, nil
	case uint64:
		return b != 0, nil
	case float32:
		return b != 0, nil
	case float64:
		return b != 0, nil
	case time.Duration:
		return b != 0, nil
	case string:
		return strconv.ParseBool(b)
	case json.Number:
		v, err := ToInt64E(b)
		if err == nil {
			return v != 0, nil
		}

		return false, fmt.Errorf(errorMsg, i, i, false)
	default:
		if i, ok := resolveAlias(i); ok {
			return ToBoolE(i)
		}

		return false, fmt.Errorf(errorMsg, i, i, false)
	}
}

// ToStringE casts any value to a string type.
func ToStringE(i any) (string, error) {
	switch s := i.(type) {
	case string:
		return s, nil
	case bool:
		return strconv.FormatBool(s), nil
	case float64:
		return strconv.FormatFloat(s, 'f', -1, 64), nil
	case float32:
		return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
	case int:
		return strconv.Itoa(s), nil
	case int8:
		return strconv.FormatInt(int64(s), 10), nil
	case int16:
		return strconv.FormatInt(int64(s), 10), nil
	case int32:
		return strconv.FormatInt(int64(s), 10), nil
	case int64:
		return strconv.FormatInt(s, 10), nil
	case uint:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint8:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint16:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint32:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint64:
		return strconv.FormatUint(s, 10), nil
	case json.Number:
		return s.String(), nil
	case []byte:
		return string(s), nil
	case template.HTML:
		return string(s), nil
	case template.URL:
		return string(s), nil
	case template.JS:
		return string(s), nil
	case template.CSS:
		return string(s), nil
	case template.HTMLAttr:
		return string(s), nil
	case nil:
		return "", nil
	case fmt.Stringer:
		return s.String(), nil
	case error:
		return s.Error(), nil
	default:
		if i, ok := indirect(i); ok {
			return ToStringE(i)
		}

		if i, ok := resolveAlias(i); ok {
			return ToStringE(i)
		}

		return "", fmt.Errorf(errorMsg, i, i, "")
	}
}
