package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"time"
)

// Level represents log level
type Level int

const (
	DebugLevel Level = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel
)

// String returns string representation of log level
func (l Level) String() string {
	switch l {
	case DebugLevel:
		return "DEBUG"
	case InfoLevel:
		return "INFO"
	case WarnLevel:
		return "WARN"
	case ErrorLevel:
		return "ERROR"
	case FatalLevel:
		return "FATAL"
	default:
		return "UNKNOWN"
	}
}

// Format represents log format
type Format int

const (
	FormatText Format = iota
	FormatJSON
	FormatPretty
)

// Config holds logger configuration
type Config struct {
	Level  Level
	Format Format
}

// Logger is the main logger type
type Logger struct {
	config Config
}

// Entry represents a log entry
type Entry struct {
	Time      string `json:"time"`
	Level     string `json:"level"`
	Message   string `json:"message"`
	Caller    string `json:"caller,omitempty"`
	Fields    map[string]interface{} `json:"fields,omitempty"`
}

// New creates a new logger
func New(cfg Config) *Logger {
	return &Logger{
		config: cfg,
	}
}

// log outputs a log message
func (l *Logger) log(level Level, msg string, fields map[string]interface{}) {
	if level < l.config.Level {
		return
	}

	entry := Entry{
		Time:    time.Now().Format(time.RFC3339),
		Level:   level.String(),
		Message: msg,
		Fields:  fields,
	}

	switch l.config.Format {
	case FormatJSON:
		data, _ := json.Marshal(entry)
		fmt.Fprintln(os.Stdout, string(data))
	case FormatPretty:
		color := getColor(level)
		reset := "\033[0m"
		fmt.Fprintf(os.Stdout, "%s[%s]%s %s%s%s %s\n",
			color, entry.Time, reset,
			color, entry.Level, reset,
			msg)
	default:
		fmt.Fprintf(os.Stdout, "[%s] %s: %s\n", entry.Time, entry.Level, msg)
	}
}

// getColor returns ANSI color code for log level
func getColor(level Level) string {
	switch level {
	case DebugLevel:
		return "\033[36m" // Cyan
	case InfoLevel:
		return "\033[92m" // Green
	case WarnLevel:
		return "\033[93m" // Yellow
	case ErrorLevel:
		return "\033[91m" // Red
	case FatalLevel:
		return "\033[1;31m" // Bold Red
	default:
		return "\033[0m"
	}
}

// Debug logs a debug message
func (l *Logger) Debug(msg string, fields ...map[string]interface{}) {
	f := mergeFields(fields...)
	l.log(DebugLevel, msg, f)
}

// Info logs an info message
func (l *Logger) Info(msg string, fields ...map[string]interface{}) {
	f := mergeFields(fields...)
	l.log(InfoLevel, msg, f)
}

// Warn logs a warning message
func (l *Logger) Warn(msg string, fields ...map[string]interface{}) {
	f := mergeFields(fields...)
	l.log(WarnLevel, msg, f)
}

// Error logs an error message
func (l *Logger) Error(msg string, fields ...map[string]interface{}) {
	f := mergeFields(fields...)
	l.log(ErrorLevel, msg, f)
}

// Fatal logs a fatal message and exits
func (l *Logger) Fatal(msg string, fields ...map[string]interface{}) {
	f := mergeFields(fields...)
	l.log(FatalLevel, msg, f)
	os.Exit(1)
}

// mergeFields merges field maps
func mergeFields(fields ...map[string]interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	for _, f := range fields {
		for k, v := range f {
			result[k] = v
		}
	}
	return result
}

// WithFields returns a new logger with default fields
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
	return &Logger{
		config: l.config,
	}
}

// SetLevel sets the log level
func (l *Logger) SetLevel(level Level) {
	l.config.Level = level
}

// SetFormat sets the log format
func (l *Logger) SetFormat(format Format) {
	l.config.Format = format
}
