package config

import (
	"fmt"
	"log/slog"
	"path/filepath"
	"strings"
	"time"
)

const (
	LogFormatJSON = "json"
	LogFormatText = "text"

	LogOutputStdout = "stdout"
	LogOutputFile   = "file"
	LogOutputBoth   = "both"

	LogCompressionGzip = "gzip"
	LogCompressionNone = "none"
)

// LogConfig controls the process-wide operational logging pipeline.
type LogConfig struct {
	Level         slog.Level
	Format        string
	Output        string
	FilePath      string
	MaxSizeMB     int
	MaxDays       int
	MaxBackups    int
	Compression   string
	BufferSizeKB  int
	FlushInterval time.Duration
}

func DefaultLogConfig() LogConfig {
	return LogConfig{
		Level:         slog.LevelInfo,
		Format:        LogFormatJSON,
		Output:        LogOutputStdout,
		MaxSizeMB:     100,
		MaxDays:       7,
		MaxBackups:    10,
		Compression:   LogCompressionGzip,
		BufferSizeKB:  256,
		FlushInterval: time.Second,
	}
}

// Normalized supplies defaults only for the zero value used by programmatic
// Config construction. Environment parsing always returns a fully populated
// value so explicit retention zeroes remain distinguishable.
func (cfg LogConfig) Normalized() LogConfig {
	if cfg == (LogConfig{}) {
		return DefaultLogConfig()
	}
	return cfg
}

func (cfg LogConfig) Validate() error {
	cfg = cfg.Normalized()
	switch cfg.Level {
	case slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError:
	default:
		return fmt.Errorf("AGENTTEAM_LOG_LEVEL must be one of debug, info, warn, or error")
	}
	switch cfg.Format {
	case LogFormatJSON, LogFormatText:
	default:
		return fmt.Errorf("AGENTTEAM_LOG_FORMAT must be one of json or text")
	}
	switch cfg.Output {
	case LogOutputStdout:
		if cfg.FilePath != "" {
			return fmt.Errorf("AGENTTEAM_LOG_FILE must be empty when AGENTTEAM_LOG_OUTPUT=stdout")
		}
	case LogOutputFile, LogOutputBoth:
		if cfg.FilePath == "" {
			return fmt.Errorf("AGENTTEAM_LOG_FILE is required when AGENTTEAM_LOG_OUTPUT=%s", cfg.Output)
		}
		if !filepath.IsAbs(cfg.FilePath) {
			return fmt.Errorf("AGENTTEAM_LOG_FILE must be an absolute path")
		}
		if cfg.MaxDays == 0 && cfg.MaxBackups == 0 {
			return fmt.Errorf("AGENTTEAM_LOG_FILE_MAX_DAYS or AGENTTEAM_LOG_FILE_MAX_BACKUPS must be positive when file logging is enabled")
		}
	default:
		return fmt.Errorf("AGENTTEAM_LOG_OUTPUT must be one of stdout, file, or both")
	}
	if cfg.MaxSizeMB < 1 || cfg.MaxSizeMB > 4096 {
		return fmt.Errorf("AGENTTEAM_LOG_FILE_MAX_SIZE_MB must be between 1 and 4096")
	}
	if cfg.MaxDays < 0 {
		return fmt.Errorf("AGENTTEAM_LOG_FILE_MAX_DAYS must be non-negative")
	}
	if cfg.MaxBackups < 0 {
		return fmt.Errorf("AGENTTEAM_LOG_FILE_MAX_BACKUPS must be non-negative")
	}
	switch cfg.Compression {
	case LogCompressionGzip, LogCompressionNone:
	default:
		return fmt.Errorf("AGENTTEAM_LOG_FILE_COMPRESSION must be one of gzip or none")
	}
	if cfg.BufferSizeKB < 0 {
		return fmt.Errorf("AGENTTEAM_LOG_FILE_BUFFER_SIZE_KB must be non-negative")
	}
	if cfg.FlushInterval <= 0 {
		return fmt.Errorf("AGENTTEAM_LOG_FILE_FLUSH_INTERVAL must be positive")
	}
	return nil
}

func loadLogConfigFromEnv() (LogConfig, error) {
	level, err := parseLogLevel(envOrDefault("AGENTTEAM_LOG_LEVEL", "info"))
	if err != nil {
		return LogConfig{}, err
	}
	maxSizeMB, err := intOrDefault("AGENTTEAM_LOG_FILE_MAX_SIZE_MB", 100)
	if err != nil {
		return LogConfig{}, err
	}
	maxDays, err := intOrDefault("AGENTTEAM_LOG_FILE_MAX_DAYS", 7)
	if err != nil {
		return LogConfig{}, err
	}
	maxBackups, err := intOrDefault("AGENTTEAM_LOG_FILE_MAX_BACKUPS", 10)
	if err != nil {
		return LogConfig{}, err
	}
	bufferSizeKB, err := intOrDefault("AGENTTEAM_LOG_FILE_BUFFER_SIZE_KB", 256)
	if err != nil {
		return LogConfig{}, err
	}
	flushInterval, err := durationOrDefault("AGENTTEAM_LOG_FILE_FLUSH_INTERVAL", time.Second)
	if err != nil {
		return LogConfig{}, err
	}
	cfg := LogConfig{
		Level:         level,
		Format:        strings.ToLower(strings.TrimSpace(envOrDefault("AGENTTEAM_LOG_FORMAT", LogFormatJSON))),
		Output:        strings.ToLower(strings.TrimSpace(envOrDefault("AGENTTEAM_LOG_OUTPUT", LogOutputStdout))),
		FilePath:      strings.TrimSpace(envOrDefaultAllowExplicitEmpty("AGENTTEAM_LOG_FILE", "")),
		MaxSizeMB:     maxSizeMB,
		MaxDays:       maxDays,
		MaxBackups:    maxBackups,
		Compression:   strings.ToLower(strings.TrimSpace(envOrDefault("AGENTTEAM_LOG_FILE_COMPRESSION", LogCompressionGzip))),
		BufferSizeKB:  bufferSizeKB,
		FlushInterval: flushInterval,
	}
	if err := cfg.Validate(); err != nil {
		return LogConfig{}, err
	}
	return cfg, nil
}

func parseLogLevel(value string) (slog.Level, error) {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "debug":
		return slog.LevelDebug, nil
	case "info":
		return slog.LevelInfo, nil
	case "warn":
		return slog.LevelWarn, nil
	case "error":
		return slog.LevelError, nil
	default:
		return 0, fmt.Errorf("AGENTTEAM_LOG_LEVEL must be one of debug, info, warn, or error")
	}
}
