package util

import (
	"context"
	"errors"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"gorm.io/gorm"
	gormLogger "gorm.io/gorm/logger"
	"tailscale.com/types/logger"
)

func LogErr(err error, msg string) {
	log.Error().Caller().Err(err).Msg(msg)
}

func TSLogfWrapper() logger.Logf {
	return func(format string, args ...any) {
		log.Debug().Caller().Msgf(format, args...)
	}
}

type DBLogWrapper struct {
	Logger                *zerolog.Logger
	Level                 zerolog.Level
	Event                 *zerolog.Event
	SlowThreshold         time.Duration
	SkipErrRecordNotFound bool
	ParameterizedQueries  bool
}

func NewDBLogWrapper(origin *zerolog.Logger, slowThreshold time.Duration, skipErrRecordNotFound bool, parameterizedQueries bool) *DBLogWrapper {
	l := &DBLogWrapper{
		Logger:                origin,
		Level:                 origin.GetLevel(),
		SlowThreshold:         slowThreshold,
		SkipErrRecordNotFound: skipErrRecordNotFound,
		ParameterizedQueries:  parameterizedQueries,
	}

	return l
}

type DBLogWrapperOption func(*DBLogWrapper)

func (l *DBLogWrapper) LogMode(gormLogger.LogLevel) gormLogger.Interface {
	return l
}

func (l *DBLogWrapper) Info(ctx context.Context, msg string, data ...any) {
	l.Logger.Info().Msgf(msg, data...)
}

func (l *DBLogWrapper) Warn(ctx context.Context, msg string, data ...any) {
	l.Logger.Warn().Msgf(msg, data...)
}

func (l *DBLogWrapper) Error(ctx context.Context, msg string, data ...any) {
	l.Logger.Error().Msgf(msg, data...)
}

func (l *DBLogWrapper) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	elapsed := time.Since(begin)
	sql, rowsAffected := fc()
	fields := map[string]any{
		"duration":     elapsed,
		"sql":          sql,
		"rowsAffected": rowsAffected,
	}

	if err != nil && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.SkipErrRecordNotFound) {
		l.Logger.Error().Err(err).Fields(fields).Msgf("")
		return
	}

	if l.SlowThreshold != 0 && elapsed > l.SlowThreshold {
		l.Logger.Warn().Fields(fields).Msgf("")
		return
	}

	l.Logger.Debug().Fields(fields).Msgf("")
}

func (l *DBLogWrapper) ParamsFilter(ctx context.Context, sql string, params ...any) (string, []any) {
	if l.ParameterizedQueries {
		return sql, nil
	}

	return sql, params
}
