package config

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

	"github.com/golang-jwt/jwt/v5"
	"github.com/spf13/viper"
)

type Config struct {
	Server   ServerConfig
	Database DatabaseConfig
	Auth     AuthConfig
	AI       AIConfig
	JWT      JWTConfig
}

type ServerConfig struct {
	Host         string
	Port         string
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

type DatabaseConfig struct {
	Host     string
	Port     string
	User     string
	Password string
	DBName   string
	SSLMode  string
	TimeZone string
}

type AuthConfig struct {
	JWTSecret           string
	JWTExpirationHours  int
	BCryptCost          int
	MaxLoginAttempts    int
	AccountLockDuration time.Duration
}

type AIConfig struct {
	OpenAI APIConfig
	Local  APIConfig
}

type APIConfig struct {
	APIKey      string
	Endpoint    string
	Model       string
	MaxTokens   int
	Temperature float64
	TopP        float64
	Timeout     time.Duration
}

type JWTConfig struct {
	Secret          string
	ExpirationHours int
	Issuer          string
	Audience        string
}

var AppConfig Config

// LoadConfig loads configuration from environment variables and config file
func LoadConfig() error {
	// Set default values
	viper.SetDefault("server.host", "0.0.0.0")
	viper.SetDefault("server.port", "8080")
	viper.SetDefault("server.readTimeout", "15s")
	viper.SetDefault("server.writeTimeout", "15s")

	viper.SetDefault("database.host", "localhost")
	viper.SetDefault("database.port", "5432")
	viper.SetDefault("database.user", "postgres")
	viper.SetDefault("database.password", "postgres")
	viper.SetDefault("database.dbname", "support")
	viper.SetDefault("database.sslmode", "disable")
	viper.SetDefault("database.timezone", "UTC")

	viper.SetDefault("auth.jwtSecret", "your-secret-key")
	viper.SetDefault("auth.jwtExpirationHours", 24)
	viper.SetDefault("auth.bcryptCost", 12)
	viper.SetDefault("auth.maxLoginAttempts", 5)
	viper.SetDefault("auth.accountLockDuration", "30m")

	viper.SetDefault("ai.openai.maxTokens", 4000)
	viper.SetDefault("ai.openai.temperature", 0.7)
	viper.SetDefault("ai.openai.topP", 1.0)
	viper.SetDefault("ai.openai.timeout", "30s")

	viper.SetDefault("ai.local.maxTokens", 2000)
	viper.SetDefault("ai.local.temperature", 0.7)
	viper.SetDefault("ai.local.topP", 1.0)
	viper.SetDefault("ai.local.timeout", "60s")
	viper.SetDefault("ai.local.endpoint", "http://localhost:11434")
	viper.SetDefault("ai.local.model", "llama2")

	viper.SetDefault("jwt.secret", "your-jwt-secret")
	viper.SetDefault("jwt.expirationHours", 24)
	viper.SetDefault("jwt.issuer", "support-system")
	viper.SetDefault("jwt.audience", "support-users")

	// Configure viper to read from environment variables
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// Read config file if it exists
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath("./")
	viper.AddConfigPath("./config")
	viper.AddConfigPath("../config")

	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return fmt.Errorf("error reading config file: %w", err)
		}
		// Config file not found; ignore error
	}

	// Unmarshal config
	if err := viper.Unmarshal(&AppConfig); err != nil {
		return fmt.Errorf("unable to decode config: %w", err)
	}

	return nil
}

// GetDSN returns the database connection string
func (c *DatabaseConfig) GetDSN() string {
	return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=%s",
		c.Host, c.User, c.Password, c.DBName, c.Port, c.SSLMode, c.TimeZone)
}

// GetServerAddress returns the server address
func (c *ServerConfig) GetServerAddress() string {
	return fmt.Sprintf("%s:%s", c.Host, c.Port)
}

// GetJWTSigningKey returns the JWT signing key
func (c *JWTConfig) GetJWTSigningKey() []byte {
	return []byte(c.Secret)
}

// ParseJWTToken parses a JWT token and returns the claims
func (c *JWTConfig) ParseJWTToken(tokenString string) (*jwt.MapClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return c.GetJWTSigningKey(), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return &claims, nil
	}

	return nil, fmt.Errorf("invalid token")
}

// GetEnv gets an environment variable with a default value
func GetEnv(key, defaultValue string) string {
	if value, exists := os.LookupEnv(key); exists {
		return value
	}
	return defaultValue
}

// GetEnvAsInt gets an environment variable as an integer with a default value
func GetEnvAsInt(key string, defaultValue int) int {
	valueStr := GetEnv(key, "")
	if value, err := strconv.Atoi(valueStr); err == nil {
		return value
	}
	return defaultValue
}

// GetEnvAsBool gets an environment variable as a boolean with a default value
func GetEnvAsBool(key string, defaultValue bool) bool {
	valueStr := GetEnv(key, "")
	if value, err := strconv.ParseBool(valueStr); err == nil {
		return value
	}
	return defaultValue
}

// GetEnvAsDuration gets an environment variable as a duration with a default value
func GetEnvAsDuration(key string, defaultValue time.Duration) time.Duration {
	valueStr := GetEnv(key, "")
	if value, err := time.ParseDuration(valueStr); err == nil {
		return value
	}
	return defaultValue
}
