package native_test

import (
	"testing"

	"github.com/stretchr/testify/require"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/state"
	"git.marketally.com/tutus-one/tutus-chain/pkg/tutustest"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)

func newLexClient(t *testing.T) *tutustest.ContractInvoker {
	return newNativeClient(t, nativenames.Lex)
}

// TestLex_GetLawCount tests the getLawCount method.
func TestLex_GetLawCount(t *testing.T) {
	c := newLexClient(t)

	// Initially should be 0 (counter starts at 1, minus 1 = 0 laws)
	c.Invoke(t, 0, "getLawCount")
}

// TestLex_GetLaw_NonExistent tests getting a non-existent law.
func TestLex_GetLaw_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Non-existent law should return null
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		require.Nil(t, stack[0].Value(), "expected null for non-existent law")
	}, "getLaw", 999)
}

// TestLex_GetLawByName_NonExistent tests getting a law by non-existent name.
func TestLex_GetLawByName_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Non-existent law name should return null
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		require.Nil(t, stack[0].Value(), "expected null for non-existent law name")
	}, "getLawByName", "NonExistentLaw")
}

// TestLex_IsLawActive_NonExistent tests isLawActive for non-existent law.
func TestLex_IsLawActive_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Non-existent law should return false
	c.Invoke(t, false, "isLawActive", 999)
}

// TestLex_GetConstitutionalRight tests retrieving a constitutional right.
func TestLex_GetConstitutionalRight(t *testing.T) {
	c := newLexClient(t)

	// RightLife = 1 should exist
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		arr, ok := stack[0].Value().([]stackitem.Item)
		require.True(t, ok, "expected struct result")
		require.Equal(t, 4, len(arr)) // ID, Name, Description, Enforcement
	}, "getConstitutionalRight", int64(state.RightLife))
}

// TestLex_GetConstitutionalRight_Invalid tests retrieving invalid right.
func TestLex_GetConstitutionalRight_Invalid(t *testing.T) {
	c := newLexClient(t)

	// Invalid right ID should return null
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		require.Nil(t, stack[0].Value(), "expected null for invalid right")
	}, "getConstitutionalRight", 999)
}

// TestLex_GetAllConstitutionalRights tests retrieving all constitutional rights.
func TestLex_GetAllConstitutionalRights(t *testing.T) {
	c := newLexClient(t)

	// Should return 14 constitutional rights
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		arr, ok := stack[0].Value().([]stackitem.Item)
		require.True(t, ok, "expected array result")
		require.Equal(t, 14, len(arr), "expected 14 constitutional rights")
	}, "getAllConstitutionalRights")
}

// TestLex_HasRight_NoRestriction tests hasRight for unrestricted rights.
func TestLex_HasRight_NoRestriction(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Without Vita, hasRight should return false (must have active Vita)
	c.Invoke(t, false, "hasRight", acc.ScriptHash(), int64(state.RightLife))
}

// TestLex_IsRestricted_NoRestriction tests isRestricted with no restrictions.
func TestLex_IsRestricted_NoRestriction(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// No restriction should return false
	c.Invoke(t, false, "isRestricted", acc.ScriptHash(), int64(state.RightLife))
}

// TestLex_GetRestriction_NonExistent tests getting non-existent restriction.
func TestLex_GetRestriction_NonExistent(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Non-existent restriction should return null
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		require.Nil(t, stack[0].Value(), "expected null for non-existent restriction")
	}, "getRestriction", acc.ScriptHash(), int64(state.RightLife))
}

// TestLex_GetSubjectRestrictions_Empty tests getting restrictions for subject with none.
func TestLex_GetSubjectRestrictions_Empty(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// No restrictions should return empty array
	c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
		require.Equal(t, 1, len(stack))
		arr, ok := stack[0].Value().([]stackitem.Item)
		require.True(t, ok, "expected array result")
		require.Equal(t, 0, len(arr), "expected empty array")
	}, "getSubjectRestrictions", acc.ScriptHash())
}

// TestLex_EnactLaw_Constitutional tests that constitutional laws cannot be enacted.
func TestLex_EnactLaw_Constitutional(t *testing.T) {
	c := newLexClient(t)

	contentHash := make([]byte, 32)
	category := int64(state.LawCategoryConstitutional)

	// Should fail - constitutional is below the valid range (Constitutional=1 < Federal=2)
	// The range check fails first with "invalid law category"
	c.InvokeFail(t, "invalid law category", "enactLaw",
		"TestLaw", contentHash, category, 0, 0, 0, int64(state.EnforcementAutomatic))
}

// TestLex_RepealLaw_NonExistent tests repealing non-existent law.
func TestLex_RepealLaw_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Should fail - law doesn't exist
	c.InvokeFail(t, "law not found", "repealLaw", 999, "Test reason")
}

// TestLex_SuspendLaw_NonExistent tests suspending non-existent law.
func TestLex_SuspendLaw_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Should fail - law doesn't exist
	c.InvokeFail(t, "law not found", "suspendLaw", 999, "Test reason", 1000)
}

// TestLex_ReinstateLaw_NonExistent tests reinstating non-existent law.
func TestLex_ReinstateLaw_NonExistent(t *testing.T) {
	c := newLexClient(t)

	// Should fail - law doesn't exist
	c.InvokeFail(t, "law not found", "reinstateLaw", 999)
}

// TestLex_RestrictRight_NoCaseID tests that restrictions require case ID.
func TestLex_RestrictRight_NoCaseID(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	emptyCaseID := make([]byte, 32) // All zeros = empty case ID

	// Should fail - case ID required
	c.InvokeFail(t, "case ID required for due process", "restrictRight",
		acc.ScriptHash(), int64(state.RightLiberty), int64(state.RestrictionSuspend), 1000, "Test reason", emptyCaseID)
}

// TestLex_RestrictRight_NoExpiration tests that restrictions require expiration.
func TestLex_RestrictRight_NoExpiration(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	caseID := make([]byte, 32)
	caseID[0] = 1 // Non-empty case ID

	// Should fail - expiration required (duration = 0)
	c.InvokeFail(t, "expiration required (no indefinite restrictions)", "restrictRight",
		acc.ScriptHash(), int64(state.RightLiberty), int64(state.RestrictionSuspend), 0, "Test reason", caseID)
}

// TestLex_LiftRestriction_NonExistent tests lifting non-existent restriction.
func TestLex_LiftRestriction_NonExistent(t *testing.T) {
	c := newLexClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - restriction doesn't exist
	c.InvokeFail(t, "restriction not found", "liftRestriction",
		acc.ScriptHash(), int64(state.RightLiberty), "Test reason")
}

// Note: More complex tests that require:
// - Enacting laws (committee authority with proper parameters)
// - Restricting/lifting rights (judicial authority)
// - Verifying Vita holders have rights
// Would require more sophisticated test setup with proper cross-contract integration.
