package config

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

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"

	"dance-lessons-coach/pkg/version"
)

// Config represents the application configuration
type Config struct {
	Server    ServerConfig    `mapstructure:"server"`
	Shutdown  ShutdownConfig  `mapstructure:"shutdown"`
	Logging   LoggingConfig   `mapstructure:"logging"`
	Telemetry TelemetryConfig `mapstructure:"telemetry"`
	API       APIConfig       `mapstructure:"api"`
	Auth      AuthConfig      `mapstructure:"auth"`
}

// ServerConfig holds server-related configuration
type ServerConfig struct {
	Host string `mapstructure:"host"`
	Port int    `mapstructure:"port"`
}

// ShutdownConfig holds shutdown-related configuration
type ShutdownConfig struct {
	Timeout time.Duration `mapstructure:"timeout"`
}

// LoggingConfig holds logging-related configuration
type LoggingConfig struct {
	JSON   bool   `mapstructure:"json"`
	Level  string `mapstructure:"level"`
	Output string `mapstructure:"output"`
}

// TelemetryConfig holds OpenTelemetry-related configuration
type TelemetryConfig struct {
	Enabled      bool                       `mapstructure:"enabled"`
	OTLPEndpoint string                     `mapstructure:"otlp_endpoint"`
	ServiceName  string                     `mapstructure:"service_name"`
	Insecure     bool                       `mapstructure:"insecure"`
	Sampler      SamplerConfig              `mapstructure:"sampler"`
	Persistence  PersistenceTelemetryConfig `mapstructure:"persistence"`
}

// PersistenceTelemetryConfig holds persistence layer telemetry configuration
type PersistenceTelemetryConfig struct {
	Enabled bool `mapstructure:"enabled"`
}

// APIConfig holds API version configuration
type APIConfig struct {
	V2Enabled bool `mapstructure:"v2_enabled"`
}

// AuthConfig holds authentication configuration
type AuthConfig struct {
	JWTSecret           string `mapstructure:"jwt_secret"`
	AdminMasterPassword string `mapstructure:"admin_master_password"`
}

// VersionInfo holds application version information
type VersionInfo struct {
	Version   string `mapstructure:"-"` // Set via ldflags
	Commit    string `mapstructure:"-"` // Set via ldflags
	Date      string `mapstructure:"-"` // Set via ldflags
	GoVersion string `mapstructure:"-"` // Set at runtime
}

// VersionCommand handles version display
func (c *Config) VersionCommand() string {
	// This will be enhanced when we integrate with cobra
	return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s, go: %s)",
		version.Version, version.Commit, version.Date, version.GoVersion)
}

// SamplerConfig holds tracing sampler configuration
type SamplerConfig struct {
	Type  string  `mapstructure:"type"`
	Ratio float64 `mapstructure:"ratio"`
}

// LoadConfig loads configuration from file, environment variables, and defaults
// Configuration priority: file > environment variables > defaults
// To specify a custom config file path, set DLC_CONFIG_FILE environment variable
func LoadConfig() (*Config, error) {
	v := viper.New()

	// Set up initial console logging for config loading messages
	consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
	log.Logger = log.Output(consoleWriter)

	// Set default values
	v.SetDefault("server.host", "0.0.0.0")
	v.SetDefault("server.port", 8080)
	v.SetDefault("shutdown.timeout", 30*time.Second)
	v.SetDefault("logging.json", false)
	v.SetDefault("logging.level", "trace")
	v.SetDefault("logging.output", "")

	// Telemetry defaults
	v.SetDefault("telemetry.enabled", false)
	v.SetDefault("telemetry.otlp_endpoint", "localhost:4317")
	v.SetDefault("telemetry.service_name", "DanceLessonsCoach")
	v.SetDefault("telemetry.insecure", true)
	v.SetDefault("telemetry.sampler.type", "parentbased_always_on")
	v.SetDefault("telemetry.sampler.ratio", 1.0)
	v.SetDefault("telemetry.persistence.enabled", false)

	// API defaults
	v.SetDefault("api.v2_enabled", false)

	// Auth defaults
	v.SetDefault("auth.jwt_secret", "default-secret-key-please-change-in-production")
	v.SetDefault("auth.admin_master_password", "admin123")

	// Check for custom config file path via environment variable
	if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" {
		v.SetConfigFile(configFile)
		log.Info().Str("config_file", configFile).Msg("Using custom config file path")
	} else {
		// Default: look for config.yaml in current directory
		v.SetConfigName("config")
		v.SetConfigType("yaml")
		v.AddConfigPath(".")
	}

	// Read config file if it exists
	if err := v.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			// Config file was found but there was an error reading it
			log.Warn().Err(err).Msg("Error reading config file, using defaults")
		}
		// Config file not found, continue with environment variables and defaults
	} else {
		log.Info().Str("config_file", v.ConfigFileUsed()).Msg("Config file loaded")
	}

	// Bind environment variables
	v.AutomaticEnv()
	v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix
	v.BindEnv("server.host", "DLC_SERVER_HOST")
	v.BindEnv("server.port", "DLC_SERVER_PORT")
	v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT")
	v.BindEnv("logging.json", "DLC_LOGGING_JSON")
	v.BindEnv("logging.level", "DLC_LOGGING_LEVEL")
	v.BindEnv("logging.output", "DLC_LOGGING_OUTPUT")

	// Telemetry environment variables
	v.BindEnv("telemetry.enabled", "DLC_TELEMETRY_ENABLED")
	v.BindEnv("telemetry.otlp_endpoint", "DLC_TELEMETRY_OTLP_ENDPOINT")
	v.BindEnv("telemetry.service_name", "DLC_TELEMETRY_SERVICE_NAME")
	v.BindEnv("telemetry.insecure", "DLC_TELEMETRY_INSECURE")

	// Auth environment variables
	v.BindEnv("auth.jwt_secret", "DLC_AUTH_JWT_SECRET")
	v.BindEnv("auth.admin_master_password", "DLC_AUTH_ADMIN_MASTER_PASSWORD")
	v.BindEnv("telemetry.sampler.type", "DLC_TELEMETRY_SAMPLER_TYPE")
	v.BindEnv("telemetry.sampler.ratio", "DLC_TELEMETRY_SAMPLER_RATIO")

	// API environment variables
	v.BindEnv("api.v2_enabled", "DLC_API_V2_ENABLED")

	// Unmarshal into Config struct
	var config Config
	if err := v.Unmarshal(&config); err != nil {
		log.Error().Err(err).Msg("Failed to unmarshal config")
		return nil, fmt.Errorf("config unmarshal error: %w", err)
	}

	// Configure log output format (JSON or console) first
	if config.Logging.JSON {
		log.Logger = log.Output(os.Stderr)
	} else {
		consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
		log.Logger = log.Output(consoleWriter)
	}

	// Setup logging based on configuration
	config.SetupLogging()

	log.Info().
		Str("host", config.Server.Host).
		Int("port", config.Server.Port).
		Dur("shutdown_timeout", config.Shutdown.Timeout).
		Bool("logging_json", config.Logging.JSON).
		Str("logging_level", config.Logging.Level).
		Str("logging_output", config.Logging.Output).
		Bool("telemetry_enabled", config.Telemetry.Enabled).
		Str("telemetry_service", config.Telemetry.ServiceName).
		Bool("api_v2_enabled", config.API.V2Enabled).
		Msg("Configuration loaded")

	return &config, nil
}

// GetServerAddress returns the formatted server address (host:port)
func (c *Config) GetServerAddress() string {
	return fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
}

// GetTelemetryEnabled returns whether telemetry is enabled
func (c *Config) GetTelemetryEnabled() bool {
	return c.Telemetry.Enabled
}

// GetOTLPEndpoint returns the OTLP endpoint for telemetry
func (c *Config) GetOTLPEndpoint() string {
	return c.Telemetry.OTLPEndpoint
}

// GetServiceName returns the service name for telemetry
func (c *Config) GetServiceName() string {
	return c.Telemetry.ServiceName
}

// GetPersistenceTelemetryEnabled returns whether persistence layer telemetry is enabled
func (c *Config) GetPersistenceTelemetryEnabled() bool {
	return c.Telemetry.Enabled && c.Telemetry.Persistence.Enabled
}

// GetTelemetryInsecure returns whether to use insecure connection
func (c *Config) GetTelemetryInsecure() bool {
	return c.Telemetry.Insecure
}

// GetSamplerType returns the sampler type
func (c *Config) GetSamplerType() string {
	return c.Telemetry.Sampler.Type
}

// GetSamplerRatio returns the sampler ratio
func (c *Config) GetSamplerRatio() float64 {
	return c.Telemetry.Sampler.Ratio
}

// GetV2Enabled returns whether v2 API is enabled
func (c *Config) GetV2Enabled() bool {
	return c.API.V2Enabled
}

// GetJWTSecret returns the JWT secret
func (c *Config) GetJWTSecret() string {
	return c.Auth.JWTSecret
}

// GetAdminMasterPassword returns the admin master password
func (c *Config) GetAdminMasterPassword() string {
	return c.Auth.AdminMasterPassword
}

// GetLogLevel returns the logging level
func (c *Config) GetLogLevel() string {
	return c.Logging.Level
}

// GetLogOutput returns the log output path
func (c *Config) GetLogOutput() string {
	return c.Logging.Output
}

// SetupLogging configures zerolog based on the configuration
func (c *Config) SetupLogging() {
	// Parse log level
	level := parseLogLevel(c.GetLogLevel())
	zerolog.SetGlobalLevel(level)

	// Setup log output
	c.setupLogOutput()

	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
}

// parseLogLevel converts a string log level to zerolog.Level
func parseLogLevel(level string) zerolog.Level {
	switch strings.ToLower(level) {
	case "trace":
		return zerolog.TraceLevel
	case "debug":
		return zerolog.DebugLevel
	case "info":
		return zerolog.InfoLevel
	case "warn", "warning":
		return zerolog.WarnLevel
	case "error":
		return zerolog.ErrorLevel
	case "fatal":
		return zerolog.FatalLevel
	case "panic":
		return zerolog.PanicLevel
	default:
		log.Warn().Str("level", level).Msg("Unknown log level, defaulting to trace")
		return zerolog.TraceLevel
	}
}

// setupLogOutput configures the log output based on configuration
func (c *Config) setupLogOutput() {
	output := c.GetLogOutput()
	if output == "" {
		// Use stderr by default
		return
	}

	// Open the log file
	file, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Error().Err(err).Str("output", output).Msg("Failed to open log file, using stderr")
		return
	}

	// Set the log output
	log.Logger = log.Output(file)
	log.Trace().Str("output", output).Msg("Logging to file")
}
