package state

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

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

// LawCategory represents the hierarchical category of a law.
type LawCategory uint8

const (
	// LawCategoryConstitutional represents immutable constitutional rights.
	LawCategoryConstitutional LawCategory = 1
	// LawCategoryFederal represents national/federal laws.
	LawCategoryFederal LawCategory = 2
	// LawCategoryRegional represents state/province laws.
	LawCategoryRegional LawCategory = 3
	// LawCategoryLocal represents municipal/local laws.
	LawCategoryLocal LawCategory = 4
	// LawCategoryAdministrative represents regulations and administrative rules.
	LawCategoryAdministrative LawCategory = 5
)

// LawStatus represents the current status of a law.
type LawStatus uint8

const (
	// LawStatusDraft indicates a proposed law not yet voted.
	LawStatusDraft LawStatus = 0
	// LawStatusActive indicates a law currently in force.
	LawStatusActive LawStatus = 1
	// LawStatusSuspended indicates a temporarily suspended law.
	LawStatusSuspended LawStatus = 2
	// LawStatusRepealed indicates a permanently removed law.
	LawStatusRepealed LawStatus = 3
	// LawStatusSuperseded indicates a law replaced by a newer one.
	LawStatusSuperseded LawStatus = 4
)

// EnforcementType specifies how a law is enforced.
type EnforcementType uint8

const (
	// EnforcementAutomatic means smart contracts block violations.
	EnforcementAutomatic EnforcementType = 1
	// EnforcementLogging means violations emit events but allow transaction.
	EnforcementLogging EnforcementType = 2
	// EnforcementAdvisory means non-binding guidance only.
	EnforcementAdvisory EnforcementType = 3
)

// RestrictionType represents the type of rights restriction.
type RestrictionType uint8

const (
	// RestrictionSuspend temporarily suspends a right.
	RestrictionSuspend RestrictionType = 1
	// RestrictionLimit limits a right (e.g., movement within area).
	RestrictionLimit RestrictionType = 2
	// RestrictionCondition makes a right conditional (e.g., probation).
	RestrictionCondition RestrictionType = 3
)

// ConstitutionalRight IDs - these are immutable and built into the protocol.
const (
	RightLife       uint64 = 1  // Right to life
	RightLiberty    uint64 = 2  // Freedom from arbitrary detention
	RightProperty   uint64 = 3  // Right to own and transfer assets
	RightEquality   uint64 = 4  // Equal treatment under law
	RightDueProcess uint64 = 5  // Fair legal proceedings
	RightPrivacy    uint64 = 6  // Privacy of personal data
	RightExpression uint64 = 7  // Freedom of expression
	RightAssembly   uint64 = 8  // Freedom of assembly
	RightMovement   uint64 = 9  // Freedom of movement
	RightEducation  uint64 = 10 // Right to education
	RightHealthcare uint64 = 11 // Right to healthcare
	RightLabor      uint64 = 12 // Right to fair labor
	RightVote       uint64 = 13 // Right to vote
	RightAsylum     uint64 = 14 // Right to seek asylum

	// MaxConstitutionalRight is the highest constitutional right ID.
	MaxConstitutionalRight uint64 = 14
)

// Law represents a law in the registry.
type Law struct {
	ID           uint64          // Unique law identifier
	Name         string          // Human-readable name
	ContentHash  util.Uint256    // Hash of full law text (stored off-chain)
	Category     LawCategory     // Constitutional, Federal, Regional, Local
	Jurisdiction uint32          // Jurisdiction ID (0 = global)
	ParentID     uint64          // Parent law in hierarchy (0 = root)
	EffectiveAt  uint32          // Block height when law becomes active
	ExpiresAt    uint32          // Block height when law expires (0 = perpetual)
	EnactedAt    uint32          // Block height of enactment
	EnactedBy    util.Uint160    // Authority that enacted
	Status       LawStatus       // Active, Suspended, Repealed, Superseded
	SupersededBy uint64          // ID of law that replaced this one
	RequiresVita bool            // Whether law applies only to Vita holders
	Enforcement  EnforcementType // How law is enforced
}

// ToStackItem implements stackitem.Convertible interface.
func (l *Law) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(l.ID))),
		stackitem.NewByteArray([]byte(l.Name)),
		stackitem.NewByteArray(l.ContentHash[:]),
		stackitem.NewBigInteger(big.NewInt(int64(l.Category))),
		stackitem.NewBigInteger(big.NewInt(int64(l.Jurisdiction))),
		stackitem.NewBigInteger(big.NewInt(int64(l.ParentID))),
		stackitem.NewBigInteger(big.NewInt(int64(l.EffectiveAt))),
		stackitem.NewBigInteger(big.NewInt(int64(l.ExpiresAt))),
		stackitem.NewBigInteger(big.NewInt(int64(l.EnactedAt))),
		stackitem.NewByteArray(l.EnactedBy.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(l.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(l.SupersededBy))),
		stackitem.NewBool(l.RequiresVita),
		stackitem.NewBigInteger(big.NewInt(int64(l.Enforcement))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (l *Law) 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))
	}

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

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

	contentHash, err := items[2].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid ContentHash: %w", err)
	}
	if len(contentHash) == 32 {
		copy(l.ContentHash[:], contentHash)
	}

	category, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid Category: %w", err)
	}
	l.Category = LawCategory(category.Uint64())

	jurisdiction, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid Jurisdiction: %w", err)
	}
	l.Jurisdiction = uint32(jurisdiction.Uint64())

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

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

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

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

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

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

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

	requiresVita, err := items[12].TryBool()
	if err != nil {
		return fmt.Errorf("invalid RequiresVita: %w", err)
	}
	l.RequiresVita = requiresVita

	enforcement, err := items[13].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid Enforcement: %w", err)
	}
	l.Enforcement = EnforcementType(enforcement.Uint64())

	return nil
}

// Bytes serializes the Law to bytes.
func (l *Law) Bytes() []byte {
	item, _ := l.ToStackItem()
	data, _ := stackitem.Serialize(item)
	return data
}

// LawFromBytes deserializes a Law from bytes.
func LawFromBytes(data []byte) (*Law, error) {
	item, err := stackitem.Deserialize(data)
	if err != nil {
		return nil, err
	}
	l := &Law{}
	if err := l.FromStackItem(item); err != nil {
		return nil, err
	}
	return l, nil
}

// RightsRestriction represents a restriction on a constitutional right.
type RightsRestriction struct {
	Subject         util.Uint160    // Who is restricted
	RightID         uint64          // Which right is restricted
	RestrictionType RestrictionType // Type of restriction
	IssuedBy        util.Uint160    // Authority that issued
	IssuedAt        uint32          // Block height when issued
	ExpiresAt       uint32          // When restriction ends (must be set)
	Reason          string          // Legal basis
	CaseID          util.Uint256    // Associated legal case (required for due process)
}

// ToStackItem implements stackitem.Convertible interface.
func (r *RightsRestriction) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewByteArray(r.Subject.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(r.RightID))),
		stackitem.NewBigInteger(big.NewInt(int64(r.RestrictionType))),
		stackitem.NewByteArray(r.IssuedBy.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(r.IssuedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(r.ExpiresAt))),
		stackitem.NewByteArray([]byte(r.Reason)),
		stackitem.NewByteArray(r.CaseID[:]),
	}), nil
}

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

	subject, err := items[0].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid Subject: %w", err)
	}
	r.Subject, err = util.Uint160DecodeBytesBE(subject)
	if err != nil {
		return fmt.Errorf("invalid Subject address: %w", err)
	}

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

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

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

	issuedAt, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid IssuedAt: %w", err)
	}
	r.IssuedAt = uint32(issuedAt.Uint64())

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

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

	caseID, err := items[7].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid CaseID: %w", err)
	}
	if len(caseID) == 32 {
		copy(r.CaseID[:], caseID)
	}

	return nil
}

// Bytes serializes the RightsRestriction to bytes.
func (r *RightsRestriction) Bytes() []byte {
	item, _ := r.ToStackItem()
	data, _ := stackitem.Serialize(item)
	return data
}

// RightsRestrictionFromBytes deserializes a RightsRestriction from bytes.
func RightsRestrictionFromBytes(data []byte) (*RightsRestriction, error) {
	item, err := stackitem.Deserialize(data)
	if err != nil {
		return nil, err
	}
	r := &RightsRestriction{}
	if err := r.FromStackItem(item); err != nil {
		return nil, err
	}
	return r, nil
}

// IsExpired returns true if the restriction has expired.
func (r *RightsRestriction) IsExpired(currentBlock uint32) bool {
	return r.ExpiresAt != 0 && r.ExpiresAt <= currentBlock
}

// ConstitutionalRight represents an immutable constitutional right.
type ConstitutionalRight struct {
	ID          uint64          // Right ID (1-14)
	Name        string          // Human-readable name
	Description string          // Description of the right
	Enforcement EnforcementType // How right is enforced
}

// GetConstitutionalRights returns all 14 constitutional rights.
// These are hardcoded and immutable.
func GetConstitutionalRights() []ConstitutionalRight {
	return []ConstitutionalRight{
		{RightLife, "Life", "Protection from arbitrary death", EnforcementAutomatic},
		{RightLiberty, "Liberty", "Freedom from arbitrary detention", EnforcementAutomatic},
		{RightProperty, "Property", "Right to own and transfer assets", EnforcementAutomatic},
		{RightEquality, "Equality", "Equal treatment under law", EnforcementAutomatic},
		{RightDueProcess, "DueProcess", "Fair legal proceedings before restriction", EnforcementAutomatic},
		{RightPrivacy, "Privacy", "Privacy of personal data", EnforcementAutomatic},
		{RightExpression, "Expression", "Freedom of expression", EnforcementLogging},
		{RightAssembly, "Assembly", "Freedom of assembly", EnforcementLogging},
		{RightMovement, "Movement", "Freedom of movement", EnforcementAutomatic},
		{RightEducation, "Education", "Right to education", EnforcementLogging},
		{RightHealthcare, "Healthcare", "Right to healthcare", EnforcementLogging},
		{RightLabor, "Labor", "Right to fair labor conditions", EnforcementLogging},
		{RightVote, "Vote", "Right to participate in democracy", EnforcementAutomatic},
		{RightAsylum, "Asylum", "Right to seek protection from persecution", EnforcementAutomatic},
	}
}

// GetConstitutionalRight returns a specific constitutional right by ID.
func GetConstitutionalRight(id uint64) *ConstitutionalRight {
	if id < 1 || id > MaxConstitutionalRight {
		return nil
	}
	rights := GetConstitutionalRights()
	return &rights[id-1]
}

// IsConstitutionalRight returns true if the right ID is a constitutional right.
func IsConstitutionalRight(id uint64) bool {
	return id >= 1 && id <= MaxConstitutionalRight
}
