package logger

import (
	"context"
	"errors"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
)

// GormZapLogger adalah custom logger GORM yang menggunakan Zap
type GormZapLogger struct {
	ZapLogger     *zap.Logger
	LogLevel      gormlogger.LogLevel
	SlowThreshold time.Duration
}

// NewGormZapLogger menginisialisasi logger baru
func NewGormZapLogger(zapLog *zap.Logger, slowThreshold time.Duration) *GormZapLogger {
	return &GormZapLogger{
		ZapLogger:     zapLog,
		LogLevel:      gormlogger.Info, // Atur default ke Info, bisa disesuaikan
		SlowThreshold: slowThreshold,   // Waktu batas query dianggap lambat (misal 200ms)
	}
}

// LogMode mengubah level log GORM
func (l *GormZapLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
	newLogger := *l
	newLogger.LogLevel = level
	return &newLogger
}

// Info mencetak log informasi biasa
func (l *GormZapLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= gormlogger.Info {
		l.ZapLogger.Sugar().Infof(msg, data...)
	}
}

// Warn mencetak log peringatan
func (l *GormZapLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= gormlogger.Warn {
		l.ZapLogger.Sugar().Warnf(msg, data...)
	}
}

// Error mencetak log kesalahan
func (l *GormZapLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= gormlogger.Error {
		l.ZapLogger.Sugar().Errorf(msg, data...)
	}
}

// Trace mencetak log internal query SQL dari GORM
func (l *GormZapLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	if l.LogLevel <= gormlogger.Silent {
		return
	}

	elapsed := time.Since(begin)
	sql, rows := fc()

	// Ambil lokasi file baris kode Go yang mengeksekusi query ini
	line := utils.FileWithLineNum()

	// Siapkan fields bawaan Zap agar log terstruktur dengan baik
	fields := []zap.Field{
		zap.String("line", line),
		zap.Duration("elapsed", elapsed),
		zap.Int64("rows", rows),
		zap.String("sql", sql),
	}

	switch {
	// Kasus 1: Terjadi Error dan Error tersebut BUKAN Record NotFound
	case err != nil && l.LogLevel >= gormlogger.Error && !errors.Is(err, gorm.ErrRecordNotFound):
		fields = append(fields, zap.Error(err))
		l.ZapLogger.Error("[GORM] query error", fields...)

	// Kasus 2: Query berjalan lambat melewati batas SlowThreshold
	case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormlogger.Warn:
		l.ZapLogger.Warn("[GORM] slow query warning", fields...)

	// Kasus 3: Log info query normal
	case l.LogLevel >= gormlogger.Info:
		l.ZapLogger.Info("[GORM] query execution", fields...)
	}
}
