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 newPonsClient(t *testing.T) *tutustest.ContractInvoker {
	return newNativeClient(t, nativenames.Pons)
}

// TestPons_GetConfig tests the getConfig method.
func TestPons_GetConfig(t *testing.T) {
	c := newPonsClient(t)

	// Get default config
	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, 5, len(arr)) // PonsConfig has 5 fields
	}, "getConfig")
}

// TestPons_GetAgreementCount tests the getAgreementCount method.
func TestPons_GetAgreementCount(t *testing.T) {
	c := newPonsClient(t)

	// Initially should be 0
	c.Invoke(t, 0, "getAgreementCount")
}

// TestPons_GetVerificationCount tests the getVerificationCount method.
func TestPons_GetVerificationCount(t *testing.T) {
	c := newPonsClient(t)

	// Initially should be 0
	c.Invoke(t, 0, "getVerificationCount")
}

// TestPons_GetSettlementCount tests the getSettlementCount method.
func TestPons_GetSettlementCount(t *testing.T) {
	c := newPonsClient(t)

	// Initially should be 0
	c.Invoke(t, 0, "getSettlementCount")
}

// TestPons_GetCredentialShareCount tests the getCredentialShareCount method.
func TestPons_GetCredentialShareCount(t *testing.T) {
	c := newPonsClient(t)

	// Initially should be 0
	c.Invoke(t, 0, "getCredentialShareCount")
}

// TestPons_GetAgreement_NonExistent tests getting a non-existent agreement.
func TestPons_GetAgreement_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Non-existent agreement 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 agreement")
	}, "getAgreement", 999)
}

// TestPons_GetVerificationRequest_NonExistent tests getting a non-existent verification.
func TestPons_GetVerificationRequest_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Non-existent verification 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 verification")
	}, "getVerificationRequest", 999)
}

// TestPons_GetSettlementRequest_NonExistent tests getting a non-existent settlement.
func TestPons_GetSettlementRequest_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Non-existent settlement 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 settlement")
	}, "getSettlementRequest", 999)
}

// TestPons_GetCredentialShare_NonExistent tests getting a non-existent credential share.
func TestPons_GetCredentialShare_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Non-existent credential share 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 credential share")
	}, "getCredentialShare", 999)
}

// TestPons_VerifyCredentialShare_NonExistent tests verifying a non-existent credential share.
func TestPons_VerifyCredentialShare_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Non-existent credential share should return false
	c.Invoke(t, false, "verifyCredentialShare", 999)
}

// TestPons_HasActiveAgreement_NoAgreement tests checking for non-existent agreement.
func TestPons_HasActiveAgreement_NoAgreement(t *testing.T) {
	c := newPonsClient(t)

	// No agreements exist, should return false
	c.Invoke(t, false, "hasActiveAgreement", 2, int64(state.AgreementTypeSettlement))
}

// TestPons_SetLocalChainID_NotCommittee tests that non-committee cannot set chain ID.
func TestPons_SetLocalChainID_NotCommittee(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

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

	// Should fail - not committee
	invoker.InvokeFail(t, "only committee can set chain ID", "setLocalChainID", 5)
}

// TestPons_CreateAgreement_NotCommittee tests that non-committee cannot create agreements.
func TestPons_CreateAgreement_NotCommittee(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)
	termsHash := make([]byte, 32)

	// Should fail - not committee
	invoker.InvokeFail(t, "only committee can create agreements", "createAgreement",
		2, int64(state.AgreementTypeSettlement), termsHash, 100000)
}

// TestPons_CreateAgreement_InvalidType tests creating agreement with invalid type.
func TestPons_CreateAgreement_InvalidType(t *testing.T) {
	c := newPonsClient(t)
	termsHash := make([]byte, 32)

	// Invalid agreement type (> AgreementTypeComprehensive)
	c.InvokeFail(t, "invalid agreement type", "createAgreement",
		2, 99, termsHash, 100000)
}

// TestPons_UpdateAgreementStatus_NonExistent tests updating non-existent agreement.
func TestPons_UpdateAgreementStatus_NonExistent(t *testing.T) {
	c := newPonsClient(t)

	// Should fail - agreement doesn't exist
	c.InvokeFail(t, "agreement not found", "updateAgreementStatus",
		999, int64(state.AgreementActive))
}

// TestPons_RequestVerification_NoAgreement tests verification without agreement.
func TestPons_RequestVerification_NoAgreement(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	dataHash := make([]byte, 32)

	// Should fail - no agreement with target chain
	c.InvokeFail(t, "no active agreement with target chain", "requestVerification",
		2, acc.ScriptHash(), int64(state.VerificationTypeIdentity), dataHash)
}

// TestPons_RespondVerification_NotCommittee tests that non-committee cannot respond.
func TestPons_RespondVerification_NotCommittee(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)
	responseHash := make([]byte, 32)

	// Should fail - not committee
	invoker.InvokeFail(t, "only committee can respond to verification requests", "respondVerification",
		1, true, responseHash)
}

// TestPons_RespondVerification_NotFound tests responding to non-existent verification.
func TestPons_RespondVerification_NotFound(t *testing.T) {
	c := newPonsClient(t)
	responseHash := make([]byte, 32)

	// Should fail - verification doesn't exist
	c.InvokeFail(t, "verification request not found", "respondVerification",
		999, true, responseHash)
}

// TestPons_RequestSettlement_NoAgreement tests settlement without agreement.
func TestPons_RequestSettlement_NoAgreement(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

	acc := e.NewAccount(t)

	// Should fail - no settlement agreement
	c.InvokeFail(t, "no active agreement with target chain", "requestSettlement",
		2, acc.ScriptHash(), 1000_00000000, "Payment reference")
}

// TestPons_CompleteSettlement_NotCommittee tests that non-committee cannot complete settlement.
func TestPons_CompleteSettlement_NotCommittee(t *testing.T) {
	c := newPonsClient(t)
	e := c.Executor

	acc := e.NewAccount(t)
	invoker := c.WithSigners(acc)
	txHash := make([]byte, 32)

	// Should fail - not committee
	invoker.InvokeFail(t, "only committee can complete settlements", "completeSettlement",
		1, txHash)
}

// TestPons_CompleteSettlement_NotFound tests completing non-existent settlement.
func TestPons_CompleteSettlement_NotFound(t *testing.T) {
	c := newPonsClient(t)
	txHash := make([]byte, 32)

	// Should fail - settlement doesn't exist
	c.InvokeFail(t, "settlement request not found", "completeSettlement",
		999, txHash)
}

// TestPons_CancelSettlement_NotFound tests canceling non-existent settlement.
func TestPons_CancelSettlement_NotFound(t *testing.T) {
	c := newPonsClient(t)

	// Should fail - settlement doesn't exist
	c.InvokeFail(t, "settlement request not found", "cancelSettlement", 999)
}

// TestPons_ShareCredential_NoAgreement tests sharing credential without agreement.
func TestPons_ShareCredential_NoAgreement(t *testing.T) {
	c := newPonsClient(t)
	contentHash := make([]byte, 32)

	// Should fail - no education agreement
	c.InvokeFail(t, "no active agreement with target chain", "shareCredential",
		2, int64(state.VerificationTypeCredential), 1, contentHash, 0)
}

// TestPons_RevokeCredentialShare_NotFound tests revoking non-existent credential share.
func TestPons_RevokeCredentialShare_NotFound(t *testing.T) {
	c := newPonsClient(t)

	// Should fail - credential share doesn't exist
	c.InvokeFail(t, "credential share not found", "revokeCredentialShare", 999)
}

// Note: More complex tests that require:
// - Creating and activating bilateral agreements
// - Full verification workflow (request -> respond)
// - Full settlement workflow (request -> complete/cancel)
// - Credential sharing with active agreements
// Would require more sophisticated test setup with committee-signed transactions
// and inter-chain simulation.
