package native

import (
	"encoding/binary"
	"errors"
	"math/big"

	"git.marketally.com/tutus-one/tutus-chain/pkg/config"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/dao"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/interop"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
	"git.marketally.com/tutus-one/tutus-chain/pkg/core/state"
	"git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract"
	"git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag"
	"git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest"
	"git.marketally.com/tutus-one/tutus-chain/pkg/util"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)

// Annos represents the lifespan/years native contract for tracking Vita holder ages.
type Annos struct {
	interop.ContractMD
	Tutus ITutus
	Vita  IVita
}

// AnnosCache represents the cached state for Annos contract.
type AnnosCache struct {
	recordCount uint64
}

// Storage key prefixes for Annos.
const (
	annosPrefixLifespan      byte = 0x01 // vitaID -> LifespanRecord
	annosPrefixByOwner       byte = 0x02 // owner -> vitaID
	annosPrefixRecordCounter byte = 0xF0 // -> uint64
	annosPrefixConfig        byte = 0xFF // -> AnnosConfig
)

// Event names for Annos.
const (
	BirthRegisteredEvent = "BirthRegistered"
	DeathRecordedEvent   = "DeathRecorded"
	LifeStageChangedEvent = "LifeStageChanged"
)

// Seconds per year (approximately, for age calculation).
const secondsPerYear = 365 * 24 * 60 * 60

// Various errors for Annos.
var (
	ErrAnnosRecordNotFound   = errors.New("lifespan record not found")
	ErrAnnosRecordExists     = errors.New("lifespan record already exists")
	ErrAnnosNoVita           = errors.New("owner must have an active Vita")
	ErrAnnosNotCommittee     = errors.New("invalid committee signature")
	ErrAnnosAlreadyDeceased  = errors.New("person is already marked as deceased")
	ErrAnnosInvalidTimestamp = errors.New("invalid birth timestamp")
	ErrAnnosNotVita          = errors.New("only Vita contract can call this method")
)

var (
	_ interop.Contract        = (*Annos)(nil)
	_ dao.NativeContractCache = (*AnnosCache)(nil)
)

// Copy implements NativeContractCache interface.
func (c *AnnosCache) Copy() dao.NativeContractCache {
	return &AnnosCache{
		recordCount: c.recordCount,
	}
}

// checkCommittee checks if the caller has committee authority.
func (a *Annos) checkCommittee(ic *interop.Context) bool {
	return a.Tutus.CheckCommittee(ic)
}

// newAnnos creates a new Annos native contract.
func newAnnos() *Annos {
	ann := &Annos{
		ContractMD: *interop.NewContractMD(nativenames.Annos, nativeids.Annos),
	}
	defer ann.BuildHFSpecificMD(ann.ActiveIn())

	// ===== Age Queries =====

	// getAge - Get current age in years
	desc := NewDescriptor("getAge", smartcontract.IntegerType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md := NewMethodAndPrice(ann.getAge, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getAgeAtTime - Get age at a specific timestamp
	desc = NewDescriptor("getAgeAtTime", smartcontract.IntegerType,
		manifest.NewParameter("owner", smartcontract.Hash160Type),
		manifest.NewParameter("timestamp", smartcontract.IntegerType))
	md = NewMethodAndPrice(ann.getAgeAtTime, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getLifeStage - Get life stage (Child, Youth, Adult, Elder)
	desc = NewDescriptor("getLifeStage", smartcontract.IntegerType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.getLifeStage, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getBirthTimestamp - Get actual birth timestamp
	desc = NewDescriptor("getBirthTimestamp", smartcontract.IntegerType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.getBirthTimestamp, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getRecord - Get full lifespan record
	desc = NewDescriptor("getRecord", smartcontract.ArrayType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.getRecord, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getRecordByVitaID - Get record by Vita ID
	desc = NewDescriptor("getRecordByVitaID", smartcontract.ArrayType,
		manifest.NewParameter("vitaID", smartcontract.IntegerType))
	md = NewMethodAndPrice(ann.getRecordByVitaID, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// ===== Entitlement Checks =====

	// isVotingAge - Check if at voting age
	desc = NewDescriptor("isVotingAge", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.isVotingAge, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// isAdult - Check if adult
	desc = NewDescriptor("isAdult", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.isAdult, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// isRetirementAge - Check if at retirement age
	desc = NewDescriptor("isRetirementAge", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.isRetirementAge, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// isAlive - Check if person is alive
	desc = NewDescriptor("isAlive", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(ann.isAlive, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// ===== Life Events (Committee Only) =====

	// recordDeath - Record death (committee only)
	desc = NewDescriptor("recordDeath", smartcontract.BoolType,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("deathTimestamp", smartcontract.IntegerType))
	md = NewMethodAndPrice(ann.recordDeath, 1<<17, callflag.States|callflag.AllowNotify)
	ann.AddMethod(md, desc)

	// ===== Query Methods =====

	// getConfig - Get Annos configuration
	desc = NewDescriptor("getConfig", smartcontract.ArrayType)
	md = NewMethodAndPrice(ann.getConfig, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// getTotalRecords - Get total lifespan records
	desc = NewDescriptor("getTotalRecords", smartcontract.IntegerType)
	md = NewMethodAndPrice(ann.getTotalRecords, 1<<15, callflag.ReadStates)
	ann.AddMethod(md, desc)

	// ===== Events =====

	// BirthRegistered event
	eDesc := NewEventDescriptor(BirthRegisteredEvent,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("owner", smartcontract.Hash160Type),
		manifest.NewParameter("birthTimestamp", smartcontract.IntegerType))
	ann.AddEvent(NewEvent(eDesc))

	// DeathRecorded event
	eDesc = NewEventDescriptor(DeathRecordedEvent,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("deathTimestamp", smartcontract.IntegerType))
	ann.AddEvent(NewEvent(eDesc))

	// LifeStageChanged event
	eDesc = NewEventDescriptor(LifeStageChangedEvent,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("previousStage", smartcontract.IntegerType),
		manifest.NewParameter("newStage", smartcontract.IntegerType))
	ann.AddEvent(NewEvent(eDesc))

	return ann
}

// Metadata returns contract metadata.
func (a *Annos) Metadata() *interop.ContractMD {
	return &a.ContractMD
}

// Initialize initializes the Annos contract.
func (a *Annos) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
	if hf != a.ActiveIn() {
		return nil
	}

	// Initialize counter
	a.setRecordCounter(ic.DAO, 0)

	// Initialize config with defaults
	cfg := state.DefaultAnnosConfig()
	a.setConfig(ic.DAO, &cfg)

	// Initialize cache
	cache := &AnnosCache{
		recordCount: 0,
	}
	ic.DAO.SetCache(a.ID, cache)

	return nil
}

// InitializeCache initializes the cache from storage.
func (a *Annos) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
	cache := &AnnosCache{
		recordCount: a.getRecordCounter(d),
	}
	d.SetCache(a.ID, cache)
	return nil
}

// OnPersist is called before block is committed.
func (a *Annos) OnPersist(ic *interop.Context) error {
	return nil
}

// PostPersist is called after block is committed.
func (a *Annos) PostPersist(ic *interop.Context) error {
	return nil
}

// ActiveIn returns the hardfork at which this contract is activated.
func (a *Annos) ActiveIn() *config.Hardfork {
	return nil // Always active
}

// ===== Storage Helpers =====

func (a *Annos) makeLifespanKey(vitaID uint64) []byte {
	key := make([]byte, 9)
	key[0] = annosPrefixLifespan
	binary.BigEndian.PutUint64(key[1:], vitaID)
	return key
}

func (a *Annos) makeByOwnerKey(owner util.Uint160) []byte {
	key := make([]byte, 21)
	key[0] = annosPrefixByOwner
	copy(key[1:], owner.BytesBE())
	return key
}

func (a *Annos) getRecordCounter(d *dao.Simple) uint64 {
	si := d.GetStorageItem(a.ID, []byte{annosPrefixRecordCounter})
	if si == nil {
		return 0
	}
	return binary.BigEndian.Uint64(si)
}

func (a *Annos) setRecordCounter(d *dao.Simple, count uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, count)
	d.PutStorageItem(a.ID, []byte{annosPrefixRecordCounter}, buf)
}

func (a *Annos) getConfigInternal(d *dao.Simple) *state.AnnosConfig {
	si := d.GetStorageItem(a.ID, []byte{annosPrefixConfig})
	if si == nil {
		cfg := state.DefaultAnnosConfig()
		return &cfg
	}
	cfg := new(state.AnnosConfig)
	item, _ := stackitem.Deserialize(si)
	cfg.FromStackItem(item)
	return cfg
}

func (a *Annos) setConfig(d *dao.Simple, cfg *state.AnnosConfig) {
	item, _ := cfg.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(a.ID, []byte{annosPrefixConfig}, data)
}

func (a *Annos) getRecordInternal(d *dao.Simple, vitaID uint64) *state.LifespanRecord {
	si := d.GetStorageItem(a.ID, a.makeLifespanKey(vitaID))
	if si == nil {
		return nil
	}
	rec := new(state.LifespanRecord)
	item, _ := stackitem.Deserialize(si)
	rec.FromStackItem(item)
	return rec
}

func (a *Annos) putRecord(d *dao.Simple, rec *state.LifespanRecord) {
	item, _ := rec.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(a.ID, a.makeLifespanKey(rec.VitaID), data)
}

func (a *Annos) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) {
	si := d.GetStorageItem(a.ID, a.makeByOwnerKey(owner))
	if si == nil {
		return 0, false
	}
	return binary.BigEndian.Uint64(si), true
}

func (a *Annos) setOwnerToVitaID(d *dao.Simple, owner util.Uint160, vitaID uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, vitaID)
	d.PutStorageItem(a.ID, a.makeByOwnerKey(owner), buf)
}

// calculateAge calculates age in years from birth timestamp to current timestamp.
func calculateAge(birthTimestamp, currentTimestamp uint64) uint8 {
	if currentTimestamp <= birthTimestamp {
		return 0
	}
	ageSeconds := currentTimestamp - birthTimestamp
	ageYears := ageSeconds / secondsPerYear
	if ageYears > 255 {
		return 255 // Cap at max uint8
	}
	return uint8(ageYears)
}

// calculateLifeStage determines the life stage based on age.
func (a *Annos) calculateLifeStage(d *dao.Simple, age uint8) state.LifeStage {
	cfg := a.getConfigInternal(d)

	if age < cfg.AdultAge {
		return state.LifeStageChild
	} else if age <= cfg.YouthMaxAge {
		return state.LifeStageYouth
	} else if age < cfg.ElderMinAge {
		return state.LifeStageAdult
	}
	return state.LifeStageElder
}

// ===== Contract Methods =====

// getAge returns the current age in years.
func (a *Annos) getAge(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	// If deceased, return age at death
	currentTime := ic.Block.Timestamp / 1000 // Block timestamp is in ms
	if rec.Status == state.LifespanDeceased && rec.DeathTimestamp > 0 {
		currentTime = rec.DeathTimestamp
	}

	age := calculateAge(rec.BirthTimestamp, currentTime)
	return stackitem.NewBigInteger(big.NewInt(int64(age)))
}

// getAgeAtTime returns the age at a specific timestamp.
func (a *Annos) getAgeAtTime(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])
	timestamp := toUint64(args[1])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	// If deceased and query time is after death, use death time
	queryTime := timestamp
	if rec.Status == state.LifespanDeceased && rec.DeathTimestamp > 0 && timestamp > rec.DeathTimestamp {
		queryTime = rec.DeathTimestamp
	}

	age := calculateAge(rec.BirthTimestamp, queryTime)
	return stackitem.NewBigInteger(big.NewInt(int64(age)))
}

// getLifeStage returns the current life stage.
func (a *Annos) getLifeStage(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	currentTime := ic.Block.Timestamp / 1000
	if rec.Status == state.LifespanDeceased && rec.DeathTimestamp > 0 {
		currentTime = rec.DeathTimestamp
	}

	age := calculateAge(rec.BirthTimestamp, currentTime)
	stage := a.calculateLifeStage(ic.DAO, age)
	return stackitem.NewBigInteger(big.NewInt(int64(stage)))
}

// getBirthTimestamp returns the birth timestamp.
func (a *Annos) getBirthTimestamp(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	return stackitem.NewBigInteger(new(big.Int).SetUint64(rec.BirthTimestamp))
}

// getRecord returns the full lifespan record.
func (a *Annos) getRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.Null{}
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.Null{}
	}

	item, _ := rec.ToStackItem()
	return item
}

// getRecordByVitaID returns the record by Vita ID.
func (a *Annos) getRecordByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	vitaID := toUint64(args[0])

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.Null{}
	}

	item, _ := rec.ToStackItem()
	return item
}

// isVotingAge checks if the owner is at voting age.
func (a *Annos) isVotingAge(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])
	return stackitem.NewBool(a.IsVotingAgeInternal(ic.DAO, owner, ic.Block.Timestamp/1000))
}

// isAdult checks if the owner is an adult.
func (a *Annos) isAdult(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])
	return stackitem.NewBool(a.IsAdultInternal(ic.DAO, owner, ic.Block.Timestamp/1000))
}

// isRetirementAge checks if the owner is at retirement age.
func (a *Annos) isRetirementAge(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])
	return stackitem.NewBool(a.IsRetirementAgeInternal(ic.DAO, owner, ic.Block.Timestamp/1000))
}

// isAlive checks if the owner is alive.
func (a *Annos) isAlive(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	vitaID, found := a.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		return stackitem.NewBool(false)
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		return stackitem.NewBool(false)
	}

	return stackitem.NewBool(rec.Status == state.LifespanActive)
}

// recordDeath records a death (committee only).
func (a *Annos) recordDeath(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	vitaID := toUint64(args[0])
	deathTimestamp := toUint64(args[1])

	// Committee only
	if !a.checkCommittee(ic) {
		panic(ErrAnnosNotCommittee)
	}

	rec := a.getRecordInternal(ic.DAO, vitaID)
	if rec == nil {
		panic(ErrAnnosRecordNotFound)
	}

	if rec.Status == state.LifespanDeceased {
		panic(ErrAnnosAlreadyDeceased)
	}

	// Validate death timestamp
	if deathTimestamp == 0 {
		deathTimestamp = ic.Block.Timestamp / 1000
	}
	if deathTimestamp < rec.BirthTimestamp {
		panic(ErrAnnosInvalidTimestamp)
	}

	// Update record
	rec.DeathTimestamp = deathTimestamp
	rec.DeathBlock = ic.Block.Index
	rec.Status = state.LifespanDeceased
	a.putRecord(ic.DAO, rec)

	// Emit event
	ic.AddNotification(a.Hash, DeathRecordedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
		stackitem.NewBigInteger(new(big.Int).SetUint64(deathTimestamp)),
	}))

	return stackitem.NewBool(true)
}

// getConfig returns the Annos configuration.
func (a *Annos) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	cfg := a.getConfigInternal(ic.DAO)
	item, _ := cfg.ToStackItem()
	return item
}

// getTotalRecords returns the total number of lifespan records.
func (a *Annos) getTotalRecords(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	cache := ic.DAO.GetROCache(a.ID).(*AnnosCache)
	return stackitem.NewBigInteger(big.NewInt(int64(cache.recordCount)))
}

// ===== Public Interface Methods for Cross-Contract Access =====

// RegisterBirthInternal is called by Vita contract to register a birth.
// This should only be called when minting a new Vita token.
func (a *Annos) RegisterBirthInternal(d *dao.Simple, ic *interop.Context, vitaID uint64, owner util.Uint160, birthTimestamp uint64) error {
	// Check if record already exists
	existing := a.getRecordInternal(d, vitaID)
	if existing != nil {
		return ErrAnnosRecordExists
	}

	// Validate birth timestamp (must not be in the future)
	currentTime := ic.Block.Timestamp / 1000
	if birthTimestamp > currentTime {
		birthTimestamp = currentTime // Cap at current time
	}

	// Increment counter
	cache := d.GetRWCache(a.ID).(*AnnosCache)
	cache.recordCount++
	a.setRecordCounter(d, cache.recordCount)

	// Create lifespan record
	rec := &state.LifespanRecord{
		VitaID:            vitaID,
		Owner:             owner,
		BirthTimestamp:    birthTimestamp,
		RegistrationBlock: ic.Block.Index,
		RegistrationTime:  currentTime,
		DeathTimestamp:    0,
		DeathBlock:        0,
		Status:            state.LifespanActive,
	}

	a.putRecord(d, rec)
	a.setOwnerToVitaID(d, owner, vitaID)

	// Emit event
	ic.AddNotification(a.Hash, BirthRegisteredEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
		stackitem.NewByteArray(owner.BytesBE()),
		stackitem.NewBigInteger(new(big.Int).SetUint64(birthTimestamp)),
	}))

	return nil
}

// GetAgeInternal returns the age of an owner at the current timestamp.
func (a *Annos) GetAgeInternal(d *dao.Simple, owner util.Uint160, currentTimestamp uint64) (uint8, error) {
	vitaID, found := a.getVitaIDByOwner(d, owner)
	if !found {
		return 0, ErrAnnosRecordNotFound
	}

	rec := a.getRecordInternal(d, vitaID)
	if rec == nil {
		return 0, ErrAnnosRecordNotFound
	}

	// If deceased, use death time
	queryTime := currentTimestamp
	if rec.Status == state.LifespanDeceased && rec.DeathTimestamp > 0 {
		queryTime = rec.DeathTimestamp
	}

	return calculateAge(rec.BirthTimestamp, queryTime), nil
}

// IsVotingAgeInternal checks if an owner is at voting age.
func (a *Annos) IsVotingAgeInternal(d *dao.Simple, owner util.Uint160, currentTimestamp uint64) bool {
	age, err := a.GetAgeInternal(d, owner, currentTimestamp)
	if err != nil {
		return false
	}
	cfg := a.getConfigInternal(d)
	return age >= cfg.VotingAge
}

// IsAdultInternal checks if an owner is an adult.
func (a *Annos) IsAdultInternal(d *dao.Simple, owner util.Uint160, currentTimestamp uint64) bool {
	age, err := a.GetAgeInternal(d, owner, currentTimestamp)
	if err != nil {
		return false
	}
	cfg := a.getConfigInternal(d)
	return age >= cfg.AdultAge
}

// IsRetirementAgeInternal checks if an owner is at retirement age.
func (a *Annos) IsRetirementAgeInternal(d *dao.Simple, owner util.Uint160, currentTimestamp uint64) bool {
	age, err := a.GetAgeInternal(d, owner, currentTimestamp)
	if err != nil {
		return false
	}
	cfg := a.getConfigInternal(d)
	return age >= cfg.RetirementAge
}

// GetLifeStageInternal returns the life stage of an owner.
func (a *Annos) GetLifeStageInternal(d *dao.Simple, owner util.Uint160, currentTimestamp uint64) state.LifeStage {
	age, err := a.GetAgeInternal(d, owner, currentTimestamp)
	if err != nil {
		return state.LifeStageChild // Default
	}
	return a.calculateLifeStage(d, age)
}

// IsAliveInternal checks if an owner is alive.
func (a *Annos) IsAliveInternal(d *dao.Simple, owner util.Uint160) bool {
	vitaID, found := a.getVitaIDByOwner(d, owner)
	if !found {
		return false
	}
	rec := a.getRecordInternal(d, vitaID)
	if rec == nil {
		return false
	}
	return rec.Status == state.LifespanActive
}

// GetRecordByOwner returns the lifespan record for an owner.
func (a *Annos) GetRecordByOwner(d *dao.Simple, owner util.Uint160) (*state.LifespanRecord, error) {
	vitaID, found := a.getVitaIDByOwner(d, owner)
	if !found {
		return nil, ErrAnnosRecordNotFound
	}
	rec := a.getRecordInternal(d, vitaID)
	if rec == nil {
		return nil, ErrAnnosRecordNotFound
	}
	return rec, nil
}

// Address returns the contract's script hash.
func (a *Annos) Address() util.Uint160 {
	return a.Hash
}
