package models

import (
	"testing"

	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
	"github.com/stretchr/testify/assert"
)

func TestMarkedItems(t *testing.T) {
	t.Run("SetMark properly reflected in MarkedItems", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
		})

		// Initially, no items should be marked
		assert.Len(t, rs.MarkedItems(), 0)

		// Mark the first item
		rs.SetMark(0, true)
		markedItems := rs.MarkedItems()
		assert.Len(t, markedItems, 1)
		assert.Equal(t, 0, markedItems[0].Index)

		// Mark the third item
		rs.SetMark(2, true)
		markedItems = rs.MarkedItems()
		assert.Len(t, markedItems, 2)
		assert.Equal(t, 0, markedItems[0].Index)
		assert.Equal(t, 2, markedItems[1].Index)

		// Verify the items themselves are correct
		item1, ok1 := markedItems[0].Item.AttributeValueAsString("id")
		item2, ok2 := markedItems[1].Item.AttributeValueAsString("id")
		assert.True(t, ok1)
		assert.True(t, ok2)
		assert.Equal(t, "item1", item1)
		assert.Equal(t, "item3", item2)
	})

	t.Run("item with Marked=true is in MarkedItems", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
		})

		// Directly set the Marked attribute to true for item at index 1
		rs.SetMark(1, true)

		markedItems := rs.MarkedItems()
		assert.Len(t, markedItems, 1)
		assert.Equal(t, 1, markedItems[0].Index)

		item, ok := markedItems[0].Item.AttributeValueAsString("id")
		assert.True(t, ok)
		assert.Equal(t, "item2", item)
	})

	t.Run("adding marked items affects result of MarkedItems", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
		})

		// Mark all items
		rs.SetMark(0, true)
		rs.SetMark(1, true)
		assert.Len(t, rs.MarkedItems(), 2)

		markedItems := rs.MarkedItems()
		expectedIndices := []int{0, 1}
		for i, expected := range expectedIndices {
			assert.Equal(t, expected, markedItems[i].Index)
		}

		// Add a new unmarked item
		rs.AddNewItem(Item{"id": &types.AttributeValueMemberS{Value: "item4"}}, ItemAttribute{})
		assert.Len(t, rs.MarkedItems(), 2)

		// Add a new marked item
		rs.AddNewItem(Item{"id": &types.AttributeValueMemberS{Value: "item5"}}, ItemAttribute{Marked: true})
		assert.Len(t, rs.MarkedItems(), 3)

		markedItems = rs.MarkedItems()
		expectedIndices = []int{0, 1, 4}
		for i, expected := range expectedIndices {
			assert.Equal(t, expected, markedItems[i].Index)
		}
	})

	t.Run("changing SetMark updates length of MarkedItems", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
			{"id": &types.AttributeValueMemberS{Value: "item4"}},
		})

		// Mark all items
		rs.SetMark(0, true)
		rs.SetMark(1, true)
		rs.SetMark(2, true)
		rs.SetMark(3, true)
		assert.Len(t, rs.MarkedItems(), 4)

		// Unmark one item
		rs.SetMark(1, false)
		assert.Len(t, rs.MarkedItems(), 3)

		// Verify the correct items are marked
		markedItems := rs.MarkedItems()
		expectedIndices := []int{0, 2, 3}
		for i, expected := range expectedIndices {
			assert.Equal(t, expected, markedItems[i].Index)
		}

		// Unmark all remaining items
		rs.SetMark(0, false)
		rs.SetMark(2, false)
		rs.SetMark(3, false)
		assert.Len(t, rs.MarkedItems(), 0)
	})

	t.Run("changing items clears all marked items", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
		})

		// Mark all items
		rs.SetMark(0, true)
		rs.SetMark(1, true)
		rs.SetMark(2, true)
		assert.Len(t, rs.MarkedItems(), 3)

		// Call SetItems with new items
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "newitem1"}},
			{"id": &types.AttributeValueMemberS{Value: "newitem2"}},
		})

		// All marks should be cleared
		assert.Len(t, rs.MarkedItems(), 0)

		// Verify none of the new items are marked
		assert.False(t, rs.Marked(0))
		assert.False(t, rs.Marked(1))
	})

	t.Run("hidden items are excluded from MarkedItems", func(t *testing.T) {
		rs := &ResultSet{}
		rs.SetItems([]Item{
			{"id": &types.AttributeValueMemberS{Value: "item1"}},
			{"id": &types.AttributeValueMemberS{Value: "item2"}},
			{"id": &types.AttributeValueMemberS{Value: "item3"}},
		})

		// Mark all items
		rs.SetMark(0, true)
		rs.SetMark(1, true)
		rs.SetMark(2, true)

		// Hide the second item
		rs.SetHidden(1, true)

		markedItems := rs.MarkedItems()
		assert.Len(t, markedItems, 2)

		// Verify only items 0 and 2 are in the marked items
		assert.Equal(t, 0, markedItems[0].Index)
		assert.Equal(t, 2, markedItems[1].Index)
	})
}
