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"
)

// ProposalCategory represents the type of proposal.
type ProposalCategory uint8

// Proposal categories.
const (
	ProposalCategoryLawAmendment     ProposalCategory = 1 // Lex integration
	ProposalCategoryInvestment       ProposalCategory = 2 // PIO/EIO/CIO (future)
	ProposalCategoryGovernanceAction ProposalCategory = 3 // Committee/policy changes
	ProposalCategoryConstitutional   ProposalCategory = 4 // Supermajority required
	ProposalCategoryReferendum       ProposalCategory = 5 // Citizen-initiated
)

// ProposalStatus represents the lifecycle status of a proposal.
type ProposalStatus uint8

// Proposal statuses.
const (
	ProposalStatusDraft     ProposalStatus = 0 // Not yet open for voting
	ProposalStatusActive    ProposalStatus = 1 // Open for voting
	ProposalStatusPassed    ProposalStatus = 2 // Met threshold, awaiting execution
	ProposalStatusRejected  ProposalStatus = 3 // Failed to meet threshold
	ProposalStatusExecuted  ProposalStatus = 4 // Successfully executed
	ProposalStatusCancelled ProposalStatus = 5 // Cancelled by proposer/committee
	ProposalStatusExpired   ProposalStatus = 6 // Voting period ended without quorum
)

// VoteChoice represents a voting decision.
type VoteChoice uint8

// Vote choices.
const (
	VoteChoiceAbstain VoteChoice = 0
	VoteChoiceYes     VoteChoice = 1
	VoteChoiceNo      VoteChoice = 2
)

// Proposal represents a democratic proposal in the Eligere voting system.
type Proposal struct {
	ID             uint64           // Unique proposal ID
	Title          string           // Short title (max 128 chars)
	ContentHash    util.Uint256     // Hash of full proposal content (stored off-chain)
	Category       ProposalCategory // Type of proposal
	Proposer       util.Uint160     // Who created the proposal
	ProposerVitaID uint64           // Vita ID of proposer

	// Timing (block heights)
	CreatedAt      uint32 // When created
	VotingStartsAt uint32 // When voting opens
	VotingEndsAt   uint32 // elegireDeadline - when voting closes
	ExecutionDelay uint32 // Blocks after passing before execution allowed

	// Thresholds
	QuorumPercent    uint8 // Minimum participation percentage (e.g., 10%)
	ThresholdPercent uint8 // Required yes votes percentage (50% or 67%)

	// Results
	Status       ProposalStatus
	TotalVotes   uint64 // Total votes cast
	YesVotes     uint64 // Votes in favor
	NoVotes      uint64 // Votes against
	AbstainVotes uint64 // Abstentions (count toward quorum)

	// Execution target (for contract integrations like Lex)
	TargetContract util.Uint160 // Contract to call on execution
	TargetMethod   string       // Method to call
	TargetParams   []byte       // Serialized parameters

	// Execution tracking
	ExecutedAt uint32       // When executed (0 if not)
	ExecutedBy util.Uint160 // Who executed
}

// ToStackItem implements stackitem.Convertible interface.
func (p *Proposal) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(p.ID))),
		stackitem.NewByteArray([]byte(p.Title)),
		stackitem.NewByteArray(p.ContentHash[:]),
		stackitem.NewBigInteger(big.NewInt(int64(p.Category))),
		stackitem.NewByteArray(p.Proposer.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(p.ProposerVitaID))),
		stackitem.NewBigInteger(big.NewInt(int64(p.CreatedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(p.VotingStartsAt))),
		stackitem.NewBigInteger(big.NewInt(int64(p.VotingEndsAt))),
		stackitem.NewBigInteger(big.NewInt(int64(p.ExecutionDelay))),
		stackitem.NewBigInteger(big.NewInt(int64(p.QuorumPercent))),
		stackitem.NewBigInteger(big.NewInt(int64(p.ThresholdPercent))),
		stackitem.NewBigInteger(big.NewInt(int64(p.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(p.TotalVotes))),
		stackitem.NewBigInteger(big.NewInt(int64(p.YesVotes))),
		stackitem.NewBigInteger(big.NewInt(int64(p.NoVotes))),
		stackitem.NewBigInteger(big.NewInt(int64(p.AbstainVotes))),
		stackitem.NewByteArray(p.TargetContract.BytesBE()),
		stackitem.NewByteArray([]byte(p.TargetMethod)),
		stackitem.NewByteArray(p.TargetParams),
		stackitem.NewBigInteger(big.NewInt(int64(p.ExecutedAt))),
		stackitem.NewByteArray(p.ExecutedBy.BytesBE()),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (p *Proposal) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(arr) < 22 {
		return errors.New("invalid proposal struct length")
	}

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

	titleBytes, err := arr[1].TryBytes()
	if err != nil {
		return err
	}
	p.Title = string(titleBytes)

	contentHashBytes, err := arr[2].TryBytes()
	if err != nil {
		return err
	}
	if len(contentHashBytes) == 32 {
		copy(p.ContentHash[:], contentHashBytes)
	}

	category, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	p.Category = ProposalCategory(category.Uint64())

	proposerBytes, err := arr[4].TryBytes()
	if err != nil {
		return err
	}
	p.Proposer, _ = util.Uint160DecodeBytesBE(proposerBytes)

	proposerVitaID, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	p.ProposerVitaID = proposerVitaID.Uint64()

	createdAt, err := arr[6].TryInteger()
	if err != nil {
		return err
	}
	p.CreatedAt = uint32(createdAt.Uint64())

	votingStartsAt, err := arr[7].TryInteger()
	if err != nil {
		return err
	}
	p.VotingStartsAt = uint32(votingStartsAt.Uint64())

	votingEndsAt, err := arr[8].TryInteger()
	if err != nil {
		return err
	}
	p.VotingEndsAt = uint32(votingEndsAt.Uint64())

	executionDelay, err := arr[9].TryInteger()
	if err != nil {
		return err
	}
	p.ExecutionDelay = uint32(executionDelay.Uint64())

	quorumPercent, err := arr[10].TryInteger()
	if err != nil {
		return err
	}
	p.QuorumPercent = uint8(quorumPercent.Uint64())

	thresholdPercent, err := arr[11].TryInteger()
	if err != nil {
		return err
	}
	p.ThresholdPercent = uint8(thresholdPercent.Uint64())

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

	totalVotes, err := arr[13].TryInteger()
	if err != nil {
		return err
	}
	p.TotalVotes = totalVotes.Uint64()

	yesVotes, err := arr[14].TryInteger()
	if err != nil {
		return err
	}
	p.YesVotes = yesVotes.Uint64()

	noVotes, err := arr[15].TryInteger()
	if err != nil {
		return err
	}
	p.NoVotes = noVotes.Uint64()

	abstainVotes, err := arr[16].TryInteger()
	if err != nil {
		return err
	}
	p.AbstainVotes = abstainVotes.Uint64()

	targetContractBytes, err := arr[17].TryBytes()
	if err != nil {
		return err
	}
	p.TargetContract, _ = util.Uint160DecodeBytesBE(targetContractBytes)

	targetMethodBytes, err := arr[18].TryBytes()
	if err != nil {
		return err
	}
	p.TargetMethod = string(targetMethodBytes)

	targetParams, err := arr[19].TryBytes()
	if err != nil {
		return err
	}
	p.TargetParams = targetParams

	executedAt, err := arr[20].TryInteger()
	if err != nil {
		return err
	}
	p.ExecutedAt = uint32(executedAt.Uint64())

	executedByBytes, err := arr[21].TryBytes()
	if err != nil {
		return err
	}
	p.ExecutedBy, _ = util.Uint160DecodeBytesBE(executedByBytes)

	return nil
}

// Vote represents a single vote record in the Eligere voting system.
type Vote struct {
	ProposalID  uint64       // Which proposal
	VoterVitaID uint64       // Vita ID of voter (ensures one-person-one-vote)
	Voter       util.Uint160 // Voter's address
	Choice      VoteChoice   // How they voted
	VotedAt     uint32       // Block height when voted
	Weight      uint64       // Vote weight (1 for equal voting)
}

// ToStackItem implements stackitem.Convertible interface.
func (v *Vote) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(v.ProposalID))),
		stackitem.NewBigInteger(big.NewInt(int64(v.VoterVitaID))),
		stackitem.NewByteArray(v.Voter.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(v.Choice))),
		stackitem.NewBigInteger(big.NewInt(int64(v.VotedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(v.Weight))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (v *Vote) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(arr) < 6 {
		return errors.New("invalid vote struct length")
	}

	proposalID, err := arr[0].TryInteger()
	if err != nil {
		return err
	}
	v.ProposalID = proposalID.Uint64()

	voterVitaID, err := arr[1].TryInteger()
	if err != nil {
		return err
	}
	v.VoterVitaID = voterVitaID.Uint64()

	voterBytes, err := arr[2].TryBytes()
	if err != nil {
		return err
	}
	v.Voter, _ = util.Uint160DecodeBytesBE(voterBytes)

	choice, err := arr[3].TryInteger()
	if err != nil {
		return err
	}
	v.Choice = VoteChoice(choice.Uint64())

	votedAt, err := arr[4].TryInteger()
	if err != nil {
		return err
	}
	v.VotedAt = uint32(votedAt.Uint64())

	weight, err := arr[5].TryInteger()
	if err != nil {
		return err
	}
	v.Weight = weight.Uint64()

	return nil
}

// EligereConfig represents configurable parameters for the Eligere voting system.
type EligereConfig struct {
	DefaultVotingPeriod     uint32 // Default blocks for voting (~7 days at 1s blocks = 604800)
	MinVotingPeriod         uint32 // Minimum blocks (~1 day = 86400)
	MaxVotingPeriod         uint32 // Maximum blocks (~30 days = 2592000)
	DefaultExecutionDelay   uint32 // Blocks between passing and execution (~1 day = 86400)
	DefaultQuorum           uint8  // Default quorum percentage (e.g., 10%)
	ConstitutionalThreshold uint8  // Supermajority threshold (e.g., 67%)
	StandardThreshold       uint8  // Simple majority (e.g., 51%)
}

// ToStackItem implements stackitem.Convertible interface.
func (c *EligereConfig) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(c.DefaultVotingPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MinVotingPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MaxVotingPeriod))),
		stackitem.NewBigInteger(big.NewInt(int64(c.DefaultExecutionDelay))),
		stackitem.NewBigInteger(big.NewInt(int64(c.DefaultQuorum))),
		stackitem.NewBigInteger(big.NewInt(int64(c.ConstitutionalThreshold))),
		stackitem.NewBigInteger(big.NewInt(int64(c.StandardThreshold))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (c *EligereConfig) FromStackItem(item stackitem.Item) error {
	arr, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(arr) < 7 {
		return errors.New("invalid config struct length")
	}

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

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

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

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

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

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

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

	return nil
}

// DefaultEligereConfig returns the default configuration for Eligere.
func DefaultEligereConfig() *EligereConfig {
	return &EligereConfig{
		DefaultVotingPeriod:     604800,  // ~7 days at 1 block/second
		MinVotingPeriod:         86400,   // ~1 day minimum
		MaxVotingPeriod:         2592000, // ~30 days maximum
		DefaultExecutionDelay:   86400,   // ~1 day delay after passing
		DefaultQuorum:           10,      // 10% participation required
		ConstitutionalThreshold: 67,      // 67% supermajority for constitutional
		StandardThreshold:       51,      // 51% simple majority
	}
}
