package state

import (
	"errors"
	"fmt"
	"math/big"

	"git.marketally.com/tutus-one/tutus-chain/pkg/io"
	"git.marketally.com/tutus-one/tutus-chain/pkg/util"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)

// OpportunityType represents the type of investment opportunity.
type OpportunityType uint8

// Investment opportunity types.
const (
	OpportunityPIO OpportunityType = 0 // Public Investment Opportunity - universal access
	OpportunityEIO OpportunityType = 1 // Employee Investment Opportunity - workplace
	OpportunityCIO OpportunityType = 2 // Contractor Investment Opportunity - gig economy
)

// OpportunityStatus represents the status of an investment opportunity.
type OpportunityStatus uint8

// Investment opportunity statuses.
const (
	OpportunityDraft      OpportunityStatus = 0 // Being created
	OpportunityVoting     OpportunityStatus = 1 // Under democratic voting
	OpportunityActive     OpportunityStatus = 2 // Open for investments
	OpportunityClosed     OpportunityStatus = 3 // No longer accepting investments
	OpportunityCompleted  OpportunityStatus = 4 // Returns distributed
	OpportunityCancelled  OpportunityStatus = 5 // Cancelled with refunds
	OpportunityFailed     OpportunityStatus = 6 // Did not meet minimum requirements
)

// InvestmentStatus represents the status of an individual investment.
type InvestmentStatus uint8

// Investment statuses.
const (
	InvestmentActive    InvestmentStatus = 0 // Investment is active
	InvestmentWithdrawn InvestmentStatus = 1 // Investor withdrew
	InvestmentCompleted InvestmentStatus = 2 // Returns distributed
	InvestmentRefunded  InvestmentStatus = 3 // Refunded due to cancellation
)

// EligibilityType represents eligibility flags.
type EligibilityType uint8

// Eligibility flags (can be combined).
const (
	EligibilityNone EligibilityType = 0
	EligibilityPIO  EligibilityType = 1 << 0 // Public investment eligible
	EligibilityEIO  EligibilityType = 1 << 1 // Employee investment eligible
	EligibilityCIO  EligibilityType = 1 << 2 // Contractor investment eligible
	EligibilityAll  EligibilityType = EligibilityPIO | EligibilityEIO | EligibilityCIO
)

// InvestmentOpportunity represents an investment opportunity.
type InvestmentOpportunity struct {
	ID                  uint64            // Unique opportunity identifier
	Type                OpportunityType   // PIO, EIO, or CIO
	Status              OpportunityStatus // Current status
	Creator             util.Uint160      // Creator's address (must have Vita)
	SponsorVitaID       uint64            // Sponsor's Vita ID (for EIO: employer, CIO: platform)
	Name                string            // Opportunity name
	Description         string            // Investment description
	TermsHash           util.Uint256      // Hash of full terms (off-chain)
	MinParticipants     uint64            // Required minimum participants
	MaxParticipants     uint64            // Maximum allowed participants (0 = unlimited)
	CurrentParticipants uint64            // Current number of participants
	MinInvestment       uint64            // Minimum VTS investment per person
	MaxInvestment       uint64            // Maximum VTS investment per person
	TotalPool           uint64            // Total VTS in opportunity
	TargetPool          uint64            // Target total pool size
	ExpectedReturns     uint64            // Projected returns (basis points, 10000 = 100%)
	RiskLevel           uint8             // Risk level 1-10 (AI-assessed)
	VotingDeadline      uint32            // Block height for voting deadline
	InvestmentDeadline  uint32            // Block height for investment deadline
	MaturityDate        uint32            // Block height when returns distributed
	ProposalID          uint64            // Associated Eligere proposal ID (0 = none)
	CreatedAt           uint32            // Block height when created
	UpdatedAt           uint32            // Block height when last updated
}

// DecodeBinary implements the io.Serializable interface.
func (o *InvestmentOpportunity) DecodeBinary(br *io.BinReader) {
	o.ID = br.ReadU64LE()
	o.Type = OpportunityType(br.ReadB())
	o.Status = OpportunityStatus(br.ReadB())
	br.ReadBytes(o.Creator[:])
	o.SponsorVitaID = br.ReadU64LE()
	o.Name = br.ReadString()
	o.Description = br.ReadString()
	br.ReadBytes(o.TermsHash[:])
	o.MinParticipants = br.ReadU64LE()
	o.MaxParticipants = br.ReadU64LE()
	o.CurrentParticipants = br.ReadU64LE()
	o.MinInvestment = br.ReadU64LE()
	o.MaxInvestment = br.ReadU64LE()
	o.TotalPool = br.ReadU64LE()
	o.TargetPool = br.ReadU64LE()
	o.ExpectedReturns = br.ReadU64LE()
	o.RiskLevel = br.ReadB()
	o.VotingDeadline = br.ReadU32LE()
	o.InvestmentDeadline = br.ReadU32LE()
	o.MaturityDate = br.ReadU32LE()
	o.ProposalID = br.ReadU64LE()
	o.CreatedAt = br.ReadU32LE()
	o.UpdatedAt = br.ReadU32LE()
}

// EncodeBinary implements the io.Serializable interface.
func (o *InvestmentOpportunity) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(o.ID)
	bw.WriteB(byte(o.Type))
	bw.WriteB(byte(o.Status))
	bw.WriteBytes(o.Creator[:])
	bw.WriteU64LE(o.SponsorVitaID)
	bw.WriteString(o.Name)
	bw.WriteString(o.Description)
	bw.WriteBytes(o.TermsHash[:])
	bw.WriteU64LE(o.MinParticipants)
	bw.WriteU64LE(o.MaxParticipants)
	bw.WriteU64LE(o.CurrentParticipants)
	bw.WriteU64LE(o.MinInvestment)
	bw.WriteU64LE(o.MaxInvestment)
	bw.WriteU64LE(o.TotalPool)
	bw.WriteU64LE(o.TargetPool)
	bw.WriteU64LE(o.ExpectedReturns)
	bw.WriteB(o.RiskLevel)
	bw.WriteU32LE(o.VotingDeadline)
	bw.WriteU32LE(o.InvestmentDeadline)
	bw.WriteU32LE(o.MaturityDate)
	bw.WriteU64LE(o.ProposalID)
	bw.WriteU32LE(o.CreatedAt)
	bw.WriteU32LE(o.UpdatedAt)
}

// ToStackItem implements stackitem.Convertible interface.
func (o *InvestmentOpportunity) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(o.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(o.Type))),
		stackitem.NewBigInteger(big.NewInt(int64(o.Status))),
		stackitem.NewByteArray(o.Creator.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(o.SponsorVitaID))),
		stackitem.NewByteArray([]byte(o.Name)),
		stackitem.NewByteArray([]byte(o.Description)),
		stackitem.NewByteArray(o.TermsHash.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(o.MinParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(o.MaxParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(o.CurrentParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(o.MinInvestment))),
		stackitem.NewBigInteger(big.NewInt(int64(o.MaxInvestment))),
		stackitem.NewBigInteger(big.NewInt(int64(o.TotalPool))),
		stackitem.NewBigInteger(big.NewInt(int64(o.TargetPool))),
		stackitem.NewBigInteger(big.NewInt(int64(o.ExpectedReturns))),
		stackitem.NewBigInteger(big.NewInt(int64(o.RiskLevel))),
		stackitem.NewBigInteger(big.NewInt(int64(o.VotingDeadline))),
		stackitem.NewBigInteger(big.NewInt(int64(o.InvestmentDeadline))),
		stackitem.NewBigInteger(big.NewInt(int64(o.MaturityDate))),
		stackitem.NewBigInteger(big.NewInt(int64(o.ProposalID))),
		stackitem.NewBigInteger(big.NewInt(int64(o.CreatedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(o.UpdatedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (o *InvestmentOpportunity) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 23 {
		return fmt.Errorf("wrong number of elements: expected 23, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	o.ID = id.Uint64()

	oppType, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid type: %w", err)
	}
	o.Type = OpportunityType(oppType.Uint64())

	status, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid status: %w", err)
	}
	o.Status = OpportunityStatus(status.Uint64())

	creator, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid creator: %w", err)
	}
	o.Creator, err = util.Uint160DecodeBytesBE(creator)
	if err != nil {
		return fmt.Errorf("invalid creator address: %w", err)
	}

	sponsorVitaID, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid sponsorVitaID: %w", err)
	}
	o.SponsorVitaID = sponsorVitaID.Uint64()

	name, err := items[5].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid name: %w", err)
	}
	o.Name = string(name)

	description, err := items[6].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid description: %w", err)
	}
	o.Description = string(description)

	termsHash, err := items[7].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid termsHash: %w", err)
	}
	o.TermsHash, err = util.Uint256DecodeBytesBE(termsHash)
	if err != nil {
		return fmt.Errorf("invalid termsHash: %w", err)
	}

	minParticipants, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minParticipants: %w", err)
	}
	o.MinParticipants = minParticipants.Uint64()

	maxParticipants, err := items[9].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxParticipants: %w", err)
	}
	o.MaxParticipants = maxParticipants.Uint64()

	currentParticipants, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid currentParticipants: %w", err)
	}
	o.CurrentParticipants = currentParticipants.Uint64()

	minInvestment, err := items[11].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minInvestment: %w", err)
	}
	o.MinInvestment = minInvestment.Uint64()

	maxInvestment, err := items[12].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxInvestment: %w", err)
	}
	o.MaxInvestment = maxInvestment.Uint64()

	totalPool, err := items[13].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalPool: %w", err)
	}
	o.TotalPool = totalPool.Uint64()

	targetPool, err := items[14].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid targetPool: %w", err)
	}
	o.TargetPool = targetPool.Uint64()

	expectedReturns, err := items[15].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid expectedReturns: %w", err)
	}
	o.ExpectedReturns = expectedReturns.Uint64()

	riskLevel, err := items[16].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid riskLevel: %w", err)
	}
	o.RiskLevel = uint8(riskLevel.Uint64())

	votingDeadline, err := items[17].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid votingDeadline: %w", err)
	}
	o.VotingDeadline = uint32(votingDeadline.Uint64())

	investmentDeadline, err := items[18].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid investmentDeadline: %w", err)
	}
	o.InvestmentDeadline = uint32(investmentDeadline.Uint64())

	maturityDate, err := items[19].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maturityDate: %w", err)
	}
	o.MaturityDate = uint32(maturityDate.Uint64())

	proposalID, err := items[20].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid proposalID: %w", err)
	}
	o.ProposalID = proposalID.Uint64()

	createdAt, err := items[21].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid createdAt: %w", err)
	}
	o.CreatedAt = uint32(createdAt.Uint64())

	updatedAt, err := items[22].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid updatedAt: %w", err)
	}
	o.UpdatedAt = uint32(updatedAt.Uint64())

	return nil
}

// Investment represents an individual's investment in an opportunity.
type Investment struct {
	ID            uint64           // Unique investment ID
	OpportunityID uint64           // Opportunity invested in
	VitaID        uint64           // Investor's Vita ID
	Investor      util.Uint160     // Investor's address
	Amount        uint64           // VTS amount invested
	Status        InvestmentStatus // Current status
	ReturnAmount  uint64           // Returns received (when completed)
	CreatedAt     uint32           // Block height when invested
	UpdatedAt     uint32           // Block height when last updated
}

// DecodeBinary implements the io.Serializable interface.
func (i *Investment) DecodeBinary(br *io.BinReader) {
	i.ID = br.ReadU64LE()
	i.OpportunityID = br.ReadU64LE()
	i.VitaID = br.ReadU64LE()
	br.ReadBytes(i.Investor[:])
	i.Amount = br.ReadU64LE()
	i.Status = InvestmentStatus(br.ReadB())
	i.ReturnAmount = br.ReadU64LE()
	i.CreatedAt = br.ReadU32LE()
	i.UpdatedAt = br.ReadU32LE()
}

// EncodeBinary implements the io.Serializable interface.
func (i *Investment) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(i.ID)
	bw.WriteU64LE(i.OpportunityID)
	bw.WriteU64LE(i.VitaID)
	bw.WriteBytes(i.Investor[:])
	bw.WriteU64LE(i.Amount)
	bw.WriteB(byte(i.Status))
	bw.WriteU64LE(i.ReturnAmount)
	bw.WriteU32LE(i.CreatedAt)
	bw.WriteU32LE(i.UpdatedAt)
}

// ToStackItem implements stackitem.Convertible interface.
func (i *Investment) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(i.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(i.OpportunityID))),
		stackitem.NewBigInteger(big.NewInt(int64(i.VitaID))),
		stackitem.NewByteArray(i.Investor.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(i.Amount))),
		stackitem.NewBigInteger(big.NewInt(int64(i.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(i.ReturnAmount))),
		stackitem.NewBigInteger(big.NewInt(int64(i.CreatedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(i.UpdatedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (i *Investment) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 9 {
		return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	i.ID = id.Uint64()

	oppID, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid opportunityID: %w", err)
	}
	i.OpportunityID = oppID.Uint64()

	vitaID, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	i.VitaID = vitaID.Uint64()

	investor, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid investor: %w", err)
	}
	i.Investor, err = util.Uint160DecodeBytesBE(investor)
	if err != nil {
		return fmt.Errorf("invalid investor address: %w", err)
	}

	amount, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid amount: %w", err)
	}
	i.Amount = amount.Uint64()

	status, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid status: %w", err)
	}
	i.Status = InvestmentStatus(status.Uint64())

	returnAmount, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid returnAmount: %w", err)
	}
	i.ReturnAmount = returnAmount.Uint64()

	createdAt, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid createdAt: %w", err)
	}
	i.CreatedAt = uint32(createdAt.Uint64())

	updatedAt, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid updatedAt: %w", err)
	}
	i.UpdatedAt = uint32(updatedAt.Uint64())

	return nil
}

// InvestorEligibility represents an investor's eligibility status.
type InvestorEligibility struct {
	VitaID               uint64          // Investor's Vita ID
	Investor             util.Uint160    // Investor's address
	Eligibility          EligibilityType // Eligibility flags
	ScireCompleted       bool            // Investment education completed
	RiskScore            uint8           // AI-calculated risk tolerance (1-10)
	TotalInvested        uint64          // Total VTS currently invested
	TotalReturns         uint64          // Total returns received
	ActiveInvestments    uint64          // Number of active investments
	CompletedInvestments uint64          // Number of completed investments
	HasViolations        bool            // Any past investment abuse
	ViolationCount       uint8           // Number of violations
	LastActivity         uint32          // Block height of last activity
	CreatedAt            uint32          // Block height when created
	UpdatedAt            uint32          // Block height when updated
}

// DecodeBinary implements the io.Serializable interface.
func (e *InvestorEligibility) DecodeBinary(br *io.BinReader) {
	e.VitaID = br.ReadU64LE()
	br.ReadBytes(e.Investor[:])
	e.Eligibility = EligibilityType(br.ReadB())
	e.ScireCompleted = br.ReadBool()
	e.RiskScore = br.ReadB()
	e.TotalInvested = br.ReadU64LE()
	e.TotalReturns = br.ReadU64LE()
	e.ActiveInvestments = br.ReadU64LE()
	e.CompletedInvestments = br.ReadU64LE()
	e.HasViolations = br.ReadBool()
	e.ViolationCount = br.ReadB()
	e.LastActivity = br.ReadU32LE()
	e.CreatedAt = br.ReadU32LE()
	e.UpdatedAt = br.ReadU32LE()
}

// EncodeBinary implements the io.Serializable interface.
func (e *InvestorEligibility) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(e.VitaID)
	bw.WriteBytes(e.Investor[:])
	bw.WriteB(byte(e.Eligibility))
	bw.WriteBool(e.ScireCompleted)
	bw.WriteB(e.RiskScore)
	bw.WriteU64LE(e.TotalInvested)
	bw.WriteU64LE(e.TotalReturns)
	bw.WriteU64LE(e.ActiveInvestments)
	bw.WriteU64LE(e.CompletedInvestments)
	bw.WriteBool(e.HasViolations)
	bw.WriteB(e.ViolationCount)
	bw.WriteU32LE(e.LastActivity)
	bw.WriteU32LE(e.CreatedAt)
	bw.WriteU32LE(e.UpdatedAt)
}

// ToStackItem implements stackitem.Convertible interface.
func (e *InvestorEligibility) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(e.VitaID))),
		stackitem.NewByteArray(e.Investor.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(e.Eligibility))),
		stackitem.NewBool(e.ScireCompleted),
		stackitem.NewBigInteger(big.NewInt(int64(e.RiskScore))),
		stackitem.NewBigInteger(big.NewInt(int64(e.TotalInvested))),
		stackitem.NewBigInteger(big.NewInt(int64(e.TotalReturns))),
		stackitem.NewBigInteger(big.NewInt(int64(e.ActiveInvestments))),
		stackitem.NewBigInteger(big.NewInt(int64(e.CompletedInvestments))),
		stackitem.NewBool(e.HasViolations),
		stackitem.NewBigInteger(big.NewInt(int64(e.ViolationCount))),
		stackitem.NewBigInteger(big.NewInt(int64(e.LastActivity))),
		stackitem.NewBigInteger(big.NewInt(int64(e.CreatedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(e.UpdatedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (e *InvestorEligibility) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 14 {
		return fmt.Errorf("wrong number of elements: expected 14, got %d", len(items))
	}

	vitaID, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	e.VitaID = vitaID.Uint64()

	investor, err := items[1].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid investor: %w", err)
	}
	e.Investor, err = util.Uint160DecodeBytesBE(investor)
	if err != nil {
		return fmt.Errorf("invalid investor address: %w", err)
	}

	eligibility, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid eligibility: %w", err)
	}
	e.Eligibility = EligibilityType(eligibility.Uint64())

	scireCompleted, err := items[3].TryBool()
	if err != nil {
		return fmt.Errorf("invalid scireCompleted: %w", err)
	}
	e.ScireCompleted = scireCompleted

	riskScore, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid riskScore: %w", err)
	}
	e.RiskScore = uint8(riskScore.Uint64())

	totalInvested, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalInvested: %w", err)
	}
	e.TotalInvested = totalInvested.Uint64()

	totalReturns, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalReturns: %w", err)
	}
	e.TotalReturns = totalReturns.Uint64()

	activeInvestments, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid activeInvestments: %w", err)
	}
	e.ActiveInvestments = activeInvestments.Uint64()

	completedInvestments, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid completedInvestments: %w", err)
	}
	e.CompletedInvestments = completedInvestments.Uint64()

	hasViolations, err := items[9].TryBool()
	if err != nil {
		return fmt.Errorf("invalid hasViolations: %w", err)
	}
	e.HasViolations = hasViolations

	violationCount, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid violationCount: %w", err)
	}
	e.ViolationCount = uint8(violationCount.Uint64())

	lastActivity, err := items[11].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid lastActivity: %w", err)
	}
	e.LastActivity = uint32(lastActivity.Uint64())

	createdAt, err := items[12].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid createdAt: %w", err)
	}
	e.CreatedAt = uint32(createdAt.Uint64())

	updatedAt, err := items[13].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid updatedAt: %w", err)
	}
	e.UpdatedAt = uint32(updatedAt.Uint64())

	return nil
}

// EmploymentVerification represents verified employment for EIO eligibility.
type EmploymentVerification struct {
	VitaID        uint64       // Employee's Vita ID
	Employee      util.Uint160 // Employee's address
	EmployerVitaID uint64      // Employer's Vita ID (organization)
	Employer      util.Uint160 // Employer's address
	Position      string       // Job position/role
	StartDate     uint32       // Block height when employment started
	EndDate       uint32       // Block height when employment ended (0 = current)
	IsActive      bool         // Currently employed
	VerifiedAt    uint32       // Block height when verified
	VerifiedBy    util.Uint160 // Verifier's address (RoleInvestmentManager)
}

// DecodeBinary implements the io.Serializable interface.
func (ev *EmploymentVerification) DecodeBinary(br *io.BinReader) {
	ev.VitaID = br.ReadU64LE()
	br.ReadBytes(ev.Employee[:])
	ev.EmployerVitaID = br.ReadU64LE()
	br.ReadBytes(ev.Employer[:])
	ev.Position = br.ReadString()
	ev.StartDate = br.ReadU32LE()
	ev.EndDate = br.ReadU32LE()
	ev.IsActive = br.ReadBool()
	ev.VerifiedAt = br.ReadU32LE()
	br.ReadBytes(ev.VerifiedBy[:])
}

// EncodeBinary implements the io.Serializable interface.
func (ev *EmploymentVerification) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(ev.VitaID)
	bw.WriteBytes(ev.Employee[:])
	bw.WriteU64LE(ev.EmployerVitaID)
	bw.WriteBytes(ev.Employer[:])
	bw.WriteString(ev.Position)
	bw.WriteU32LE(ev.StartDate)
	bw.WriteU32LE(ev.EndDate)
	bw.WriteBool(ev.IsActive)
	bw.WriteU32LE(ev.VerifiedAt)
	bw.WriteBytes(ev.VerifiedBy[:])
}

// ToStackItem implements stackitem.Convertible interface.
func (ev *EmploymentVerification) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(ev.VitaID))),
		stackitem.NewByteArray(ev.Employee.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(ev.EmployerVitaID))),
		stackitem.NewByteArray(ev.Employer.BytesBE()),
		stackitem.NewByteArray([]byte(ev.Position)),
		stackitem.NewBigInteger(big.NewInt(int64(ev.StartDate))),
		stackitem.NewBigInteger(big.NewInt(int64(ev.EndDate))),
		stackitem.NewBool(ev.IsActive),
		stackitem.NewBigInteger(big.NewInt(int64(ev.VerifiedAt))),
		stackitem.NewByteArray(ev.VerifiedBy.BytesBE()),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (ev *EmploymentVerification) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 10 {
		return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
	}

	vitaID, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	ev.VitaID = vitaID.Uint64()

	employee, err := items[1].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid employee: %w", err)
	}
	ev.Employee, err = util.Uint160DecodeBytesBE(employee)
	if err != nil {
		return fmt.Errorf("invalid employee address: %w", err)
	}

	employerVitaID, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid employerVitaID: %w", err)
	}
	ev.EmployerVitaID = employerVitaID.Uint64()

	employer, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid employer: %w", err)
	}
	ev.Employer, err = util.Uint160DecodeBytesBE(employer)
	if err != nil {
		return fmt.Errorf("invalid employer address: %w", err)
	}

	position, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid position: %w", err)
	}
	ev.Position = string(position)

	startDate, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid startDate: %w", err)
	}
	ev.StartDate = uint32(startDate.Uint64())

	endDate, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid endDate: %w", err)
	}
	ev.EndDate = uint32(endDate.Uint64())

	isActive, err := items[7].TryBool()
	if err != nil {
		return fmt.Errorf("invalid isActive: %w", err)
	}
	ev.IsActive = isActive

	verifiedAt, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid verifiedAt: %w", err)
	}
	ev.VerifiedAt = uint32(verifiedAt.Uint64())

	verifiedBy, err := items[9].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid verifiedBy: %w", err)
	}
	ev.VerifiedBy, err = util.Uint160DecodeBytesBE(verifiedBy)
	if err != nil {
		return fmt.Errorf("invalid verifiedBy address: %w", err)
	}

	return nil
}

// ContractorVerification represents verified contractor status for CIO eligibility.
type ContractorVerification struct {
	VitaID       uint64       // Contractor's Vita ID
	Contractor   util.Uint160 // Contractor's address
	PlatformID   string       // Platform identifier
	Platform     util.Uint160 // Platform's address
	ContractorID string       // Platform-specific contractor ID
	StartDate    uint32       // Block height when started
	EndDate      uint32       // Block height when ended (0 = current)
	IsActive     bool         // Currently active
	VerifiedAt   uint32       // Block height when verified
	VerifiedBy   util.Uint160 // Verifier's address
}

// DecodeBinary implements the io.Serializable interface.
func (cv *ContractorVerification) DecodeBinary(br *io.BinReader) {
	cv.VitaID = br.ReadU64LE()
	br.ReadBytes(cv.Contractor[:])
	cv.PlatformID = br.ReadString()
	br.ReadBytes(cv.Platform[:])
	cv.ContractorID = br.ReadString()
	cv.StartDate = br.ReadU32LE()
	cv.EndDate = br.ReadU32LE()
	cv.IsActive = br.ReadBool()
	cv.VerifiedAt = br.ReadU32LE()
	br.ReadBytes(cv.VerifiedBy[:])
}

// EncodeBinary implements the io.Serializable interface.
func (cv *ContractorVerification) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(cv.VitaID)
	bw.WriteBytes(cv.Contractor[:])
	bw.WriteString(cv.PlatformID)
	bw.WriteBytes(cv.Platform[:])
	bw.WriteString(cv.ContractorID)
	bw.WriteU32LE(cv.StartDate)
	bw.WriteU32LE(cv.EndDate)
	bw.WriteBool(cv.IsActive)
	bw.WriteU32LE(cv.VerifiedAt)
	bw.WriteBytes(cv.VerifiedBy[:])
}

// ToStackItem implements stackitem.Convertible interface.
func (cv *ContractorVerification) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(cv.VitaID))),
		stackitem.NewByteArray(cv.Contractor.BytesBE()),
		stackitem.NewByteArray([]byte(cv.PlatformID)),
		stackitem.NewByteArray(cv.Platform.BytesBE()),
		stackitem.NewByteArray([]byte(cv.ContractorID)),
		stackitem.NewBigInteger(big.NewInt(int64(cv.StartDate))),
		stackitem.NewBigInteger(big.NewInt(int64(cv.EndDate))),
		stackitem.NewBool(cv.IsActive),
		stackitem.NewBigInteger(big.NewInt(int64(cv.VerifiedAt))),
		stackitem.NewByteArray(cv.VerifiedBy.BytesBE()),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (cv *ContractorVerification) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 10 {
		return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
	}

	vitaID, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	cv.VitaID = vitaID.Uint64()

	contractor, err := items[1].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid contractor: %w", err)
	}
	cv.Contractor, err = util.Uint160DecodeBytesBE(contractor)
	if err != nil {
		return fmt.Errorf("invalid contractor address: %w", err)
	}

	platformID, err := items[2].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid platformID: %w", err)
	}
	cv.PlatformID = string(platformID)

	platform, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid platform: %w", err)
	}
	cv.Platform, err = util.Uint160DecodeBytesBE(platform)
	if err != nil {
		return fmt.Errorf("invalid platform address: %w", err)
	}

	contractorID, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid contractorID: %w", err)
	}
	cv.ContractorID = string(contractorID)

	startDate, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid startDate: %w", err)
	}
	cv.StartDate = uint32(startDate.Uint64())

	endDate, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid endDate: %w", err)
	}
	cv.EndDate = uint32(endDate.Uint64())

	isActive, err := items[7].TryBool()
	if err != nil {
		return fmt.Errorf("invalid isActive: %w", err)
	}
	cv.IsActive = isActive

	verifiedAt, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid verifiedAt: %w", err)
	}
	cv.VerifiedAt = uint32(verifiedAt.Uint64())

	verifiedBy, err := items[9].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid verifiedBy: %w", err)
	}
	cv.VerifiedBy, err = util.Uint160DecodeBytesBE(verifiedBy)
	if err != nil {
		return fmt.Errorf("invalid verifiedBy address: %w", err)
	}

	return nil
}

// InvestmentViolation represents a recorded investment abuse violation.
type InvestmentViolation struct {
	ID              uint64       // Unique violation ID
	VitaID          uint64       // Violator's Vita ID
	Violator        util.Uint160 // Violator's address
	OpportunityID   uint64       // Related opportunity (0 = general)
	ViolationType   string       // Type of violation
	Description     string       // Violation description
	EvidenceHash    util.Uint256 // Hash of evidence (off-chain)
	Penalty         uint64       // VTS penalty applied
	ReportedBy      util.Uint160 // Reporter's address
	ReportedAt      uint32       // Block height when reported
	ResolvedAt      uint32       // Block height when resolved (0 = pending)
	Resolution      string       // Resolution description
}

// DecodeBinary implements the io.Serializable interface.
func (v *InvestmentViolation) DecodeBinary(br *io.BinReader) {
	v.ID = br.ReadU64LE()
	v.VitaID = br.ReadU64LE()
	br.ReadBytes(v.Violator[:])
	v.OpportunityID = br.ReadU64LE()
	v.ViolationType = br.ReadString()
	v.Description = br.ReadString()
	br.ReadBytes(v.EvidenceHash[:])
	v.Penalty = br.ReadU64LE()
	br.ReadBytes(v.ReportedBy[:])
	v.ReportedAt = br.ReadU32LE()
	v.ResolvedAt = br.ReadU32LE()
	v.Resolution = br.ReadString()
}

// EncodeBinary implements the io.Serializable interface.
func (v *InvestmentViolation) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(v.ID)
	bw.WriteU64LE(v.VitaID)
	bw.WriteBytes(v.Violator[:])
	bw.WriteU64LE(v.OpportunityID)
	bw.WriteString(v.ViolationType)
	bw.WriteString(v.Description)
	bw.WriteBytes(v.EvidenceHash[:])
	bw.WriteU64LE(v.Penalty)
	bw.WriteBytes(v.ReportedBy[:])
	bw.WriteU32LE(v.ReportedAt)
	bw.WriteU32LE(v.ResolvedAt)
	bw.WriteString(v.Resolution)
}

// ToStackItem implements stackitem.Convertible interface.
func (v *InvestmentViolation) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(v.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(v.VitaID))),
		stackitem.NewByteArray(v.Violator.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(v.OpportunityID))),
		stackitem.NewByteArray([]byte(v.ViolationType)),
		stackitem.NewByteArray([]byte(v.Description)),
		stackitem.NewByteArray(v.EvidenceHash.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(v.Penalty))),
		stackitem.NewByteArray(v.ReportedBy.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(v.ReportedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(v.ResolvedAt))),
		stackitem.NewByteArray([]byte(v.Resolution)),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (v *InvestmentViolation) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 12 {
		return fmt.Errorf("wrong number of elements: expected 12, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	v.ID = id.Uint64()

	vitaID, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	v.VitaID = vitaID.Uint64()

	violator, err := items[2].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid violator: %w", err)
	}
	v.Violator, err = util.Uint160DecodeBytesBE(violator)
	if err != nil {
		return fmt.Errorf("invalid violator address: %w", err)
	}

	oppID, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid opportunityID: %w", err)
	}
	v.OpportunityID = oppID.Uint64()

	violationType, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid violationType: %w", err)
	}
	v.ViolationType = string(violationType)

	description, err := items[5].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid description: %w", err)
	}
	v.Description = string(description)

	evidenceHash, err := items[6].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid evidenceHash: %w", err)
	}
	v.EvidenceHash, err = util.Uint256DecodeBytesBE(evidenceHash)
	if err != nil {
		return fmt.Errorf("invalid evidenceHash: %w", err)
	}

	penalty, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid penalty: %w", err)
	}
	v.Penalty = penalty.Uint64()

	reportedBy, err := items[8].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid reportedBy: %w", err)
	}
	v.ReportedBy, err = util.Uint160DecodeBytesBE(reportedBy)
	if err != nil {
		return fmt.Errorf("invalid reportedBy address: %w", err)
	}

	reportedAt, err := items[9].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid reportedAt: %w", err)
	}
	v.ReportedAt = uint32(reportedAt.Uint64())

	resolvedAt, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid resolvedAt: %w", err)
	}
	v.ResolvedAt = uint32(resolvedAt.Uint64())

	resolution, err := items[11].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid resolution: %w", err)
	}
	v.Resolution = string(resolution)

	return nil
}

// CollocatioConfig represents configurable parameters for the investment contract.
type CollocatioConfig struct {
	// Minimum requirements
	MinPIOParticipants uint64 // Default minimum for PIO (default: 100)
	MinEIOParticipants uint64 // Default minimum for EIO (default: 10)
	MinCIOParticipants uint64 // Default minimum for CIO (default: 25)

	// Investment limits
	DefaultMinInvestment uint64 // Default minimum investment in VTS (default: 100)
	MaxIndividualCap     uint64 // Maximum any individual can invest (default: 1000000)
	WealthConcentration  uint64 // Max % of pool by single investor (basis points, default: 500 = 5%)

	// Fees
	CreationFee      uint64 // VTS fee to create opportunity
	InvestmentFee    uint64 // Fee per investment (basis points, default: 50 = 0.5%)
	WithdrawalPenalty uint64 // Early withdrawal penalty (basis points, default: 200 = 2%)

	// Timing
	MinVotingPeriod     uint32 // Minimum blocks for voting (default: 10000)
	MinInvestmentPeriod uint32 // Minimum blocks for investment window (default: 20000)
	MinMaturityPeriod   uint32 // Minimum blocks until maturity (default: 50000)

	// Violation thresholds
	MaxViolationsBeforeBan uint8  // Violations before permanent ban (default: 3)
	ViolationCooldown      uint32 // Blocks before violation expires (default: 1000000)

	// Commit-reveal timing (anti-front-running)
	CommitRevealDelay    uint32 // Min blocks between commit and reveal (default: 10)
	CommitRevealWindow   uint32 // Max blocks to reveal after delay (default: 1000)
}

// DecodeBinary implements the io.Serializable interface.
func (c *CollocatioConfig) DecodeBinary(br *io.BinReader) {
	c.MinPIOParticipants = br.ReadU64LE()
	c.MinEIOParticipants = br.ReadU64LE()
	c.MinCIOParticipants = br.ReadU64LE()
	c.DefaultMinInvestment = br.ReadU64LE()
	c.MaxIndividualCap = br.ReadU64LE()
	c.WealthConcentration = br.ReadU64LE()
	c.CreationFee = br.ReadU64LE()
	c.InvestmentFee = br.ReadU64LE()
	c.WithdrawalPenalty = br.ReadU64LE()
	c.MinVotingPeriod = br.ReadU32LE()
	c.MinInvestmentPeriod = br.ReadU32LE()
	c.MinMaturityPeriod = br.ReadU32LE()
	c.MaxViolationsBeforeBan = br.ReadB()
	c.ViolationCooldown = br.ReadU32LE()
	c.CommitRevealDelay = br.ReadU32LE()
	c.CommitRevealWindow = br.ReadU32LE()
}

// EncodeBinary implements the io.Serializable interface.
func (c *CollocatioConfig) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(c.MinPIOParticipants)
	bw.WriteU64LE(c.MinEIOParticipants)
	bw.WriteU64LE(c.MinCIOParticipants)
	bw.WriteU64LE(c.DefaultMinInvestment)
	bw.WriteU64LE(c.MaxIndividualCap)
	bw.WriteU64LE(c.WealthConcentration)
	bw.WriteU64LE(c.CreationFee)
	bw.WriteU64LE(c.InvestmentFee)
	bw.WriteU64LE(c.WithdrawalPenalty)
	bw.WriteU32LE(c.MinVotingPeriod)
	bw.WriteU32LE(c.MinInvestmentPeriod)
	bw.WriteU32LE(c.MinMaturityPeriod)
	bw.WriteB(c.MaxViolationsBeforeBan)
	bw.WriteU32LE(c.ViolationCooldown)
	bw.WriteU32LE(c.CommitRevealDelay)
	bw.WriteU32LE(c.CommitRevealWindow)
}

// ToStackItem implements stackitem.Convertible interface.
func (c *CollocatioConfig) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(c.MinPIOParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinEIOParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinCIOParticipants))),
		stackitem.NewBigInteger(big.NewInt(int64(c.DefaultMinInvestment))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MaxIndividualCap))),
		stackitem.NewBigInteger(big.NewInt(int64(c.WealthConcentration))),
		stackitem.NewBigInteger(big.NewInt(int64(c.CreationFee))),
		stackitem.NewBigInteger(big.NewInt(int64(c.InvestmentFee))),
		stackitem.NewBigInteger(big.NewInt(int64(c.WithdrawalPenalty))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinVotingPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinInvestmentPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinMaturityPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MaxViolationsBeforeBan))),
		stackitem.NewBigInteger(big.NewInt(int64(c.ViolationCooldown))),
		stackitem.NewBigInteger(big.NewInt(int64(c.CommitRevealDelay))),
		stackitem.NewBigInteger(big.NewInt(int64(c.CommitRevealWindow))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (c *CollocatioConfig) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 16 {
		return fmt.Errorf("wrong number of elements: expected 16, got %d", len(items))
	}

	minPIO, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minPIOParticipants: %w", err)
	}
	c.MinPIOParticipants = minPIO.Uint64()

	minEIO, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minEIOParticipants: %w", err)
	}
	c.MinEIOParticipants = minEIO.Uint64()

	minCIO, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minCIOParticipants: %w", err)
	}
	c.MinCIOParticipants = minCIO.Uint64()

	defaultMin, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid defaultMinInvestment: %w", err)
	}
	c.DefaultMinInvestment = defaultMin.Uint64()

	maxCap, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxIndividualCap: %w", err)
	}
	c.MaxIndividualCap = maxCap.Uint64()

	wealthConc, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid wealthConcentration: %w", err)
	}
	c.WealthConcentration = wealthConc.Uint64()

	creationFee, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid creationFee: %w", err)
	}
	c.CreationFee = creationFee.Uint64()

	investmentFee, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid investmentFee: %w", err)
	}
	c.InvestmentFee = investmentFee.Uint64()

	withdrawalPenalty, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid withdrawalPenalty: %w", err)
	}
	c.WithdrawalPenalty = withdrawalPenalty.Uint64()

	minVoting, err := items[9].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minVotingPeriod: %w", err)
	}
	c.MinVotingPeriod = uint32(minVoting.Uint64())

	minInvestment, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minInvestmentPeriod: %w", err)
	}
	c.MinInvestmentPeriod = uint32(minInvestment.Uint64())

	minMaturity, err := items[11].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minMaturityPeriod: %w", err)
	}
	c.MinMaturityPeriod = uint32(minMaturity.Uint64())

	maxViolations, err := items[12].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxViolationsBeforeBan: %w", err)
	}
	c.MaxViolationsBeforeBan = uint8(maxViolations.Uint64())

	cooldown, err := items[13].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid violationCooldown: %w", err)
	}
	c.ViolationCooldown = uint32(cooldown.Uint64())

	commitDelay, err := items[14].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid commitRevealDelay: %w", err)
	}
	c.CommitRevealDelay = uint32(commitDelay.Uint64())

	commitWindow, err := items[15].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid commitRevealWindow: %w", err)
	}
	c.CommitRevealWindow = uint32(commitWindow.Uint64())

	return nil
}

// DefaultCollocatioConfig returns the default configuration.
func DefaultCollocatioConfig() CollocatioConfig {
	return CollocatioConfig{
		MinPIOParticipants:     100,
		MinEIOParticipants:     10,
		MinCIOParticipants:     25,
		DefaultMinInvestment:   100_00000000, // 100 VTS
		MaxIndividualCap:       1_000_000_00000000, // 1M VTS
		WealthConcentration:    500, // 5%
		CreationFee:            1000_00000000, // 1000 VTS
		InvestmentFee:          50,  // 0.5%
		WithdrawalPenalty:      200, // 2%
		MinVotingPeriod:        10000,  // ~10000 blocks
		MinInvestmentPeriod:    20000,  // ~20000 blocks
		MinMaturityPeriod:      50000,  // ~50000 blocks
		MaxViolationsBeforeBan: 3,
		ViolationCooldown:      1000000, // ~1M blocks
		CommitRevealDelay:      10,      // ~10 blocks minimum between commit and reveal
		CommitRevealWindow:     1000,    // ~1000 blocks to reveal after delay
	}
}

// CommitmentStatus represents the status of an investment commitment.
type CommitmentStatus uint8

// Commitment statuses.
const (
	CommitmentPending  CommitmentStatus = 0 // Awaiting reveal
	CommitmentRevealed CommitmentStatus = 1 // Successfully revealed and invested
	CommitmentExpired  CommitmentStatus = 2 // Expired without reveal
	CommitmentCanceled CommitmentStatus = 3 // Canceled by investor
)

// InvestmentCommitment represents a commit-reveal commitment for investment.
// This prevents front-running attacks by hiding the investment amount until reveal.
type InvestmentCommitment struct {
	ID            uint64           // Unique commitment ID
	OpportunityID uint64           // Opportunity being invested in
	VitaID        uint64           // Investor's Vita ID
	Investor      util.Uint160     // Investor's address
	Commitment    util.Uint256     // hash(amount || nonce || investor)
	Status        CommitmentStatus // Current status
	CommittedAt   uint32           // Block height when committed
	RevealDeadline uint32          // Block height by which reveal must occur
	RevealedAmount uint64          // Amount revealed (0 until revealed)
	InvestmentID  uint64           // Resulting investment ID (0 until revealed)
}

// DecodeBinary implements the io.Serializable interface.
func (c *InvestmentCommitment) DecodeBinary(br *io.BinReader) {
	c.ID = br.ReadU64LE()
	c.OpportunityID = br.ReadU64LE()
	c.VitaID = br.ReadU64LE()
	br.ReadBytes(c.Investor[:])
	br.ReadBytes(c.Commitment[:])
	c.Status = CommitmentStatus(br.ReadB())
	c.CommittedAt = br.ReadU32LE()
	c.RevealDeadline = br.ReadU32LE()
	c.RevealedAmount = br.ReadU64LE()
	c.InvestmentID = br.ReadU64LE()
}

// EncodeBinary implements the io.Serializable interface.
func (c *InvestmentCommitment) EncodeBinary(bw *io.BinWriter) {
	bw.WriteU64LE(c.ID)
	bw.WriteU64LE(c.OpportunityID)
	bw.WriteU64LE(c.VitaID)
	bw.WriteBytes(c.Investor[:])
	bw.WriteBytes(c.Commitment[:])
	bw.WriteB(byte(c.Status))
	bw.WriteU32LE(c.CommittedAt)
	bw.WriteU32LE(c.RevealDeadline)
	bw.WriteU64LE(c.RevealedAmount)
	bw.WriteU64LE(c.InvestmentID)
}

// ToStackItem implements stackitem.Convertible interface.
func (c *InvestmentCommitment) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(c.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(c.OpportunityID))),
		stackitem.NewBigInteger(big.NewInt(int64(c.VitaID))),
		stackitem.NewByteArray(c.Investor.BytesBE()),
		stackitem.NewByteArray(c.Commitment.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(c.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(c.CommittedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(c.RevealDeadline))),
		stackitem.NewBigInteger(big.NewInt(int64(c.RevealedAmount))),
		stackitem.NewBigInteger(big.NewInt(int64(c.InvestmentID))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (c *InvestmentCommitment) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 10 {
		return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	c.ID = id.Uint64()

	oppID, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid opportunityID: %w", err)
	}
	c.OpportunityID = oppID.Uint64()

	vitaID, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	c.VitaID = vitaID.Uint64()

	investor, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid investor: %w", err)
	}
	c.Investor, err = util.Uint160DecodeBytesBE(investor)
	if err != nil {
		return fmt.Errorf("invalid investor address: %w", err)
	}

	commitment, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid commitment: %w", err)
	}
	c.Commitment, err = util.Uint256DecodeBytesBE(commitment)
	if err != nil {
		return fmt.Errorf("invalid commitment hash: %w", err)
	}

	status, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid status: %w", err)
	}
	c.Status = CommitmentStatus(status.Uint64())

	committedAt, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid committedAt: %w", err)
	}
	c.CommittedAt = uint32(committedAt.Uint64())

	revealDeadline, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid revealDeadline: %w", err)
	}
	c.RevealDeadline = uint32(revealDeadline.Uint64())

	revealedAmount, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid revealedAmount: %w", err)
	}
	c.RevealedAmount = revealedAmount.Uint64()

	investmentID, err := items[9].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid investmentID: %w", err)
	}
	c.InvestmentID = investmentID.Uint64()

	return nil
}
