package native_test

import (
	"testing"

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

func newFederationClient(t *testing.T) *tutustest.ContractInvoker {
	// Federation activates at HFFaun hardfork
	return newCustomNativeClient(t, nativenames.Federation, func(cfg *config.Blockchain) {
		cfg.Hardforks = map[string]uint32{
			config.HFFaun.String(): 0, // Activate from genesis
		}
	})
}

// TestFederation_GetFeePercent tests the getFeePercent method.
func TestFederation_GetFeePercent(t *testing.T) {
	c := newFederationClient(t)

	// Default fee percent should be 50
	c.Invoke(t, 50, "getFeePercent")
}

// TestFederation_IsVisitor_NotRegistered tests isVisitor for unregistered account.
func TestFederation_IsVisitor_NotRegistered(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Unregistered account should not be a visitor
	c.Invoke(t, false, "isVisitor", acc.ScriptHash())
}

// TestFederation_GetHomeChain_NotRegistered tests getHomeChain for unregistered account.
func TestFederation_GetHomeChain_NotRegistered(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Unregistered account should return 0
	c.Invoke(t, 0, "getHomeChain", acc.ScriptHash())
}

// TestFederation_GetInterChainDebt_NoDebt tests getInterChainDebt with no debt.
func TestFederation_GetInterChainDebt_NoDebt(t *testing.T) {
	c := newFederationClient(t)

	// No debt should return 0
	c.Invoke(t, 0, "getInterChainDebt", 1)
}

// TestFederation_HasAsylum_NotGranted tests hasAsylum for account without asylum.
func TestFederation_HasAsylum_NotGranted(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// No asylum granted
	c.Invoke(t, false, "hasAsylum", acc.ScriptHash())
}

// TestFederation_GetAsylumInfo_NotGranted tests getAsylumInfo for account without asylum.
func TestFederation_GetAsylumInfo_NotGranted(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// No asylum 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 account without asylum")
	}, "getAsylumInfo", acc.ScriptHash())
}

// TestFederation_IsNaturalizedCitizen_NotNaturalized tests isNaturalizedCitizen for non-naturalized account.
func TestFederation_IsNaturalizedCitizen_NotNaturalized(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Not naturalized
	c.Invoke(t, false, "isNaturalizedCitizen", acc.ScriptHash())
}

// TestFederation_GetNaturalizationInfo_NotNaturalized tests getNaturalizationInfo for non-naturalized account.
func TestFederation_GetNaturalizationInfo_NotNaturalized(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Not naturalized 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-naturalized account")
	}, "getNaturalizationInfo", acc.ScriptHash())
}

// TestFederation_SetFeePercent_NotCommittee tests that non-committee cannot set fee percent.
func TestFederation_SetFeePercent_NotCommittee(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)

	// Should fail - not committee
	invoker.InvokeFail(t, "invalid committee signature", "setFeePercent", 60)
}

// TestFederation_SetFeePercent_InvalidPercent tests invalid fee percent values.
func TestFederation_SetFeePercent_InvalidPercent(t *testing.T) {
	c := newFederationClient(t)

	// Should fail - percent > 100
	c.InvokeFail(t, "fee percent must be 0-100", "setFeePercent", 101)
}

// TestFederation_RegisterVisitor_NotCommittee tests that non-committee cannot register visitors.
func TestFederation_RegisterVisitor_NotCommittee(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)

	// Should fail - not committee
	invoker.InvokeFail(t, "invalid committee signature", "registerVisitor", acc.ScriptHash(), 2)
}

// TestFederation_RegisterVisitor_InvalidChainID tests invalid chain ID.
func TestFederation_RegisterVisitor_InvalidChainID(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - chain ID 0 is invalid
	c.InvokeFail(t, "invalid chain ID", "registerVisitor", acc.ScriptHash(), 0)
}

// TestFederation_UnregisterVisitor_NotRegistered tests unregistering non-registered visitor.
func TestFederation_UnregisterVisitor_NotRegistered(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - visitor not registered
	c.InvokeFail(t, "visitor not found", "unregisterVisitor", acc.ScriptHash())
}

// TestFederation_SettleDebt_NotCommittee tests that non-committee cannot settle debt.
func TestFederation_SettleDebt_NotCommittee(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)

	// Should fail - not committee
	invoker.InvokeFail(t, "invalid committee signature", "settleDebt", 1, 1000)
}

// TestFederation_SettleDebt_InsufficientDebt tests settling more debt than exists.
func TestFederation_SettleDebt_InsufficientDebt(t *testing.T) {
	c := newFederationClient(t)

	// Should fail - no debt to settle
	c.InvokeFail(t, "insufficient debt to settle", "settleDebt", 1, 1000)
}

// TestFederation_GrantAsylum_NotCommittee tests that non-committee cannot grant asylum.
func TestFederation_GrantAsylum_NotCommittee(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)

	// Should fail - not committee
	invoker.InvokeFail(t, "invalid committee signature", "grantAsylum", acc.ScriptHash(), 2, "Persecution")
}

// TestFederation_GrantAsylum_InvalidChainID tests granting asylum with invalid chain ID.
func TestFederation_GrantAsylum_InvalidChainID(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - chain ID 0 is invalid
	c.InvokeFail(t, "invalid chain ID", "grantAsylum", acc.ScriptHash(), 0, "Persecution")
}

// TestFederation_RevokeAsylum_NotGranted tests revoking asylum when not granted.
func TestFederation_RevokeAsylum_NotGranted(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - asylum not granted
	c.InvokeFail(t, "asylum not found", "revokeAsylum", acc.ScriptHash())
}

// TestFederation_Naturalize_NotCommittee tests that non-committee cannot naturalize.
func TestFederation_Naturalize_NotCommittee(t *testing.T) {
	c := newFederationClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)

	// Should fail - not committee
	invoker.InvokeFail(t, "invalid committee signature", "naturalize", acc.ScriptHash(), 2)
}

// Note: More complex tests that require:
// - Successfully registering visitors and verifying state changes
// - Successfully granting/revoking asylum
// - Successfully naturalizing citizens
// - Debt accumulation and settlement
// Would require more sophisticated test setup with committee-signed transactions.
