// Package envcompat reads environment variables with rename-tolerant lookups.
//
// AgentTeam and MeshX occasionally rename env vars to keep namespaces coherent.
// Each rename is recorded in Registry; callers pass the new name plus any
// legacy names, and Lookup tries them in order. Existing deployments that
// still set legacy names keep working unchanged.
package envcompat

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

// Lookup returns the value, the env name that yielded it, and whether any
// source had a value. Tries preferred first, then legacy names in declared
// order. Whitespace-only values are treated as unset, matching the trim-empty
// convention used by AgentTeam's config layer.
func Lookup(preferred string, legacy ...string) (value, source string, ok bool) {
	if v, found := os.LookupEnv(preferred); found && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v), preferred, true
	}
	for _, name := range legacy {
		if v, found := os.LookupEnv(name); found && strings.TrimSpace(v) != "" {
			return strings.TrimSpace(v), name, true
		}
	}
	return "", "", false
}

// String returns the looked-up value or fallback when nothing is set.
func String(preferred, fallback string, legacy ...string) string {
	if v, _, ok := Lookup(preferred, legacy...); ok {
		return v
	}
	return fallback
}

// Bool parses the looked-up value as true/false (case-insensitive). Unset
// returns fallback. Invalid values return fallback with an error naming the
// env var that produced the bad input.
func Bool(preferred string, fallback bool, legacy ...string) (bool, error) {
	v, source, ok := Lookup(preferred, legacy...)
	if !ok {
		return fallback, nil
	}
	switch strings.ToLower(v) {
	case "true":
		return true, nil
	case "false":
		return false, nil
	default:
		return fallback, fmt.Errorf("%s must be true or false", source)
	}
}

// Duration parses the looked-up value as a time.Duration. Unset returns
// fallback. Parse errors return zero with an error naming the offending var.
func Duration(preferred string, fallback time.Duration, legacy ...string) (time.Duration, error) {
	v, source, ok := Lookup(preferred, legacy...)
	if !ok {
		return fallback, nil
	}
	parsed, err := time.ParseDuration(v)
	if err != nil {
		return 0, fmt.Errorf("%s: %w", source, err)
	}
	return parsed, nil
}

// Int parses the looked-up value as an int. Unset returns fallback. Parse
// errors return zero with an error naming the offending var.
func Int(preferred string, fallback int, legacy ...string) (int, error) {
	v, source, ok := Lookup(preferred, legacy...)
	if !ok {
		return fallback, nil
	}
	parsed, err := strconv.Atoi(v)
	if err != nil {
		return 0, fmt.Errorf("%s: %w", source, err)
	}
	return parsed, nil
}
