// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package statedb

import (
	"context"
	"maps"
	"slices"
	"strings"
	"testing"

	"github.com/cilium/hive"
	"github.com/cilium/hive/cell"
	"github.com/cilium/hive/hivetest"
	"github.com/cilium/hive/script"
	"github.com/cilium/hive/script/scripttest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestScript(t *testing.T) {
	log := hivetest.Logger(t)
	h := hive.New(
		Cell, // DB
		cell.Invoke(func(db *DB) {
			_ = newTestObjectTable(t, db, "test1", tagsIndex)
			_ = newTestObjectTable(t, db, "test2", tagsIndex)
		}),
	)
	t.Cleanup(func() {
		assert.NoError(t, h.Stop(log, context.TODO()))
	})
	cmds, err := h.ScriptCommands(log)
	require.NoError(t, err, "ScriptCommands")
	maps.Insert(cmds, maps.All(script.DefaultCmds()))
	engine := &script.Engine{
		Cmds: cmds,
	}
	scripttest.Test(t,
		context.Background(),
		func(t testing.TB, args []string) *script.Engine {
			return engine
		},
		[]string{},
		"testdata/*.txtar",
	)
}

func TestHeaderLine(t *testing.T) {
	type retrieval struct {
		header string
		idxs   []int
	}
	testCases := []struct {
		line  string
		names []string
		pos   []int
		get   []retrieval
	}{
		{
			"Foo   Bar   ",
			[]string{"Foo", "Bar"},
			[]int{0, 6},
			[]retrieval{
				{"Foo", []int{0}},
				{"Bar", []int{1}},
				{"Bar Foo Bar", []int{1, 0, 1}},
			},
		},
		{
			"Foo Bar   Quux",
			[]string{"Foo", "Bar", "Quux"},
			[]int{0, 4, 10},
			[]retrieval{
				{"Foo", []int{0}},
				{"Bar", []int{1}},
				{"Bar  Foo", []int{1, 0}},
				{"Quux", []int{2}},
				{"Quux   Foo", []int{2, 0}},
			},
		},
	}

	for _, tc := range testCases {
		// Parse header line into names and positions
		names, pos := splitHeaderLine(tc.line)
		require.Equal(t, tc.names, names)
		require.Equal(t, tc.pos, pos)

		// Split the header line with the parsed positions.
		header := splitByPositions(tc.line, pos, false)
		require.Equal(t, tc.names, header)

		// Join the headers with the positions.
		line := joinByPositions(header, pos, false)
		require.Equal(t, strings.TrimRight(tc.line, " \t"), line)

		// Test retrievals
		for _, r := range tc.get {
			names, pos = splitHeaderLine(r.header)
			idxs, err := getColumnIndexes(names, header)
			require.NoError(t, err)
			require.Equal(t, r.idxs, idxs)

			row := slices.Clone(header)
			cols := takeColumns(row, idxs)
			line := joinByPositions(cols, pos, false)
			require.Equal(t, line, r.header)
		}
	}
}

func TestSortedFlags(t *testing.T) {
	cases := []struct{ input, expected string }{
		{"foo bar", "foo bar"},
		{"bar foo", "bar foo"},
		{"foo bar -baz=1", "-baz=1 foo bar"},
		{"bar foo -baz=1", "-baz=1 bar foo"},
		{"foo -baz=1 bar", "-baz=1 foo bar"},
		{"-baz=1 foo bar", "-baz=1 foo bar"},
		{"-baz=1 foo -quux=2 bar", "-baz=1 -quux=2 foo bar"},
		{"-baz=1 bar foo -quux=2", "-baz=1 -quux=2 bar foo"},
	}

	for _, tc := range cases {
		actual := strings.Join(sortedArgs(strings.Split(tc.input, " ")), " ")
		assert.Equal(t, tc.expected, actual)
	}
}
