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

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

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

const (
	threadName = "slog_file"
)

// СлогФайл -- бэкенд для вывода в файл
type СлогФайл struct {
	kern     types.ИЯдро
	file     *os.File
	fileName string
	isWork   *safe_bool.БезопБул
	chMsg    chan []byte // канал входящих сообщений
	block    sync.Mutex
}

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

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

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

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

// Write -- записывает данные в файл
func (сам *СлогФайл) Write(msg string) {
	if !сам.isWork.Получ() {
		return
	}
	сам.block.Lock()
	defer сам.block.Unlock()
	сам.chMsg <- []byte(msg)
}
