package common

import (
	"fmt"
	"io"
	"time"
)

type Logger interface {
	LogPlainError(err error) (int, error)
	LogError(str string, fmt ...any) (int, error)
	Log(str string, fmt ...any) (int, error)
}

type StdoutLogging struct {
	Id             uint
	Stdout, Stderr io.StringWriter
	Date           time.Time
}

var id uint

func CreateStdoutLogger(stdout io.StringWriter, stderr io.StringWriter) Logger {
	id++

	log := &StdoutLogging{
		Id:     id,
		Stdout: stdout,
		Stderr: stderr,
		Date:   time.Now(),
	}

	return log
}

func prepareMsg(time time.Duration, id uint, str string, params ...any) string {
	var allParams []any = []any{
		id,
		time.String(),
	}
	allParams = append(allParams, params...)

	return fmt.Sprintf("[%x] [%s]: "+str+"\n", allParams...)
}

func (s *StdoutLogging) LogPlainError(err error) (int, error) {
	return s.Stderr.WriteString(prepareMsg(time.Since(s.Date), s.Id, "%#v\n", err))
}

func (s *StdoutLogging) LogError(str string, params ...any) (int, error) {
	return s.Stderr.WriteString(prepareMsg(time.Since(s.Date),s.Id, str, params...))
}

func (s *StdoutLogging) Log(str string, params ...any) (int, error) {
	return s.Stdout.WriteString(prepareMsg(time.Since(s.Date), s.Id, str, params...))
}

type LoggingWriteCloser struct {
	err bool
	*StdoutLogging
}

func (l *LoggingWriteCloser) Write(data []byte) (int, error) {
	if l.err {
		return l.LogError("%s", data)
	}

	return l.Log("%s", data)
}

func (l *LoggingWriteCloser) Close() error {
	return nil
}

func (s *StdoutLogging) CreateLogWriter(err bool) io.WriteCloser {
	logger := new(LoggingWriteCloser)
	logger.StdoutLogging = s
	logger.err = err

	return logger
}
