package logger

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	INFO
	WARN
	ERROR
)

var levelStrings = map[Level]string{
	DEBUG: "DEBUG",
	INFO:  "INFO",
	WARN:  "WARN",
	ERROR: "ERROR",
}

var stringToLevel = map[string]Level{
	"debug": DEBUG,
	"info":  INFO,
	"warn":  WARN,
	"error": ERROR,
}

type Logger struct {
	logger     *log.Logger
	level      Level
	format     string
	mu         sync.RWMutex
	jsonWriter *jsonLogWriter
}

type jsonLogWriter struct {
	out io.Writer
}

type Config struct {
	Level  string
	Format string
	File   string
}

var defaultLogger *Logger

func init() {
	defaultLogger = New(Config{
		Level:  "info",
		Format: "text",
	})
}

func New(config Config) *Logger {
	var output io.Writer = os.Stdout

	if config.File != "" {
		file, err := os.OpenFile(config.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err == nil {
			output = io.MultiWriter(file, os.Stdout)
		}
	}

	level := stringToLevel[strings.ToLower(config.Level)]
	format := strings.ToLower(config.Format)

	l := &Logger{
		level:  level,
		format: format,
	}

	if format == "json" {
		l.jsonWriter = &jsonLogWriter{out: output}
		l.logger = log.New(l.jsonWriter, "", 0)
	} else {
		l.logger = log.New(output, "", log.LstdFlags|log.Lshortfile)
	}

	return l
}

func (w *jsonLogWriter) Write(p []byte) (n int, err error) {
	entry := map[string]interface{}{
		"timestamp": time.Now().Format(time.RFC3339),
		"message":   strings.TrimSpace(string(p)),
	}

	jsonData, err := json.Marshal(entry)
	if err != nil {
		return 0, err
	}

	return w.out.Write(append(jsonData, '\n'))
}

func (l *Logger) log(level Level, format string, v ...interface{}) {
	l.mu.RLock()
	defer l.mu.RUnlock()

	if level < l.level {
		return
	}

	msg := fmt.Sprintf(format, v...)
	if l.format == "json" {
		entry := map[string]interface{}{
			"timestamp": time.Now().Format(time.RFC3339),
			"level":     levelStrings[level],
			"message":   msg,
		}
		jsonData, _ := json.Marshal(entry)
		l.logger.Print(string(jsonData))
	} else {
		l.logger.Printf("[%s] %s", levelStrings[level], msg)
	}
}

func (l *Logger) Debug(format string, v ...interface{}) {
	l.log(DEBUG, format, v...)
}

func (l *Logger) Info(format string, v ...interface{}) {
	l.log(INFO, format, v...)
}

func (l *Logger) Warn(format string, v ...interface{}) {
	l.log(WARN, format, v...)
}

func (l *Logger) Error(format string, v ...interface{}) {
	l.log(ERROR, format, v...)
}

// Global logger functions
func Debug(format string, v ...interface{}) {
	defaultLogger.Debug(format, v...)
}

func Info(format string, v ...interface{}) {
	defaultLogger.Info(format, v...)
}

func Warn(format string, v ...interface{}) {
	defaultLogger.Warn(format, v...)
}

func Error(format string, v ...interface{}) {
	defaultLogger.Error(format, v...)
}

func Configure(config Config) {
	defaultLogger = New(config)
}
