package loggers

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/haydenhargreaves/Potion/internal/infrastructure/logging"
)

type FileLogger struct {
	writer io.Writer
	file   *os.File
	filter logging.LogLevel
}

var _ logging.Logger = (*FileLogger)(nil)

// NewFileLogger creates a new file logger, opened on the filepath provided. If any errors
// occur, an error will be returned, along with an EMPTY logger. This is not a pointer return
// so it will never be nil, just empty.
//
// This function does not close the file, cleanup function that is returned should be called
// to close the file opened in this function.
func NewFileLogger(filepath string, filter logging.LogLevel) (logging.Logger, func() error, error) {
	f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
	if err != nil {
		return &FileLogger{}, nil, err
	}

	if f == nil {
		return &FileLogger{}, nil, fmt.Errorf("File could not be opened. File is nil.")
	}

	logger := &FileLogger{
		writer: f,
		file:   f,
		filter: filter,
	}

	cleanup := func() error {
		return f.Close()
	}

	return logger, cleanup, nil
}

// Log implements the interface.
func (l *FileLogger) Log(level logging.LogLevel, format string, v ...any) {
	// level is too low, do not log
	if !l.filter.MatchFilter(level) {
		return
	}

	timestamp := time.Now().UTC().Format("01/02/2006 - 15:04:05")
	fullFormat := fmt.Sprintf("%-13s %s | %s\n", "["+level+"]", timestamp, format)
	bytes := fmt.Appendf(nil, fullFormat, v...)
	l.writer.Write(bytes)
	l.file.Sync()
}
