package output

import (
	"bytes"
	"errors"
	"io"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/salvacybersec/keyhunter/pkg/engine"
)

// fakeFormatter is a minimal Formatter used to verify the registry round-trip.
type fakeFormatter struct{ tag string }

func (f fakeFormatter) Format(findings []engine.Finding, w io.Writer, opts Options) error {
	_, err := w.Write([]byte(f.tag))
	return err
}

func TestFormatter_RegisterAndGet(t *testing.T) {
	Register("__test_fake__", fakeFormatter{tag: "ok"})
	t.Cleanup(func() { delete(registry, "__test_fake__") })

	f, err := Get("__test_fake__")
	require.NoError(t, err)
	require.NotNil(t, f)

	var buf bytes.Buffer
	require.NoError(t, f.Format(nil, &buf, Options{}))
	assert.Equal(t, "ok", buf.String())
}

func TestFormatter_GetUnknown(t *testing.T) {
	_, err := Get("__definitely_not_registered__")
	require.Error(t, err)
	assert.True(t, errors.Is(err, ErrUnknownFormat), "error should wrap ErrUnknownFormat")
}

func TestFormatter_NamesSorted(t *testing.T) {
	Register("__z_fake__", fakeFormatter{})
	Register("__a_fake__", fakeFormatter{})
	t.Cleanup(func() {
		delete(registry, "__z_fake__")
		delete(registry, "__a_fake__")
	})

	names := Names()
	// The full registry may contain other entries (e.g. "table"); just assert
	// that our two names appear in sorted order relative to each other.
	var aIdx, zIdx = -1, -1
	for i, n := range names {
		switch n {
		case "__a_fake__":
			aIdx = i
		case "__z_fake__":
			zIdx = i
		}
	}
	require.GreaterOrEqual(t, aIdx, 0)
	require.GreaterOrEqual(t, zIdx, 0)
	assert.Less(t, aIdx, zIdx, "Names() must return entries in ascending order")
}

func TestOptions_Defaults(t *testing.T) {
	var opts Options
	assert.False(t, opts.Unmask)
	assert.Equal(t, "", opts.ToolName)
	assert.Equal(t, "", opts.ToolVersion)
}
