// package slog_file -- бекенд для вывода в файл
package slog_file

import (
	"fmt"
	// "log"
	"os"
	"sync"
	"time"

	"wartank/pkg/components/safebool"
	"wartank/pkg/cons"
	"wartank/pkg/types"
)

const (
	threadName = "slog_file"
)

// SlogFile -- бэкенд для вывода в файл
type SlogFile struct {
	kern     types.IKernel
	file     *os.File
	fileName string
	isWork   *safebool.SafeBool
	chMsg    chan []byte // канал входящих сообщений
	block    sync.Mutex
}

// NewSlogFile -- возвращает новый бэкенд логгера для вывода в файл
func NewSlogFile(kern types.IKernel, build string) (*SlogFile, error) {
	{ // Предусловия
		if kern == nil {
			return nil, fmt.Errorf("NewSlogFile(): IKernel is nil")
		}
		if build == "" {
			return nil, fmt.Errorf("NewSlogFile(): strBuild is empty")
		}
	}
	sf := &SlogFile{
		kern:     kern,
		fileName: cons.SelfName + "_" + build + time.Now().UTC().Format("_2006-01-02 15:04:05.000") + ".log",
		isWork:   safebool.NewSafeBool(),
		chMsg:    make(chan []byte, 10),
	}
	sf.isWork.Set()
	_ = os.MkdirAll("./log", 0700)
	sf.fileName = "./log/" + sf.fileName
	if err := sf.open(); err != nil {
		return nil, fmt.Errorf("NewSlogFile(): in open log-file, err=%w", err)
	}
	sf.kern.Wg().Add(threadName)
	go sf.run()
	return sf, nil
}

// Открывает файл на запись
func (sf *SlogFile) open() error {
	file, err := os.OpenFile(sf.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		sf.kern.CancelApp()
		return fmt.Errorf("NewSlogFile.open(): in open %q, err=%w", sf.fileName, err)
	}
	sf.file = file
	return nil
}

// Запускает работу в отдельном потоке
func (sf *SlogFile) run() {
	defer func() {
		sf.isWork.Reset()
		sf.block.Lock()
		defer sf.block.Unlock()
		_ = sf.file.Close()
		close(sf.chMsg)
		sf.kern.Wg().Done(threadName)
		// log._rintf("SlogFile.run(): end work\n")
		sf.kern.CancelApp()
	}()
	for {
		select {
		case <-sf.kern.Done(): // Ожидание отмены контекста приложения
			return
		case msg := <-sf.chMsg:
			if err := sf.write(msg); err != nil {
				sf.kern.Slog().Errorf("SlogFile.run(): in write msg, err=\n\t%v", err)
				return
			}
		}
	}
}

// Внцтренни вызов записи
func (sf *SlogFile) write(msg []byte) error {
	_, err := sf.file.Write(msg)
	if err != nil {
		return fmt.Errorf("SlogFile.run(): in write file %q, err=\n\t%w", sf.fileName, err)
	}
	return nil
}

// Write -- записывает данные в файл
func (sf *SlogFile) Write(msg string) {
	if !sf.isWork.Get() {
		return
	}
	sf.block.Lock()
	defer sf.block.Unlock()
	sf.chMsg <- []byte(msg)
}
