package state

import (
	"errors"
	"math/big"

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

// VelocityAccountStatus represents the velocity tracking account status.
type VelocityAccountStatus uint8

const (
	// VelocityAccountActive indicates the account is actively tracked.
	VelocityAccountActive VelocityAccountStatus = 0
	// VelocityAccountExempt indicates the account is exempt from tribute.
	VelocityAccountExempt VelocityAccountStatus = 1
	// VelocityAccountSuspended indicates velocity tracking is suspended.
	VelocityAccountSuspended VelocityAccountStatus = 2
)

// AssessmentStatus represents the status of a tribute assessment.
type AssessmentStatus uint8

const (
	// AssessmentPending indicates the assessment is pending collection.
	AssessmentPending AssessmentStatus = 0
	// AssessmentCollected indicates the tribute has been collected.
	AssessmentCollected AssessmentStatus = 1
	// AssessmentWaived indicates the assessment was waived.
	AssessmentWaived AssessmentStatus = 2
	// AssessmentAppealed indicates the assessment is under appeal.
	AssessmentAppealed AssessmentStatus = 3
)

// HoardingLevel represents the severity of resource hoarding.
type HoardingLevel uint8

const (
	// HoardingNone indicates no hoarding detected.
	HoardingNone HoardingLevel = 0
	// HoardingMild indicates mild hoarding (below normal velocity).
	HoardingMild HoardingLevel = 1
	// HoardingModerate indicates moderate hoarding.
	HoardingModerate HoardingLevel = 2
	// HoardingSevere indicates severe hoarding.
	HoardingSevere HoardingLevel = 3
	// HoardingExtreme indicates extreme hoarding (stagnant resources).
	HoardingExtreme HoardingLevel = 4
)

// IncentiveType represents types of circulation incentives.
type IncentiveType uint8

const (
	// IncentiveVelocityBonus rewards high resource velocity.
	IncentiveVelocityBonus IncentiveType = 0
	// IncentiveProductiveUse rewards productive resource use.
	IncentiveProductiveUse IncentiveType = 1
	// IncentiveCommunitySupport rewards community contributions.
	IncentiveCommunitySupport IncentiveType = 2
	// IncentiveEducationSpending rewards education investment.
	IncentiveEducationSpending IncentiveType = 3
	// IncentiveHealthcareSpending rewards healthcare investment.
	IncentiveHealthcareSpending IncentiveType = 4
)

// VelocityAccount tracks resource velocity for a Vita holder.
type VelocityAccount struct {
	VitaID              uint64                // Owner's Vita token ID
	Owner               util.Uint160          // Owner's address
	CurrentVelocity     uint64                // Current velocity score (0-10000 basis points)
	AverageVelocity     uint64                // Rolling average velocity
	LastActivityBlock   uint32                // Last transaction block
	TotalInflow         uint64                // Total resources received
	TotalOutflow        uint64                // Total resources spent/transferred
	StagnantBalance     uint64                // Balance considered stagnant
	HoardingLevel       HoardingLevel         // Current hoarding assessment
	ExemptionReason     string                // Reason for exemption (if any)
	TotalTributePaid    uint64                // Lifetime tribute paid
	TotalIncentivesRcvd uint64                // Lifetime incentives received
	Status              VelocityAccountStatus // Account status
	CreatedAt           uint32                // Block height when created
	UpdatedAt           uint32                // Last update block
}

// ToStackItem implements stackitem.Convertible.
func (a *VelocityAccount) ToStackItem() stackitem.Item {
	return stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)),
		stackitem.NewByteArray(a.Owner.BytesBE()),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.CurrentVelocity)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.AverageVelocity)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.LastActivityBlock))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalInflow)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalOutflow)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantBalance)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))),
		stackitem.NewByteArray([]byte(a.ExemptionReason)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalTributePaid)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalIncentivesRcvd)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CreatedAt))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.UpdatedAt))),
	})
}

// FromStackItem implements stackitem.Convertible.
func (a *VelocityAccount) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok || len(arr) < 15 {
		return errors.New("not a struct")
	}
	vitaID, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	a.VitaID = vitaID.Uint64()

	ownerBytes, err := arr[1].TryBytes()
	if err != nil {
		return err
	}
	a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes)
	if err != nil {
		return err
	}

	currentVel, err := arr[2].TryInteger()
	if err != nil {
		return err
	}
	a.CurrentVelocity = currentVel.Uint64()

	avgVel, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	a.AverageVelocity = avgVel.Uint64()

	lastActivity, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	a.LastActivityBlock = uint32(lastActivity.Uint64())

	totalIn, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	a.TotalInflow = totalIn.Uint64()

	totalOut, err := arr[6].TryInteger()
	if err != nil {
		return err
	}
	a.TotalOutflow = totalOut.Uint64()

	stagnant, err := arr[7].TryInteger()
	if err != nil {
		return err
	}
	a.StagnantBalance = stagnant.Uint64()

	hoardLevel, err := arr[8].TryInteger()
	if err != nil {
		return err
	}
	a.HoardingLevel = HoardingLevel(hoardLevel.Uint64())

	exemptReason, err := arr[9].TryBytes()
	if err != nil {
		return err
	}
	a.ExemptionReason = string(exemptReason)

	totalTrib, err := arr[10].TryInteger()
	if err != nil {
		return err
	}
	a.TotalTributePaid = totalTrib.Uint64()

	totalInc, err := arr[11].TryInteger()
	if err != nil {
		return err
	}
	a.TotalIncentivesRcvd = totalInc.Uint64()

	status, err := arr[12].TryInteger()
	if err != nil {
		return err
	}
	a.Status = VelocityAccountStatus(status.Uint64())

	created, err := arr[13].TryInteger()
	if err != nil {
		return err
	}
	a.CreatedAt = uint32(created.Uint64())

	updated, err := arr[14].TryInteger()
	if err != nil {
		return err
	}
	a.UpdatedAt = uint32(updated.Uint64())

	return nil
}

// TributeAssessment represents a hoarding tribute assessment.
type TributeAssessment struct {
	ID              uint64           // Unique assessment ID
	VitaID          uint64           // Owner's Vita ID
	Owner           util.Uint160     // Owner's address
	AssessmentBlock uint32           // Block when assessment made
	HoardingLevel   HoardingLevel    // Hoarding level at assessment
	StagnantAmount  uint64           // Amount considered stagnant
	TributeRate     uint64           // Rate applied (basis points)
	TributeAmount   uint64           // Tribute amount due
	DueBlock        uint32           // Block by which tribute is due
	CollectedBlock  uint32           // Block when collected (0 if not)
	Status          AssessmentStatus // Assessment status
	AppealReason    string           // Reason for appeal (if any)
}

// ToStackItem implements stackitem.Convertible.
func (a *TributeAssessment) ToStackItem() stackitem.Item {
	return stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.ID)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)),
		stackitem.NewByteArray(a.Owner.BytesBE()),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.AssessmentBlock))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantAmount)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeRate)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeAmount)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.DueBlock))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CollectedBlock))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))),
		stackitem.NewByteArray([]byte(a.AppealReason)),
	})
}

// FromStackItem implements stackitem.Convertible.
func (a *TributeAssessment) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok || len(arr) < 12 {
		return errors.New("not a struct")
	}

	id, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	a.ID = id.Uint64()

	vitaID, err := arr[1].TryInteger()
	if err != nil {
		return err
	}
	a.VitaID = vitaID.Uint64()

	ownerBytes, err := arr[2].TryBytes()
	if err != nil {
		return err
	}
	a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes)
	if err != nil {
		return err
	}

	assessBlock, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	a.AssessmentBlock = uint32(assessBlock.Uint64())

	hoardLevel, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	a.HoardingLevel = HoardingLevel(hoardLevel.Uint64())

	stagnant, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	a.StagnantAmount = stagnant.Uint64()

	rate, err := arr[6].TryInteger()
	if err != nil {
		return err
	}
	a.TributeRate = rate.Uint64()

	amount, err := arr[7].TryInteger()
	if err != nil {
		return err
	}
	a.TributeAmount = amount.Uint64()

	due, err := arr[8].TryInteger()
	if err != nil {
		return err
	}
	a.DueBlock = uint32(due.Uint64())

	collected, err := arr[9].TryInteger()
	if err != nil {
		return err
	}
	a.CollectedBlock = uint32(collected.Uint64())

	status, err := arr[10].TryInteger()
	if err != nil {
		return err
	}
	a.Status = AssessmentStatus(status.Uint64())

	appealReason, err := arr[11].TryBytes()
	if err != nil {
		return err
	}
	a.AppealReason = string(appealReason)

	return nil
}

// CirculationIncentive represents a reward for resource circulation.
type CirculationIncentive struct {
	ID            uint64        // Unique incentive ID
	VitaID        uint64        // Recipient's Vita ID
	Recipient     util.Uint160  // Recipient's address
	IncentiveType IncentiveType // Type of incentive
	Amount        uint64        // Incentive amount
	Reason        string        // Reason for incentive
	VelocityScore uint64        // Velocity score that triggered incentive
	GrantedBlock  uint32        // Block when granted
	ClaimedBlock  uint32        // Block when claimed (0 if not)
	Claimed       bool          // Whether incentive was claimed
}

// ToStackItem implements stackitem.Convertible.
func (i *CirculationIncentive) ToStackItem() stackitem.Item {
	return stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(new(big.Int).SetUint64(i.ID)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(i.VitaID)),
		stackitem.NewByteArray(i.Recipient.BytesBE()),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.IncentiveType))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(i.Amount)),
		stackitem.NewByteArray([]byte(i.Reason)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(i.VelocityScore)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.GrantedBlock))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.ClaimedBlock))),
		stackitem.NewBool(i.Claimed),
	})
}

// FromStackItem implements stackitem.Convertible.
func (i *CirculationIncentive) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok || len(arr) < 10 {
		return errors.New("not a struct")
	}

	id, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	i.ID = id.Uint64()

	vitaID, err := arr[1].TryInteger()
	if err != nil {
		return err
	}
	i.VitaID = vitaID.Uint64()

	recipBytes, err := arr[2].TryBytes()
	if err != nil {
		return err
	}
	i.Recipient, err = util.Uint160DecodeBytesBE(recipBytes)
	if err != nil {
		return err
	}

	incType, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	i.IncentiveType = IncentiveType(incType.Uint64())

	amount, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	i.Amount = amount.Uint64()

	reason, err := arr[5].TryBytes()
	if err != nil {
		return err
	}
	i.Reason = string(reason)

	velScore, err := arr[6].TryInteger()
	if err != nil {
		return err
	}
	i.VelocityScore = velScore.Uint64()

	granted, err := arr[7].TryInteger()
	if err != nil {
		return err
	}
	i.GrantedBlock = uint32(granted.Uint64())

	claimed, err := arr[8].TryInteger()
	if err != nil {
		return err
	}
	i.ClaimedBlock = uint32(claimed.Uint64())

	claimedBool, err := arr[9].TryBool()
	if err != nil {
		return err
	}
	i.Claimed = claimedBool

	return nil
}

// RedistributionRecord tracks wealth redistribution events.
type RedistributionRecord struct {
	ID               uint64       // Unique record ID
	SourceAssessment uint64       // Source assessment ID
	TotalAmount      uint64       // Total amount redistributed
	RecipientCount   uint64       // Number of recipients
	PerCapitaAmount  uint64       // Amount per recipient
	RedistBlock      uint32       // Block when redistribution occurred
	TargetCategory   string       // Category of recipients (e.g., "low_velocity", "all_citizens")
}

// ToStackItem implements stackitem.Convertible.
func (r *RedistributionRecord) ToStackItem() stackitem.Item {
	return stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(new(big.Int).SetUint64(r.ID)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(r.SourceAssessment)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(r.TotalAmount)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(r.RecipientCount)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(r.PerCapitaAmount)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(r.RedistBlock))),
		stackitem.NewByteArray([]byte(r.TargetCategory)),
	})
}

// FromStackItem implements stackitem.Convertible.
func (r *RedistributionRecord) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok || len(arr) < 7 {
		return errors.New("not a struct")
	}

	id, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	r.ID = id.Uint64()

	sourceAssess, err := arr[1].TryInteger()
	if err != nil {
		return err
	}
	r.SourceAssessment = sourceAssess.Uint64()

	total, err := arr[2].TryInteger()
	if err != nil {
		return err
	}
	r.TotalAmount = total.Uint64()

	recipCount, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	r.RecipientCount = recipCount.Uint64()

	perCapita, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	r.PerCapitaAmount = perCapita.Uint64()

	redistBlock, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	r.RedistBlock = uint32(redistBlock.Uint64())

	targetCat, err := arr[6].TryBytes()
	if err != nil {
		return err
	}
	r.TargetCategory = string(targetCat)

	return nil
}

// TributeConfig holds configurable parameters for the Tribute system.
type TributeConfig struct {
	VelocityThresholdMild     uint64 // Velocity below this = mild hoarding (basis points)
	VelocityThresholdModerate uint64 // Velocity below this = moderate hoarding
	VelocityThresholdSevere   uint64 // Velocity below this = severe hoarding
	VelocityThresholdExtreme  uint64 // Velocity below this = extreme hoarding
	TributeRateMild           uint64 // Tribute rate for mild hoarding (basis points)
	TributeRateModerate       uint64 // Tribute rate for moderate hoarding
	TributeRateSevere         uint64 // Tribute rate for severe hoarding
	TributeRateExtreme        uint64 // Tribute rate for extreme hoarding
	IncentiveRateHigh         uint64 // Incentive rate for high velocity
	IncentiveRateVeryHigh     uint64 // Incentive rate for very high velocity
	StagnancyPeriod           uint32 // Blocks before balance considered stagnant
	AssessmentPeriod          uint32 // Blocks between assessments
	GracePeriod               uint32 // Blocks before tribute due after assessment
	MinBalanceForTribute      uint64 // Minimum balance to assess tribute
	ExemptionThreshold        uint64 // Balance below which exempt from tribute
}

// ToStackItem implements stackitem.Convertible.
func (c *TributeConfig) ToStackItem() stackitem.Item {
	return stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdMild)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdModerate)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdSevere)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdExtreme)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateMild)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateModerate)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateSevere)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateExtreme)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateHigh)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateVeryHigh)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.StagnancyPeriod))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.AssessmentPeriod))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.GracePeriod))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.MinBalanceForTribute)),
		stackitem.NewBigInteger(new(big.Int).SetUint64(c.ExemptionThreshold)),
	})
}

// FromStackItem implements stackitem.Convertible.
func (c *TributeConfig) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok || len(arr) < 15 {
		return errors.New("not a struct")
	}

	threshMild, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	c.VelocityThresholdMild = threshMild.Uint64()

	threshMod, err := arr[1].TryInteger()
	if err != nil {
		return err
	}
	c.VelocityThresholdModerate = threshMod.Uint64()

	threshSev, err := arr[2].TryInteger()
	if err != nil {
		return err
	}
	c.VelocityThresholdSevere = threshSev.Uint64()

	threshExt, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	c.VelocityThresholdExtreme = threshExt.Uint64()

	rateMild, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	c.TributeRateMild = rateMild.Uint64()

	rateMod, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	c.TributeRateModerate = rateMod.Uint64()

	rateSev, err := arr[6].TryInteger()
	if err != nil {
		return err
	}
	c.TributeRateSevere = rateSev.Uint64()

	rateExt, err := arr[7].TryInteger()
	if err != nil {
		return err
	}
	c.TributeRateExtreme = rateExt.Uint64()

	incHigh, err := arr[8].TryInteger()
	if err != nil {
		return err
	}
	c.IncentiveRateHigh = incHigh.Uint64()

	incVeryHigh, err := arr[9].TryInteger()
	if err != nil {
		return err
	}
	c.IncentiveRateVeryHigh = incVeryHigh.Uint64()

	stagnancy, err := arr[10].TryInteger()
	if err != nil {
		return err
	}
	c.StagnancyPeriod = uint32(stagnancy.Uint64())

	assessPeriod, err := arr[11].TryInteger()
	if err != nil {
		return err
	}
	c.AssessmentPeriod = uint32(assessPeriod.Uint64())

	grace, err := arr[12].TryInteger()
	if err != nil {
		return err
	}
	c.GracePeriod = uint32(grace.Uint64())

	minBal, err := arr[13].TryInteger()
	if err != nil {
		return err
	}
	c.MinBalanceForTribute = minBal.Uint64()

	exemptThresh, err := arr[14].TryInteger()
	if err != nil {
		return err
	}
	c.ExemptionThreshold = exemptThresh.Uint64()

	return nil
}
