// package log_buf -- потокобезопасный буфер лога
package log_buf

import (
	"fmt"
	"sync"

	"gitp78su.ipnodns.ru/svi/kern/v3/kc/helpers"
	"gitp78su.ipnodns.ru/svi/kern/v3/kc/log_buf/log_msg"
	"gitp78su.ipnodns.ru/svi/kern/v3/kc/safe_bool"
	. "gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
)

// logBuf -- потокобезопасный буфер лога
type logBuf struct {
	sync.RWMutex
	isTerm ISafeBool // Признак вывода в стандартный поток
	prefix string    // Префикс для сообщений
	lst    []ILogMsg
	lstErr []ILogMsg
}

// OptionLogBuf -- опция для конфигурирования ILogBuf
type OptionLogBuf func(logBuf *logBuf)

// OptIsTerm -- устанавливает признак вывода в терминал
func OptIsTerm(isTerm bool) OptionLogBuf {
	return func(sf *logBuf) {
		if isTerm {
			sf.IsTerm().Set()
		}
	}
}

// OptPrefix -- устанавливает префикс в сообщениях
func OptPrefix(prefix string) OptionLogBuf {
	return func(sf *logBuf) {
		sf.prefix = prefix + "."
	}
}

// NewLogBuf -- возвращает новый потокобезопасный буфер лога
func NewLogBuf(opts ...OptionLogBuf) ILogBuf {
	sf := &logBuf{
		isTerm: safe_bool.NewSafeBool(),
		lst:    []ILogMsg{},
		lstErr: []ILogMsg{},
	}

	for _, opt := range opts {
		opt(sf)
	}
	return sf
}

// IsTerm -- возвращает признак логирования
func (sf *logBuf) IsTerm() ISafeBool {
	return sf.isTerm
}

// GetErr -- возвращает сообщение ошибки по номеру
func (sf *logBuf) GetErr(num int) ILogMsg {
	sf.RLock()
	defer sf.RUnlock()
	if len(sf.lstErr) == 0 {
		return log_msg.NewLogMsg(log_msg.DEBUG, sf.prefix+"not error msg")
	}
	if num >= len(sf.lstErr) {
		return sf.lstErr[len(sf.lstErr)-1]
	}
	if num <= 0 {
		return sf.lstErr[0]
	}
	return sf.lstErr[num]
}

// Get -- возвращает сообщение по номеру
func (sf *logBuf) Get(num int) ILogMsg {
	sf.RLock()
	defer sf.RUnlock()
	if len(sf.lst) == 0 {
		return log_msg.NewLogMsg(log_msg.DEBUG, sf.prefix+"*no msg*")
	}
	if num >= len(sf.lst) {
		return log_msg.NewLogMsg(log_msg.DEBUG, sf.prefix+"*no msg*")
	}
	if num <= 0 {
		return log_msg.NewLogMsg(log_msg.DEBUG, sf.prefix+"*no msg*")
	}
	return sf.lst[num]
}

type tMsg struct {
	text string
	args []any
}

// Debug -- сообщение отладки
func (sf *logBuf) Debug(fMsg string, args ...any) {
	sf.Lock()
	defer sf.Unlock()
	msg := tMsg{
		text: fMsg,
		args: args,
	}
	strMsg := fmt.Sprintf(msg.text, msg.args...)
	_msg := log_msg.NewLogMsg(log_msg.DEBUG, sf.prefix+strMsg)
	sf.lst = append(sf.lst, _msg)
	sf.checkLen()
	sf.printTerm(_msg)
}

// Info -- информационные сообщения
func (sf *logBuf) Info(fMsg string, args ...any) {
	sf.Lock()
	defer sf.Unlock()
	msg := tMsg{
		text: fMsg,
		args: args,
	}
	strMsg := fmt.Sprintf(msg.text, msg.args...)
	_msg := log_msg.NewLogMsg(log_msg.INFO, sf.prefix+strMsg)
	sf.lst = append(sf.lst, _msg)
	sf.checkLen()
	sf.printTerm(_msg)
}

// Warn -- предупреждающие сообщения
func (sf *logBuf) Warn(fMsg string, args ...any) {
	sf.Lock()
	defer sf.Unlock()
	msg := tMsg{
		text: fMsg,
		args: args,
	}
	strMsg := fmt.Sprintf(msg.text, msg.args...)
	_msg := log_msg.NewLogMsg(log_msg.WARN, sf.prefix+strMsg)
	sf.lst = append(sf.lst, _msg)
	sf.checkLen()
	sf.printTerm(_msg)
}

// Err -- сообщения об ошибках
func (sf *logBuf) Err(fMsg string, args ...any) {
	sf.Lock()
	defer sf.Unlock()
	msg := tMsg{
		text: fMsg,
		args: args,
	}
	strMsg := fmt.Sprintf(msg.text, msg.args...)
	_msg := log_msg.NewLogMsg(log_msg.ERROR, sf.prefix+strMsg)
	sf.lst = append(sf.lst, _msg)
	sf.lstErr = append(sf.lstErr, _msg)
	sf.checkLen()
	sf.checkLenErr()
	sf.printTerm(_msg)
}

// Size -- возвращает размер буфера
func (sf *logBuf) Size() int {
	sf.RLock()
	defer sf.RUnlock()
	return len(sf.lst)
}

// Проверяет длину общую лога
func (sf *logBuf) checkLen() {
	for len(sf.lst) > 100 {
		sf.lst = sf.lst[1:]
	}
}

// Проверяет длину лога ошибок
func (sf *logBuf) checkLenErr() {
	for len(sf.lstErr) > 100 {
		sf.lstErr = sf.lstErr[1:]
	}
}

// Печатает сообщение в терминал, если разрешено
func (sf *logBuf) printTerm(msg ILogMsg) {
	if !sf.isTerm.Get() {
		return
	}
	level := msg.Level()
	if helpers.IsStageProd {
		switch level {
		case "ERRO", "WARN":
			fmt.Printf("%v   %v\n", level, msg.String())
		default:
			return
		}
	}
	fmt.Printf("%v\n", msg.String())
}
