package database

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

	"github.com/spf13/viper"
	"gorm.io/gorm/logger"
)

// Config holds database configuration settings
type Config struct {
	// Database file path
	DatabasePath string

	// Connection pool settings
	MaxIdleConns    int
	MaxOpenConns    int
	ConnMaxLifetime time.Duration
	ConnMaxIdleTime time.Duration

	// Logging settings
	LogLevel     logger.LogLevel
	SlowQueryLog time.Duration

	// Migration settings
	AutoMigrate     bool
	DropTableFirst  bool
	CreateBatchSize int

	// Performance settings
	PrepareStmt                      bool
	DisableForeignKeyCheck           bool
	IgnoreRelationshipsWhenMigrating bool

	// Development settings
	Debug           bool
	DryRun          bool
	QueryFields     bool
	CreateInBatches int
}

// DefaultConfig returns a configuration with sensible defaults
func DefaultConfig() *Config {
	return &Config{
		DatabasePath:                     "fuel_stops.db",
		MaxIdleConns:                     10,
		MaxOpenConns:                     100,
		ConnMaxLifetime:                  time.Hour,
		ConnMaxIdleTime:                  30 * time.Minute,
		LogLevel:                         logger.Silent,
		SlowQueryLog:                     200 * time.Millisecond,
		AutoMigrate:                      true,
		DropTableFirst:                   false,
		CreateBatchSize:                  1000,
		PrepareStmt:                      true,
		DisableForeignKeyCheck:           false,
		IgnoreRelationshipsWhenMigrating: false,
		Debug:                            false,
		DryRun:                           false,
		QueryFields:                      false,
		CreateInBatches:                  100,
	}
}

// LoadFromConfig loads configuration from config file using Viper
func LoadFromConfig(configPath string) *Config {
	config := DefaultConfig()

	// Initialize Viper
	v := viper.New()

	// Set config file path if provided
	if configPath != "" {
		v.SetConfigFile(configPath)
	} else {
		// Search for config file in multiple locations
		v.SetConfigName("config")
		v.SetConfigType("yaml")
		v.AddConfigPath(".")
		v.AddConfigPath("./config")
		v.AddConfigPath("$HOME/.tankstopp")
		v.AddConfigPath("/etc/tankstopp")
	}

	// Try to read config file
	if err := v.ReadInConfig(); err != nil {
		// If config file not found, fall back to environment variables
		return LoadFromEnv()
	}

	// Load database configuration from Viper
	if v.IsSet("database.path") {
		config.DatabasePath = v.GetString("database.path")
	}

	// Connection pool settings
	if v.IsSet("database.connection_pool.max_idle_connections") {
		config.MaxIdleConns = v.GetInt("database.connection_pool.max_idle_connections")
	}
	if v.IsSet("database.connection_pool.max_open_connections") {
		config.MaxOpenConns = v.GetInt("database.connection_pool.max_open_connections")
	}
	if v.IsSet("database.connection_pool.connection_max_lifetime") {
		config.ConnMaxLifetime = v.GetDuration("database.connection_pool.connection_max_lifetime")
	}
	if v.IsSet("database.connection_pool.connection_max_idle_time") {
		config.ConnMaxIdleTime = v.GetDuration("database.connection_pool.connection_max_idle_time")
	}

	// Logging settings
	if v.IsSet("database.logging.level") {
		config.LogLevel = getLogLevelFromString(v.GetString("database.logging.level"))
	}
	if v.IsSet("database.logging.debug") {
		config.Debug = v.GetBool("database.logging.debug")
	}
	if v.IsSet("database.logging.slow_query_threshold") {
		config.SlowQueryLog = v.GetDuration("database.logging.slow_query_threshold")
	}

	// Migration settings
	if v.IsSet("database.migration.auto_migrate") {
		config.AutoMigrate = v.GetBool("database.migration.auto_migrate")
	}
	if v.IsSet("database.migration.drop_tables_first") {
		config.DropTableFirst = v.GetBool("database.migration.drop_tables_first")
	}
	if v.IsSet("database.migration.create_batch_size") {
		config.CreateBatchSize = v.GetInt("database.migration.create_batch_size")
	}

	// Performance settings
	if v.IsSet("database.performance.prepare_statements") {
		config.PrepareStmt = v.GetBool("database.performance.prepare_statements")
	}
	if v.IsSet("database.performance.disable_foreign_key_check") {
		config.DisableForeignKeyCheck = v.GetBool("database.performance.disable_foreign_key_check")
	}
	if v.IsSet("database.performance.ignore_relationships_when_migrating") {
		config.IgnoreRelationshipsWhenMigrating = v.GetBool("database.performance.ignore_relationships_when_migrating")
	}
	if v.IsSet("database.performance.query_fields") {
		config.QueryFields = v.GetBool("database.performance.query_fields")
	}
	if v.IsSet("database.performance.dry_run") {
		config.DryRun = v.GetBool("database.performance.dry_run")
	}
	if v.IsSet("database.performance.create_in_batches") {
		config.CreateInBatches = v.GetInt("database.performance.create_in_batches")
	}

	// Environment variables still take precedence over config file
	config = mergeWithEnvVars(config)

	return config
}

// LoadFromEnv loads configuration from environment variables
func LoadFromEnv() *Config {
	config := DefaultConfig()

	// Database path
	if dbPath := os.Getenv("DB_PATH"); dbPath != "" {
		config.DatabasePath = dbPath
	}

	// Connection pool settings
	if maxIdle := getEnvInt("DB_MAX_IDLE_CONNS", config.MaxIdleConns); maxIdle > 0 {
		config.MaxIdleConns = maxIdle
	}

	if maxOpen := getEnvInt("DB_MAX_OPEN_CONNS", config.MaxOpenConns); maxOpen > 0 {
		config.MaxOpenConns = maxOpen
	}

	if lifetime := getEnvDuration("DB_CONN_MAX_LIFETIME", config.ConnMaxLifetime); lifetime > 0 {
		config.ConnMaxLifetime = lifetime
	}

	if idleTime := getEnvDuration("DB_CONN_MAX_IDLE_TIME", config.ConnMaxIdleTime); idleTime > 0 {
		config.ConnMaxIdleTime = idleTime
	}

	// Logging settings
	config.LogLevel = getLogLevel()
	config.Debug = getEnvBool("DB_DEBUG", config.Debug)

	if slowLog := getEnvDuration("DB_SLOW_QUERY_LOG", config.SlowQueryLog); slowLog > 0 {
		config.SlowQueryLog = slowLog
	}

	// Migration settings
	config.AutoMigrate = getEnvBool("DB_AUTO_MIGRATE", config.AutoMigrate)
	config.DropTableFirst = getEnvBool("DB_DROP_TABLE_FIRST", config.DropTableFirst)

	if batchSize := getEnvInt("DB_CREATE_BATCH_SIZE", config.CreateBatchSize); batchSize > 0 {
		config.CreateBatchSize = batchSize
	}

	// Performance settings
	config.PrepareStmt = getEnvBool("DB_PREPARE_STMT", config.PrepareStmt)
	config.DisableForeignKeyCheck = getEnvBool("DB_DISABLE_FOREIGN_KEY_CHECK", config.DisableForeignKeyCheck)
	config.IgnoreRelationshipsWhenMigrating = getEnvBool("DB_IGNORE_RELATIONSHIPS_WHEN_MIGRATING", config.IgnoreRelationshipsWhenMigrating)

	// Development settings
	config.DryRun = getEnvBool("DB_DRY_RUN", config.DryRun)
	config.QueryFields = getEnvBool("DB_QUERY_FIELDS", config.QueryFields)

	if inBatches := getEnvInt("DB_CREATE_IN_BATCHES", config.CreateInBatches); inBatches > 0 {
		config.CreateInBatches = inBatches
	}

	return config
}

// mergeWithEnvVars merges environment variables into existing config
// Environment variables take precedence over config file values
func mergeWithEnvVars(config *Config) *Config {
	// Database path
	if dbPath := os.Getenv("DB_PATH"); dbPath != "" {
		config.DatabasePath = dbPath
	}

	// Connection pool settings
	if maxIdle := getEnvInt("DB_MAX_IDLE_CONNS", config.MaxIdleConns); maxIdle > 0 {
		config.MaxIdleConns = maxIdle
	}

	if maxOpen := getEnvInt("DB_MAX_OPEN_CONNS", config.MaxOpenConns); maxOpen > 0 {
		config.MaxOpenConns = maxOpen
	}

	if lifetime := getEnvDuration("DB_CONN_MAX_LIFETIME", config.ConnMaxLifetime); lifetime > 0 {
		config.ConnMaxLifetime = lifetime
	}

	if idleTime := getEnvDuration("DB_CONN_MAX_IDLE_TIME", config.ConnMaxIdleTime); idleTime > 0 {
		config.ConnMaxIdleTime = idleTime
	}

	// Logging settings
	if envLogLevel := getLogLevel(); envLogLevel != logger.Silent {
		config.LogLevel = envLogLevel
	}
	if envDebug := os.Getenv("DB_DEBUG"); envDebug != "" {
		config.Debug = getEnvBool("DB_DEBUG", config.Debug)
	}

	if slowLog := getEnvDuration("DB_SLOW_QUERY_LOG", config.SlowQueryLog); slowLog > 0 {
		config.SlowQueryLog = slowLog
	}

	// Migration settings
	if envAutoMigrate := os.Getenv("DB_AUTO_MIGRATE"); envAutoMigrate != "" {
		config.AutoMigrate = getEnvBool("DB_AUTO_MIGRATE", config.AutoMigrate)
	}
	if envDropFirst := os.Getenv("DB_DROP_TABLE_FIRST"); envDropFirst != "" {
		config.DropTableFirst = getEnvBool("DB_DROP_TABLE_FIRST", config.DropTableFirst)
	}

	if batchSize := getEnvInt("DB_CREATE_BATCH_SIZE", config.CreateBatchSize); batchSize > 0 {
		config.CreateBatchSize = batchSize
	}

	// Performance settings
	if envPrepare := os.Getenv("DB_PREPARE_STMT"); envPrepare != "" {
		config.PrepareStmt = getEnvBool("DB_PREPARE_STMT", config.PrepareStmt)
	}
	if envFKCheck := os.Getenv("DB_DISABLE_FOREIGN_KEY_CHECK"); envFKCheck != "" {
		config.DisableForeignKeyCheck = getEnvBool("DB_DISABLE_FOREIGN_KEY_CHECK", config.DisableForeignKeyCheck)
	}
	if envIgnoreRel := os.Getenv("DB_IGNORE_RELATIONSHIPS_WHEN_MIGRATING"); envIgnoreRel != "" {
		config.IgnoreRelationshipsWhenMigrating = getEnvBool("DB_IGNORE_RELATIONSHIPS_WHEN_MIGRATING", config.IgnoreRelationshipsWhenMigrating)
	}

	// Development settings
	if envDryRun := os.Getenv("DB_DRY_RUN"); envDryRun != "" {
		config.DryRun = getEnvBool("DB_DRY_RUN", config.DryRun)
	}
	if envQueryFields := os.Getenv("DB_QUERY_FIELDS"); envQueryFields != "" {
		config.QueryFields = getEnvBool("DB_QUERY_FIELDS", config.QueryFields)
	}

	if inBatches := getEnvInt("DB_CREATE_IN_BATCHES", config.CreateInBatches); inBatches > 0 {
		config.CreateInBatches = inBatches
	}

	return config
}

// Validate checks if the configuration is valid
func (c *Config) Validate() error {
	if c.DatabasePath == "" {
		return fmt.Errorf("database path cannot be empty")
	}

	if c.MaxIdleConns < 0 {
		return fmt.Errorf("max idle connections cannot be negative")
	}

	if c.MaxOpenConns < 0 {
		return fmt.Errorf("max open connections cannot be negative")
	}

	if c.MaxIdleConns > c.MaxOpenConns && c.MaxOpenConns > 0 {
		return fmt.Errorf("max idle connections (%d) cannot be greater than max open connections (%d)",
			c.MaxIdleConns, c.MaxOpenConns)
	}

	if c.ConnMaxLifetime < 0 {
		return fmt.Errorf("connection max lifetime cannot be negative")
	}

	if c.ConnMaxIdleTime < 0 {
		return fmt.Errorf("connection max idle time cannot be negative")
	}

	if c.SlowQueryLog < 0 {
		return fmt.Errorf("slow query log threshold cannot be negative")
	}

	if c.CreateBatchSize <= 0 {
		return fmt.Errorf("create batch size must be greater than 0")
	}

	if c.CreateInBatches <= 0 {
		return fmt.Errorf("create in batches size must be greater than 0")
	}

	return nil
}

// String returns a string representation of the configuration
func (c *Config) String() string {
	return fmt.Sprintf(`Database Configuration:
  Database Path: %s
  Max Idle Connections: %d
  Max Open Connections: %d
  Connection Max Lifetime: %v
  Connection Max Idle Time: %v
  Log Level: %v
  Slow Query Log Threshold: %v
  Auto Migrate: %t
  Prepare Statements: %t
  Debug Mode: %t
  Dry Run: %t
  Create Batch Size: %d
  Create In Batches: %d`,
		c.DatabasePath,
		c.MaxIdleConns,
		c.MaxOpenConns,
		c.ConnMaxLifetime,
		c.ConnMaxIdleTime,
		c.LogLevel,
		c.SlowQueryLog,
		c.AutoMigrate,
		c.PrepareStmt,
		c.Debug,
		c.DryRun,
		c.CreateBatchSize,
		c.CreateInBatches,
	)
}

// IsProduction returns true if running in production environment
func (c *Config) IsProduction() bool {
	env := os.Getenv("ENV")
	return env == "production" || env == "prod"
}

// IsDevelopment returns true if running in development environment
func (c *Config) IsDevelopment() bool {
	env := os.Getenv("ENV")
	return env == "development" || env == "dev" || env == ""
}

// IsTest returns true if running in test environment
func (c *Config) IsTest() bool {
	env := os.Getenv("ENV")
	return env == "test" || env == "testing"
}

// Helper functions

func getEnvInt(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intValue, err := strconv.Atoi(value); err == nil {
			return intValue
		}
	}
	return defaultValue
}

func getEnvBool(key string, defaultValue bool) bool {
	if value := os.Getenv(key); value != "" {
		if boolValue, err := strconv.ParseBool(value); err == nil {
			return boolValue
		}
	}
	return defaultValue
}

func getEnvDuration(key string, defaultValue time.Duration) time.Duration {
	if value := os.Getenv(key); value != "" {
		if duration, err := time.ParseDuration(value); err == nil {
			return duration
		}
	}
	return defaultValue
}

func getLogLevel() logger.LogLevel {
	debug := getEnvBool("DB_DEBUG", false)
	env := os.Getenv("ENV")
	logLevel := os.Getenv("DB_LOG_LEVEL")

	switch {
	case debug:
		return logger.Info
	case env == "development" || env == "dev":
		return logger.Warn
	case env == "test" || env == "testing":
		return logger.Silent
	case logLevel == "silent":
		return logger.Silent
	case logLevel == "error":
		return logger.Error
	case logLevel == "warn":
		return logger.Warn
	case logLevel == "info":
		return logger.Info
	default:
		return logger.Silent
	}
}

// getLogLevelFromString converts string log level to GORM logger level
func getLogLevelFromString(level string) logger.LogLevel {
	switch strings.ToLower(level) {
	case "silent":
		return logger.Silent
	case "error":
		return logger.Error
	case "warn", "warning":
		return logger.Warn
	case "info":
		return logger.Info
	default:
		return logger.Silent
	}
}

// Environment variable documentation
/*
Available Environment Variables:

Database Settings:
  DB_PATH                    - Database file path (default: "fuel_stops.db")
  DB_AUTO_MIGRATE           - Enable automatic migrations (default: true)
  DB_DROP_TABLE_FIRST       - Drop tables before migration (default: false)

Connection Pool Settings:
  DB_MAX_IDLE_CONNS         - Maximum idle connections (default: 10)
  DB_MAX_OPEN_CONNS         - Maximum open connections (default: 100)
  DB_CONN_MAX_LIFETIME      - Connection maximum lifetime (default: "1h")
  DB_CONN_MAX_IDLE_TIME     - Connection maximum idle time (default: "30m")

Logging Settings:
  DB_DEBUG                  - Enable debug logging (default: false)
  DB_LOG_LEVEL             - Log level: silent, error, warn, info (default: silent)
  DB_SLOW_QUERY_LOG        - Slow query threshold (default: "200ms")

Performance Settings:
  DB_PREPARE_STMT           - Use prepared statements (default: true)
  DB_CREATE_BATCH_SIZE      - Batch size for migrations (default: 1000)
  DB_CREATE_IN_BATCHES      - Batch size for bulk operations (default: 100)
  DB_QUERY_FIELDS          - Select only required fields (default: false)

Development Settings:
  ENV                       - Environment: development, production, test
  DB_DRY_RUN               - Enable dry run mode (default: false)
  DB_DISABLE_FOREIGN_KEY_CHECK - Disable FK checks (default: false)
  DB_IGNORE_RELATIONSHIPS_WHEN_MIGRATING - Ignore relationships in migration (default: false)

Examples:
  export DB_DEBUG=true
  export DB_MAX_OPEN_CONNS=200
  export DB_CONN_MAX_LIFETIME=2h
  export DB_LOG_LEVEL=info
  export ENV=development
*/
