package config

import (
	"fmt"
	"strings"
	"time"

	"github.com/spf13/viper"
)

// Config holds all configuration for the MPC system
type Config struct {
	Server      ServerConfig      `mapstructure:"server"`
	Database    DatabaseConfig    `mapstructure:"database"`
	Redis       RedisConfig       `mapstructure:"redis"`
	RabbitMQ    RabbitMQConfig    `mapstructure:"rabbitmq"`
	Consul      ConsulConfig      `mapstructure:"consul"`
	JWT         JWTConfig         `mapstructure:"jwt"`
	MPC         MPCConfig         `mapstructure:"mpc"`
	Logger      LoggerConfig      `mapstructure:"logger"`
}

// ServerConfig holds server-related configuration
type ServerConfig struct {
	GRPCPort    int           `mapstructure:"grpc_port"`
	HTTPPort    int           `mapstructure:"http_port"`
	Environment string        `mapstructure:"environment"`
	Timeout     time.Duration `mapstructure:"timeout"`
	TLSEnabled  bool          `mapstructure:"tls_enabled"`
	TLSCertFile string        `mapstructure:"tls_cert_file"`
	TLSKeyFile  string        `mapstructure:"tls_key_file"`
}

// DatabaseConfig holds database configuration
type DatabaseConfig struct {
	Host         string        `mapstructure:"host"`
	Port         int           `mapstructure:"port"`
	User         string        `mapstructure:"user"`
	Password     string        `mapstructure:"password"`
	DBName       string        `mapstructure:"dbname"`
	SSLMode      string        `mapstructure:"sslmode"`
	MaxOpenConns int           `mapstructure:"max_open_conns"`
	MaxIdleConns int           `mapstructure:"max_idle_conns"`
	ConnMaxLife  time.Duration `mapstructure:"conn_max_life"`
}

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

// RedisConfig holds Redis configuration
type RedisConfig struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	Password string `mapstructure:"password"`
	DB       int    `mapstructure:"db"`
}

// Addr returns the Redis address
func (c *RedisConfig) Addr() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

// RabbitMQConfig holds RabbitMQ configuration
type RabbitMQConfig struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	VHost    string `mapstructure:"vhost"`
}

// URL returns the RabbitMQ connection URL
func (c *RabbitMQConfig) URL() string {
	return fmt.Sprintf(
		"amqp://%s:%s@%s:%d/%s",
		c.User, c.Password, c.Host, c.Port, c.VHost,
	)
}

// ConsulConfig holds Consul configuration
type ConsulConfig struct {
	Host       string `mapstructure:"host"`
	Port       int    `mapstructure:"port"`
	ServiceID  string `mapstructure:"service_id"`
	Tags       []string `mapstructure:"tags"`
}

// Addr returns the Consul address
func (c *ConsulConfig) Addr() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

// JWTConfig holds JWT configuration
type JWTConfig struct {
	SecretKey     string        `mapstructure:"secret_key"`
	Issuer        string        `mapstructure:"issuer"`
	TokenExpiry   time.Duration `mapstructure:"token_expiry"`
	RefreshExpiry time.Duration `mapstructure:"refresh_expiry"`
}

// MPCConfig holds MPC-specific configuration
type MPCConfig struct {
	DefaultThresholdN int           `mapstructure:"default_threshold_n"`
	DefaultThresholdT int           `mapstructure:"default_threshold_t"`
	SessionTimeout    time.Duration `mapstructure:"session_timeout"`
	MessageTimeout    time.Duration `mapstructure:"message_timeout"`
	KeygenTimeout     time.Duration `mapstructure:"keygen_timeout"`
	SigningTimeout    time.Duration `mapstructure:"signing_timeout"`
	MaxParties        int           `mapstructure:"max_parties"`
}

// LoggerConfig holds logger configuration
type LoggerConfig struct {
	Level      string `mapstructure:"level"`
	Encoding   string `mapstructure:"encoding"`
	OutputPath string `mapstructure:"output_path"`
}

// Load loads configuration from file and environment variables
func Load(configPath string) (*Config, error) {
	v := viper.New()

	// Set default values
	setDefaults(v)

	// Read config file
	if configPath != "" {
		v.SetConfigFile(configPath)
	} else {
		v.SetConfigName("config")
		v.SetConfigType("yaml")
		v.AddConfigPath(".")
		v.AddConfigPath("./config")
		v.AddConfigPath("/etc/mpc-system/")
	}

	// Read environment variables
	v.SetEnvPrefix("MPC")
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AutomaticEnv()

	// Read config file (if exists)
	if err := v.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return nil, fmt.Errorf("failed to read config file: %w", err)
		}
		// Config file not found is not an error, we'll use defaults + env vars
	}

	var config Config
	if err := v.Unmarshal(&config); err != nil {
		return nil, fmt.Errorf("failed to unmarshal config: %w", err)
	}

	return &config, nil
}

// setDefaults sets default configuration values
func setDefaults(v *viper.Viper) {
	// Server defaults
	v.SetDefault("server.grpc_port", 50051)
	v.SetDefault("server.http_port", 8080)
	v.SetDefault("server.environment", "development")
	v.SetDefault("server.timeout", "30s")
	v.SetDefault("server.tls_enabled", false)

	// Database defaults
	v.SetDefault("database.host", "localhost")
	v.SetDefault("database.port", 5432)
	v.SetDefault("database.user", "mpc_user")
	v.SetDefault("database.password", "")
	v.SetDefault("database.dbname", "mpc_system")
	v.SetDefault("database.sslmode", "disable")
	v.SetDefault("database.max_open_conns", 25)
	v.SetDefault("database.max_idle_conns", 5)
	v.SetDefault("database.conn_max_life", "5m")

	// Redis defaults
	v.SetDefault("redis.host", "localhost")
	v.SetDefault("redis.port", 6379)
	v.SetDefault("redis.password", "")
	v.SetDefault("redis.db", 0)

	// RabbitMQ defaults
	v.SetDefault("rabbitmq.host", "localhost")
	v.SetDefault("rabbitmq.port", 5672)
	v.SetDefault("rabbitmq.user", "guest")
	v.SetDefault("rabbitmq.password", "guest")
	v.SetDefault("rabbitmq.vhost", "/")

	// Consul defaults
	v.SetDefault("consul.host", "localhost")
	v.SetDefault("consul.port", 8500)

	// JWT defaults
	v.SetDefault("jwt.issuer", "mpc-system")
	v.SetDefault("jwt.token_expiry", "15m")
	v.SetDefault("jwt.refresh_expiry", "24h")

	// MPC defaults
	v.SetDefault("mpc.default_threshold_n", 3)
	v.SetDefault("mpc.default_threshold_t", 2)
	v.SetDefault("mpc.session_timeout", "10m")
	v.SetDefault("mpc.message_timeout", "30s")
	v.SetDefault("mpc.keygen_timeout", "10m")
	v.SetDefault("mpc.signing_timeout", "5m")
	v.SetDefault("mpc.max_parties", 10)

	// Logger defaults
	v.SetDefault("logger.level", "info")
	v.SetDefault("logger.encoding", "json")
	v.SetDefault("logger.output_path", "stdout")
}

// MustLoad loads configuration and panics on error
func MustLoad(configPath string) *Config {
	cfg, err := Load(configPath)
	if err != nil {
		panic(fmt.Sprintf("failed to load config: %v", err))
	}
	return cfg
}
