package slog

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"
	"wartank/pkg/components/kernel/slog/slog_file"
	"wartank/pkg/components/kernel/slog/slog_term"
	"wartank/pkg/components/safe_bool"
	"wartank/pkg/cons"
	"wartank/pkg/types"
)

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

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

// Слог -- лог событий для записи
type Слог struct {
	kern   types.ИЯдро
	buf    strings.Builder
	block  sync.RWMutex
	lf     *slog_file.СлогФайл
	lt     *slog_term.SlogTerm
	isWork *safe_bool.БезопБул
	level  int // Уровень логирования
}

// НовСлог -- возвращает объект логирования
func НовСлог(kern types.ИЯдро) (*Слог, error) {
	if kern == nil {
		return nil, fmt.Errorf("NewSlog(): IKernel is nil")
	}
	сам := &Слог{
		kern:   kern,
		buf:    strings.Builder{},
		lt:     slog_term.NewSlogTerm(),
		isWork: safe_bool.НовБезопБул(),
		level:  LevelDebug,
	}
	lf, err := slog_file.НовСлогФайл(kern, "test")
	if err != nil {
		return nil, fmt.Errorf("НовСлог(): in create SlogFile, err=\n\t%w", err)
	}
	сам.lf = lf

	go сам.close()
	сам.isWork.Уст()
	ош := сам.kern.Wg().Add(strSlog)
	if ош != nil {
		return nil, fmt.Errorf("НовСлог(): при добавлении группы ожидания %q, ош=\n\t%w", strSlog, ош)
	}
	сам.Инфо("НовСлог()\n")
	_ = types.ИЛоггер(сам)
	return сам, nil
}

// Паника -- проверяет на правильное утверждение
func (сам *Слог) Паника(усл bool, формат string, значения ...interface{}) {
	if !усл {
		panic(fmt.Sprintf(формат, значения...))
	}
}

// Отладка -- выводит в лог отладочную инфу
func (сам *Слог) Отладка(str string, lstVal ...interface{}) {
	сам.block.Lock()
	defer сам.block.Unlock()
	if !сам.isWork.Получ() {
		return
	}
	сам.printf("DEBUG", str, lstVal...)
}

// Инфо -- выводит в лог дежурную инфу
func (сам *Слог) Инфо(str string, lstVal ...interface{}) {
	сам.block.Lock()
	defer сам.block.Unlock()
	if !сам.isWork.Получ() {
		return
	}
	сам.printf("INFO", str, lstVal...)
}

// Внимание -- выводит в лог предупредительную инфу
func (сам *Слог) Внимание(str string, lstVal ...interface{}) {
	сам.block.Lock()
	defer сам.block.Unlock()
	if !сам.isWork.Получ() {
		return
	}
	сам.printf("WARN", str, lstVal...)
}

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

// Внутренний вызов без блокировки
func (сам *Слог) 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")
	сам.buf.WriteString(strTime + "\t")
	сам.buf.WriteString(pref + "\t")
	сам.buf.WriteString(cons.СамИмя + "\n\t")
	сам.buf.WriteString(fmt.Sprintf(str, lstVal...))
	сам.lf.Write(сам.buf.String())
	сам.lt.Write(style + сам.buf.String() + "\033[00m")
	сам.buf.Reset()
}

// Ожидает закрытия приложения
func (сам *Слог) close() {
	<-сам.kern.Done()
	сам.block.Lock()
	defer сам.block.Unlock()
	if !сам.isWork.Получ() {
		return
	}
	сам.isWork.Сброс()
	ош := сам.kern.Wg().Done(strSlog)
	if ош != nil {
		log.Printf("Слог.close(): при удалении группы ожидания, ош=\n\t%v", ош)
	}
	сам.kern.Отменить()
}
