package slog

import (
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"wartank/pkg/components/kernel/slog/slog_file"
	"wartank/pkg/components/kernel/slog/slog_term"
	"wartank/pkg/components/safebool"
	"wartank/pkg/cons"
	"wartank/pkg/types"
)

/*
	Попытка сделать логирование в файл и консоль
*/

const (
	strSlog    = "ISlog"
	LevelDebug = iota
	LevelInfo
	LevelWarn
	LevelError
)

// Slog -- лог событий для записи
type Slog struct {
	kern   types.IKernel
	buf    strings.Builder
	block  sync.RWMutex
	lf     *slog_file.SlogFile
	lt     *slog_term.SlogTerm
	isWork *safebool.SafeBool
	level  int // Уровень логирования
}

// NewSlog -- возвращает объект логирования
func NewSlog(kern types.IKernel) (*Slog, error) {
	if kern == nil {
		return nil, fmt.Errorf("NewSlog(): IKernel is nil")
	}
	os.Setenv("BUILD", "test_BUILD")
	build := os.Getenv("BUILD")
	if build == "" {
		return nil, fmt.Errorf("NewSlog(): strBuild is empty")
	}
	sf := &Slog{
		kern:   kern,
		buf:    strings.Builder{},
		lt:     slog_term.NewSlogTerm(),
		isWork: safebool.NewSafeBool(),
	}
	lf, err := slog_file.NewSlogFile(kern, build)
	if err != nil {
		return nil, fmt.Errorf("NewSlog(): in create SlogFile, err=\n\t%w", err)
	}
	sf.lf = lf

	go sf.close()
	sf.isWork.Set()
	sf.kern.Wg().Add(strSlog)
	sf.Infof("NewSlog()\n")
	return sf, nil
}

// Debugf -- выводит в лог отладочную инфу
func (sf *Slog) Debugf(str string, lstVal ...interface{}) {
	sf.block.Lock()
	defer sf.block.Unlock()
	if !sf.isWork.Get() {
		return
	}
	sf.printf("DEBUG", str, lstVal...)
}

// Infof -- выводит в лог дежурную инфу
func (sf *Slog) Infof(str string, lstVal ...interface{}) {
	sf.block.Lock()
	defer sf.block.Unlock()
	if !sf.isWork.Get() {
		return
	}
	sf.printf("INFO", str, lstVal...)
}

// Warnf -- выводит в лог предупредительную инфу
func (sf *Slog) Warnf(str string, lstVal ...interface{}) {
	sf.block.Lock()
	defer sf.block.Unlock()
	if !sf.isWork.Get() {
		return
	}
	sf.printf("WARN", str, lstVal...)
}

// Errorf -- выводит в лог инфу об ошибках
func (sf *Slog) Errorf(str string, lstVal ...interface{}) {
	sf.block.Lock()
	defer sf.block.Unlock()
	if !sf.isWork.Get() {
		return
	}
	sf.printf("ERROR", str, lstVal...)
}

// Внутренний вызов без блокировки
func (sf *Slog) printf(pref, str string, lstVal ...interface{}) {
	style := ""
	switch pref {
	case "DEBUG": // Режим отладки
		style = "\033[2;37;40m"
	// case "PRINT": // Режим печати
	// 	style = "\033[0;34;40m"
	case "INFO": // Режим информирования
		style = "\033[0;37;40m"
	case "WARN": // Режим предупреждения
		style = "\033[1;31;40m"
	case "ERROR": // Режим ошибки
		style = "\033[1;37;41m"
	}
	strTime := time.Now().Local().Format("2006-01-02 15:04:05.000\t")
	sf.buf.WriteString(strTime + "\t")
	sf.buf.WriteString(pref + "\t")
	sf.buf.WriteString(cons.SelfName + "\n\t")
	sf.buf.WriteString(fmt.Sprintf(str, lstVal...))
	sf.lf.Write(sf.buf.String())
	sf.lt.Write(style + sf.buf.String() + "\033[00m")
	sf.buf.Reset()
}

// Ожидает закрытия приложения
func (sf *Slog) close() {
	<-sf.kern.Done()
	sf.block.Lock()
	if !sf.isWork.Get() {
		return
	}
	sf.isWork.Reset()
	sf.kern.Wg().Done(strSlog)
	sf.block.Unlock()
}
