package cmdlogger

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"strings"
)

var (
	GlobalLogger *slog.Logger
)

type Handler struct {
	stdout             io.Writer
	stderr             io.Writer
	hasErrored         bool
	everythingToStderr bool
	level              slog.Leveler
	overrideHandler    slog.Handler

	hasErroredBecauseInvalidConfig bool
}

// SendEverythingToStderr tells the logger to send all logs to stderr regardless
// of their level.
//
// This is useful if we're expecting to output structured data to stdout such
// as JSON, which cannot be mixed with other output.
func (c *Handler) SendEverythingToStderr() {
	c.everythingToStderr = true
}

func (c *Handler) SetLevel(level slog.Leveler) {
	c.level = level
}

func (c *Handler) writer(level slog.Level) io.Writer {
	if c.everythingToStderr || level == slog.LevelError {
		return c.stderr
	}

	return c.stdout
}

func (c *Handler) Enabled(ctx context.Context, level slog.Level) bool {
	if level == slog.LevelError {
		c.SetHasErrored()
	}

	if c.overrideHandler != nil {
		return c.overrideHandler.Enabled(ctx, level)
	}

	return level >= c.level.Level()
}

func (c *Handler) Handle(ctx context.Context, record slog.Record) error {
	// todo: we probably want to be reporting disabled plugins in some way,
	//  but currently our snapshot-based tests cannot handle os-dependent
	//  output and we've got some plugins that are only available for linux
	if strings.HasPrefix(record.Message, "Disabling plugin ") {
		return nil
	}

	if record.Level == slog.LevelError {
		c.SetHasErrored()

		if strings.HasPrefix(record.Message, "Ignored invalid config file") {
			c.hasErroredBecauseInvalidConfig = true
		}
	}

	if c.overrideHandler != nil {
		return c.overrideHandler.Handle(ctx, record)
	}

	_, err := fmt.Fprint(c.writer(record.Level), record.Message+"\n")

	return err
}

func (c *Handler) SetHasErrored() {
	c.hasErrored = true
}

// HasErrored returns true if there have been any calls to Handle with
// a level of [slog.LevelError]
func (c *Handler) HasErrored() bool {
	return c.hasErrored
}

// HasErroredBecauseInvalidConfig returns true if there have been any calls to
// Handle with a level of [slog.LevelError] due to a config file being invalid
func (c *Handler) HasErroredBecauseInvalidConfig() bool {
	return c.hasErroredBecauseInvalidConfig
}

func (c *Handler) WithAttrs(a []slog.Attr) slog.Handler {
	if c.overrideHandler != nil {
		return c.overrideHandler.WithAttrs(a)
	}
	panic("not supported")
}

func (c *Handler) WithGroup(g string) slog.Handler {
	if c.overrideHandler != nil {
		return c.overrideHandler.WithGroup(g)
	}
	panic("not supported")
}

var _ CmdLogger = &Handler{}

func New(stdout, stderr io.Writer) CmdLogger {
	return &Handler{
		stdout: stdout,
		stderr: stderr,
		level:  slog.LevelInfo,
	}
}

func NewOverride(overrideHandler slog.Handler) CmdLogger {
	return &Handler{
		overrideHandler: overrideHandler,
	}
}
