// Package logger provides structured logging functionality for AutoSSO.
// It wraps zerolog to provide a consistent logging interface with support for
// configurable log levels, context fields, and sensitive data filtering.
package logger

import (
	"io"
	"os"
	"strings"
	"time"

	"github.com/rs/zerolog"
)

// Level represents the logging level.
type Level string

const (
	// DebugLevel is the most verbose logging level.
	DebugLevel Level = "debug"
	// InfoLevel is the default logging level.
	InfoLevel Level = "info"
	// WarnLevel is for warning messages.
	WarnLevel Level = "warn"
	// ErrorLevel is for error messages.
	ErrorLevel Level = "error"
)

// sensitiveFields contains field names that should be filtered from logs.
var sensitiveFields = map[string]bool{
	"password":          true,
	"secret":            true,
	"token":             true,
	"access_key":        true,
	"access_key_id":     true,
	"access_key_secret": true,
	"client_secret":     true,
	"api_key":           true,
	"credential":        true,
	"credentials":       true,
	"app_secret":        true,
}

// Logger wraps zerolog.Logger to provide structured logging with context support.
type Logger struct {
	zl     zerolog.Logger
	module string
}

// Config holds the configuration for creating a new Logger.
type Config struct {
	// Level is the minimum logging level (debug, info, warn, error).
	Level Level
	// Output is the writer to output logs to. Defaults to os.Stdout.
	Output io.Writer
	// PrettyPrint enables human-readable output format.
	PrettyPrint bool
	// Module is the default module name for this logger instance.
	Module string
}

// DefaultConfig returns a Config with sensible defaults.
func DefaultConfig() Config {
	return Config{
		Level:       InfoLevel,
		Output:      os.Stdout,
		PrettyPrint: false,
		Module:      "",
	}
}

// NewLogger creates a new Logger instance with the given configuration.
func NewLogger(cfg Config) *Logger {
	// Set default output if not specified
	output := cfg.Output
	if output == nil {
		output = os.Stdout
	}

	// Configure zerolog
	zerolog.TimeFieldFormat = time.RFC3339

	var zl zerolog.Logger
	if cfg.PrettyPrint {
		zl = zerolog.New(zerolog.ConsoleWriter{Out: output, TimeFormat: time.RFC3339}).
			With().
			Timestamp().
			Logger()
	} else {
		zl = zerolog.New(output).
			With().
			Timestamp().
			Logger()
	}

	// Set log level
	zl = zl.Level(parseLevel(cfg.Level))

	return &Logger{
		zl:     zl,
		module: cfg.Module,
	}
}

// parseLevel converts a Level string to zerolog.Level.
func parseLevel(level Level) zerolog.Level {
	switch strings.ToLower(string(level)) {
	case "debug":
		return zerolog.DebugLevel
	case "info":
		return zerolog.InfoLevel
	case "warn", "warning":
		return zerolog.WarnLevel
	case "error":
		return zerolog.ErrorLevel
	default:
		return zerolog.InfoLevel
	}
}

// ParseLevel converts a string to a Level type.
func ParseLevel(s string) Level {
	switch strings.ToLower(s) {
	case "debug":
		return DebugLevel
	case "info":
		return InfoLevel
	case "warn", "warning":
		return WarnLevel
	case "error":
		return ErrorLevel
	default:
		return InfoLevel
	}
}

// WithModule creates a new Logger with the specified module name.
func (l *Logger) WithModule(module string) *Logger {
	return &Logger{
		zl:     l.zl,
		module: module,
	}
}

// WithFields creates a new Logger with additional context fields.
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
	ctx := l.zl.With()
	for k, v := range fields {
		ctx = ctx.Interface(filterSensitive(k, v))
	}
	return &Logger{
		zl:     ctx.Logger(),
		module: l.module,
	}
}

// filterSensitive filters sensitive field values.
func filterSensitive(key string, value interface{}) (string, interface{}) {
	lowerKey := strings.ToLower(key)
	for sensitiveKey := range sensitiveFields {
		if strings.Contains(lowerKey, sensitiveKey) {
			return key, "[REDACTED]"
		}
	}
	return key, value
}

// Debug logs a debug message with optional context fields.
func (l *Logger) Debug(msg string, fields ...Field) {
	l.log(zerolog.DebugLevel, msg, fields...)
}

// Info logs an info message with optional context fields.
func (l *Logger) Info(msg string, fields ...Field) {
	l.log(zerolog.InfoLevel, msg, fields...)
}

// Warn logs a warning message with optional context fields.
func (l *Logger) Warn(msg string, fields ...Field) {
	l.log(zerolog.WarnLevel, msg, fields...)
}

// Error logs an error message with optional context fields.
func (l *Logger) Error(msg string, fields ...Field) {
	l.log(zerolog.ErrorLevel, msg, fields...)
}

// log is the internal logging method that handles all log levels.
func (l *Logger) log(level zerolog.Level, msg string, fields ...Field) {
	event := l.zl.WithLevel(level)
	if event == nil {
		return
	}

	// Add module if set
	if l.module != "" {
		event = event.Str("module", l.module)
	}

	// Add all fields with sensitive data filtering
	for _, f := range fields {
		event = f.apply(event)
	}

	event.Msg(msg)
}

// Field represents a log field that can be added to a log entry.
type Field struct {
	key   string
	value interface{}
}

// apply adds the field to the zerolog event with sensitive data filtering.
func (f Field) apply(event *zerolog.Event) *zerolog.Event {
	key, value := filterSensitive(f.key, f.value)
	switch v := value.(type) {
	case string:
		return event.Str(key, v)
	case int:
		return event.Int(key, v)
	case int64:
		return event.Int64(key, v)
	case float64:
		return event.Float64(key, v)
	case bool:
		return event.Bool(key, v)
	case error:
		return event.AnErr(key, v)
	case time.Time:
		return event.Time(key, v)
	case time.Duration:
		return event.Dur(key, v)
	default:
		return event.Interface(key, v)
	}
}

// String creates a string field.
func String(key, value string) Field {
	return Field{key: key, value: value}
}

// Int creates an integer field.
func Int(key string, value int) Field {
	return Field{key: key, value: value}
}

// Int64 creates an int64 field.
func Int64(key string, value int64) Field {
	return Field{key: key, value: value}
}

// Float64 creates a float64 field.
func Float64(key string, value float64) Field {
	return Field{key: key, value: value}
}

// Bool creates a boolean field.
func Bool(key string, value bool) Field {
	return Field{key: key, value: value}
}

// Err creates an error field.
func Err(err error) Field {
	return Field{key: "error", value: err}
}

// Time creates a time field.
func Time(key string, value time.Time) Field {
	return Field{key: key, value: value}
}

// Duration creates a duration field.
func Duration(key string, value time.Duration) Field {
	return Field{key: key, value: value}
}

// Any creates a field with any value type.
func Any(key string, value interface{}) Field {
	return Field{key: key, value: value}
}

// Operation creates an operation field for tracking the current operation.
func Operation(op string) Field {
	return Field{key: "operation", value: op}
}

// AccountID creates an account ID field.
func AccountID(id string) Field {
	return Field{key: "account_id", value: id}
}

// UserEmail creates a user email field.
func UserEmail(email string) Field {
	return Field{key: "user_email", value: email}
}

// CloudType creates a cloud type field.
func CloudType(cloudType string) Field {
	return Field{key: "cloud_type", value: cloudType}
}

// InstanceID creates an instance ID field.
func InstanceID(id string) Field {
	return Field{key: "instance_id", value: id}
}
