package logger

import (
	"cargo-erp-backend/internal/config"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

type LoggerInterface interface {
	Init()
	Get(key string) *zap.Logger
}
type Logger struct {
	Config config.ConfigInterface
	Logger map[string]*zap.Logger
}

func NewLogger(config config.ConfigInterface) LoggerInterface {
	return &Logger{
		Config: config,
	}
}
func (l *Logger) Init() {
	log.Println("initial database logger")
	l.Logger = make(map[string]*zap.Logger)
	l.Logger["DB"] = nil
	l.Logger["APP"] = nil
	if v, ok := l.Config.Get("database.logger").(map[string]interface{}); ok {
		if val, ok := v["filename"]; ok {
			if filename, ok := val.(string); ok {
				file_location, err := filepath.Abs(filename)
				MaxSize := 10
				MaxBackups := 5
				MaxAge := 30
				Compress := true
				if err == nil {
					if s, ok := v["MaxSize"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxSize = size
						}
					}
					if s, ok := v["max_backups"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxBackups = size
						}
					}
					if s, ok := v["max_age"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxAge = size
						}
					}
					if s, ok := v["compress"]; ok {
						if sv, ok := s.(bool); ok {
							Compress = sv
						}
					}
				}
				l.Logger["DB"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress)
			}
		}
	}
	if v, ok := l.Config.Get("logger").(map[string]interface{}); ok {
		if val, ok := v["filename"]; ok {
			if filename, ok := val.(string); ok {
				file_location, err := filepath.Abs(filename)
				MaxSize := 10
				MaxBackups := 5
				MaxAge := 30
				Compress := true
				if err == nil {
					if s, ok := v["MaxSize"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxSize = size
						}
					}
					if s, ok := v["max_backups"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxBackups = size
						}
					}
					if s, ok := v["max_age"]; ok {
						size, err := strconv.Atoi(fmt.Sprintf("%v", s))
						if err != nil {
							MaxAge = size
						}
					}
					if s, ok := v["compress"]; ok {
						if sv, ok := s.(bool); ok {
							Compress = sv
						}
					}
				}
				l.Logger["APP"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress)
				if l.Logger["DB"] == nil {
					l.Logger["DB"] = l.Logger["APP"]
				}
			}
		}
	}
}
func (l *Logger) Get(key string) *zap.Logger {
	if v, ok := l.Logger[key]; ok {
		return v
	}
	return nil
}
func (l *Logger) NewLog(filename string, max_size int, max_backup int, max_age int, compress bool) *zap.Logger {
	lumberjackLogger := &lumberjack.Logger{
		Filename:   filename,   // Lokasi file
		MaxSize:    max_size,   // Megabytes sebelum rotasi
		MaxBackups: max_backup, // Jumlah file cadangan
		MaxAge:     max_age,    // Retention dalam HARI
		Compress:   compress,   // Otomatis kompres ke file .gz
	}
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Format waktu: 2026-08-13T...
	jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
	consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
	// Hanya meloloskan level DEBUG dan INFO
	isInfoOrDebug := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl == zap.DebugLevel || lvl == zap.InfoLevel
	})

	// Hanya meloloskan level WARN, ERROR, PANIC, FATAL
	isErrorOrHigher := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zap.WarnLevel
	})
	core := zapcore.NewTee(
		zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isInfoOrDebug),
		// Masuk ke file error (Ganti os.Stderr dengan errorFileLogger Anda)
		zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isErrorOrHigher),

		// CONSOLE LOGS (Terminal): Biasanya ingin melihat semua level dari DEBUG sampai FATAL
		zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zap.DebugLevel),
	)
	logger := zap.New(core, zap.AddCaller())
	defer logger.Sync()
	return logger
}
