package common

import (
	"fmt"
	"strings"
	"sync"
)

// Variable represents a configuration variable with name, value, and optional comment
type Variable struct {
	name    string
	value   string
	comment string
}

// NewVariable creates a new variable
func NewVariable(name, value, comment string) *Variable {
	return &Variable{
		name:    name,
		value:   value,
		comment: comment,
	}
}

// GetName returns the variable name
func (v *Variable) GetName() string {
	return v.name
}

// GetValue returns the variable value
func (v *Variable) GetValue() string {
	return v.value
}

// GetComment returns the variable comment
func (v *Variable) GetComment() string {
	return v.comment
}

// GetNameValuePair returns the name and value as a single string
func (v *Variable) GetNameValuePair() string {
	return fmt.Sprintf("%s %s", v.name, v.value)
}

// SetValue updates the variable value
func (v *Variable) SetValue(value string) {
	v.value = value
}

// Variables manages a collection of configuration variables
type Variables struct {
	variables map[string]*Variable
	mutex     sync.RWMutex
}

// NewVariables creates a new variables manager
func NewVariables() *Variables {
	return &Variables{
		variables: make(map[string]*Variable),
	}
}

// AddVariable adds a variable to the collection
func (v *Variables) AddVariable(variable *Variable) {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	v.variables[variable.name] = variable
}

// FindVariable finds a variable by exact name
func (v *Variables) FindVariable(name string) *Variable {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	return v.variables[name]
}

// GetVariable is an alias for FindVariable for convenience
func (v *Variables) GetVariable(name string) *Variable {
	return v.FindVariable(name)
}

// GetVariables returns all variables that contain the partial name
func (v *Variables) GetVariables(partialName string) []*Variable {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	results := make([]*Variable, 0)
	partialLower := strings.ToLower(partialName)

	for name, variable := range v.variables {
		if strings.Contains(strings.ToLower(name), partialLower) {
			results = append(results, variable)
		}
	}

	return results
}

// GetAllVariables returns all variables in the collection
func (v *Variables) GetAllVariables() map[string]*Variable {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	// Return a copy to prevent external modification
	result := make(map[string]*Variable)
	for name, variable := range v.variables {
		result[name] = variable
	}

	return result
}

// GetVariableNames returns all variable names
func (v *Variables) GetVariableNames() []string {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	names := make([]string, 0, len(v.variables))
	for name := range v.variables {
		names = append(names, name)
	}

	return names
}

// SetVariable sets or updates a variable value
func (v *Variables) SetVariable(name, value string) {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	if variable, exists := v.variables[name]; exists {
		variable.SetValue(value)
	} else {
		// Create new variable if it doesn't exist
		v.variables[name] = NewVariable(name, value, "")
	}
}

// DeleteVariable removes a variable by name
func (v *Variables) DeleteVariable(name string) bool {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	if _, exists := v.variables[name]; exists {
		delete(v.variables, name)
		return true
	}

	return false
}

// ClearVariables removes all variables
func (v *Variables) ClearVariables() {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	v.variables = make(map[string]*Variable)
}

// Count returns the number of variables
func (v *Variables) Count() int {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	return len(v.variables)
}

// HasVariable checks if a variable exists
func (v *Variables) HasVariable(name string) bool {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	_, exists := v.variables[name]
	return exists
}

// GetVariableValue returns just the value of a variable, or empty string if not found
func (v *Variables) GetVariableValue(name string) string {
	if variable := v.FindVariable(name); variable != nil {
		return variable.GetValue()
	}
	return ""
}

// GetVariableValueWithDefault returns the value of a variable, or a default if not found
func (v *Variables) GetVariableValueWithDefault(name, defaultValue string) string {
	if variable := v.FindVariable(name); variable != nil {
		return variable.GetValue()
	}
	return defaultValue
}

// GetVariableAsInt attempts to parse a variable value as an integer
func (v *Variables) GetVariableAsInt(name string, defaultValue int) int {
	if variable := v.FindVariable(name); variable != nil {
		var intValue int
		if _, err := fmt.Sscanf(variable.GetValue(), "%d", &intValue); err == nil {
			return intValue
		}
	}
	return defaultValue
}

// GetVariableAsFloat attempts to parse a variable value as a float
func (v *Variables) GetVariableAsFloat(name string, defaultValue float64) float64 {
	if variable := v.FindVariable(name); variable != nil {
		var floatValue float64
		if _, err := fmt.Sscanf(variable.GetValue(), "%f", &floatValue); err == nil {
			return floatValue
		}
	}
	return defaultValue
}

// GetVariableAsBool attempts to parse a variable value as a boolean
func (v *Variables) GetVariableAsBool(name string, defaultValue bool) bool {
	if variable := v.FindVariable(name); variable != nil {
		value := strings.ToLower(variable.GetValue())
		switch value {
		case "true", "1", "yes", "on", "enabled":
			return true
		case "false", "0", "no", "off", "disabled":
			return false
		}
	}
	return defaultValue
}

// Clone creates a deep copy of the variables collection
func (v *Variables) Clone() *Variables {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	newVars := NewVariables()
	for name, variable := range v.variables {
		newVar := NewVariable(variable.name, variable.value, variable.comment)
		newVars.variables[name] = newVar
	}

	return newVars
}

// Merge merges another Variables collection into this one
// If overwrite is true, existing variables will be overwritten
func (v *Variables) Merge(other *Variables, overwrite bool) {
	if other == nil {
		return
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	otherVars := other.GetAllVariables()
	for name, variable := range otherVars {
		if _, exists := v.variables[name]; !exists || overwrite {
			v.variables[name] = NewVariable(variable.name, variable.value, variable.comment)
		}
	}
}
