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

// Salus represents the universal healthcare native contract.
type Salus struct {
	interop.ContractMD
	Tutus ITutus
	Vita         IVita
	RoleRegistry IRoleRegistry
	Lex          ILex
}

// SalusCache represents the cached state for Salus contract.
type SalusCache struct {
	accountCount       uint64
	recordCount        uint64
	providerCount      uint64
	authorizationCount uint64
	emergencyCount     uint64
}

// Storage key prefixes for Salus.
const (
	salusPrefixAccount           byte = 0x01 // vitaID -> HealthcareAccount
	salusPrefixAccountByOwner    byte = 0x02 // owner -> vitaID
	salusPrefixRecord            byte = 0x10 // recordID -> MedicalRecord
	salusPrefixRecordByPatient   byte = 0x11 // vitaID + recordID -> exists
	salusPrefixRecordByProvider  byte = 0x12 // provider + recordID -> exists
	salusPrefixProvider          byte = 0x20 // providerID -> HealthcareProvider
	salusPrefixProviderByAddress byte = 0x21 // provider address -> providerID
	salusPrefixAuthorization     byte = 0x30 // authID -> ProviderAuthorization
	salusPrefixAuthByPatient     byte = 0x31 // vitaID + authID -> exists
	salusPrefixAuthByProvider    byte = 0x32 // provider + authID -> exists
	salusPrefixActiveAuth        byte = 0x33 // vitaID + provider -> authID
	salusPrefixEmergencyAccess   byte = 0x40 // emergencyID -> EmergencyAccess
	salusPrefixEmergencyByPatient byte = 0x41 // vitaID + emergencyID -> exists
	salusPrefixAccountCounter    byte = 0xF0 // -> uint64
	salusPrefixRecordCounter     byte = 0xF1 // -> next record ID
	salusPrefixProviderCounter   byte = 0xF2 // -> next provider ID
	salusPrefixAuthCounter       byte = 0xF3 // -> next authorization ID
	salusPrefixEmergencyCounter  byte = 0xF4 // -> next emergency access ID
	salusPrefixConfig            byte = 0xFF // -> SalusConfig
)

// Event names for Salus.
const (
	HealthcareActivatedEvent     = "HealthcareActivated"
	CreditsAllocatedEventSalus   = "CreditsAllocated"
	MedicalRecordCreatedEvent    = "MedicalRecordCreated"
	ProviderRegisteredEvent      = "ProviderRegistered"
	ProviderSuspendedEvent       = "ProviderSuspended"
	AuthorizationGrantedEvent    = "AuthorizationGranted"
	AuthorizationRevokedEvent    = "AuthorizationRevoked"
	EmergencyAccessGrantedEvent  = "EmergencyAccessGranted"
	EmergencyAccessReviewedEvent = "EmergencyAccessReviewed"
)

// Role constants for healthcare providers.
const (
	RoleHealthcare uint64 = 21 // Can record medical events and access authorized records
)

// Various errors for Salus.
var (
	ErrSalusAccountNotFound       = errors.New("healthcare account not found")
	ErrSalusAccountExists         = errors.New("healthcare account already exists")
	ErrSalusAccountSuspended      = errors.New("healthcare account is suspended")
	ErrSalusAccountClosed         = errors.New("healthcare account is closed")
	ErrSalusNoVita                = errors.New("owner must have an active Vita")
	ErrSalusInsufficientCredits   = errors.New("insufficient healthcare credits")
	ErrSalusInvalidCredits        = errors.New("invalid credit amount")
	ErrSalusRecordNotFound        = errors.New("medical record not found")
	ErrSalusProviderNotFound      = errors.New("healthcare provider not found")
	ErrSalusProviderExists        = errors.New("healthcare provider already registered")
	ErrSalusProviderSuspended     = errors.New("healthcare provider is suspended")
	ErrSalusProviderRevoked       = errors.New("healthcare provider is revoked")
	ErrSalusNotProvider           = errors.New("caller is not an authorized healthcare provider")
	ErrSalusNotCommittee          = errors.New("invalid committee signature")
	ErrSalusInvalidOwner          = errors.New("invalid owner address")
	ErrSalusInvalidProvider       = errors.New("invalid provider address")
	ErrSalusAuthorizationNotFound = errors.New("authorization not found")
	ErrSalusAuthorizationExpired  = errors.New("authorization has expired")
	ErrSalusAuthorizationExists   = errors.New("authorization already exists")
	ErrSalusNotPatient            = errors.New("caller is not the patient")
	ErrSalusHealthcareRestricted  = errors.New("healthcare right is restricted")
	ErrSalusEmergencyNotFound     = errors.New("emergency access not found")
	ErrSalusInvalidReason         = errors.New("invalid reason")
	ErrSalusInvalidName           = errors.New("invalid name")
	ErrSalusInvalidSpecialty      = errors.New("invalid specialty")
	ErrSalusNoAccess              = errors.New("no access to patient records")
	ErrSalusExceedsMaxDuration    = errors.New("exceeds maximum authorization duration")
)

var (
	_ interop.Contract        = (*Salus)(nil)
	_ dao.NativeContractCache = (*SalusCache)(nil)
)

// Copy implements NativeContractCache interface.
func (c *SalusCache) Copy() dao.NativeContractCache {
	return &SalusCache{
		accountCount:       c.accountCount,
		recordCount:        c.recordCount,
		providerCount:      c.providerCount,
		authorizationCount: c.authorizationCount,
		emergencyCount:     c.emergencyCount,
	}
}

// checkCommittee checks if the caller has committee authority.
func (s *Salus) checkCommittee(ic *interop.Context) bool {
	if s.RoleRegistry != nil {
		return s.RoleRegistry.CheckCommittee(ic)
	}
	return s.Tutus.CheckCommittee(ic)
}

// checkHealthcareProvider checks if the caller has healthcare provider authority.
func (s *Salus) checkHealthcareProvider(ic *interop.Context) bool {
	caller := ic.VM.GetCallingScriptHash()
	if s.RoleRegistry != nil {
		if s.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleHealthcare, ic.Block.Index) {
			return true
		}
	}
	// Committee members can also act as healthcare providers
	return s.checkCommittee(ic)
}

// checkHealthcareRight checks if subject has healthcare rights via Lex.
func (s *Salus) checkHealthcareRight(ic *interop.Context, subject util.Uint160) bool {
	if s.Lex == nil {
		return true // Allow if Lex not available
	}
	return s.Lex.HasRightInternal(ic.DAO, subject, state.RightHealthcare, ic.Block.Index)
}

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

	// ===== Account Management =====

	// activateHealthcare - Activate healthcare account for a Vita holder
	desc := NewDescriptor("activateHealthcare", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md := NewMethodAndPrice(s.activateHealthcare, 1<<17, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getAccount - Get healthcare account by owner
	desc = NewDescriptor("getAccount", smartcontract.ArrayType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(s.getAccount, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

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

	// allocateCredits - Allocate healthcare credits (committee only)
	desc = NewDescriptor("allocateCredits", smartcontract.BoolType,
		manifest.NewParameter("owner", smartcontract.Hash160Type),
		manifest.NewParameter("amount", smartcontract.IntegerType),
		manifest.NewParameter("reason", smartcontract.StringType))
	md = NewMethodAndPrice(s.allocateCredits, 1<<16, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getCredits - Get available credits
	desc = NewDescriptor("getCredits", smartcontract.IntegerType,
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	md = NewMethodAndPrice(s.getCredits, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// ===== Medical Records =====

	// recordMedicalEvent - Record a medical event (provider only)
	desc = NewDescriptor("recordMedicalEvent", smartcontract.IntegerType,
		manifest.NewParameter("patient", smartcontract.Hash160Type),
		manifest.NewParameter("recordType", smartcontract.IntegerType),
		manifest.NewParameter("contentHash", smartcontract.Hash256Type),
		manifest.NewParameter("credits", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.recordMedicalEvent, 1<<17, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getMedicalRecord - Get medical record by ID
	desc = NewDescriptor("getMedicalRecord", smartcontract.ArrayType,
		manifest.NewParameter("recordID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.getMedicalRecord, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// ===== Provider Management =====

	// registerProvider - Register a healthcare provider (committee only)
	desc = NewDescriptor("registerProvider", smartcontract.IntegerType,
		manifest.NewParameter("address", smartcontract.Hash160Type),
		manifest.NewParameter("name", smartcontract.StringType),
		manifest.NewParameter("specialty", smartcontract.StringType),
		manifest.NewParameter("licenseHash", smartcontract.Hash256Type))
	md = NewMethodAndPrice(s.registerProvider, 1<<17, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// suspendProvider - Suspend a healthcare provider (committee only)
	desc = NewDescriptor("suspendProvider", smartcontract.BoolType,
		manifest.NewParameter("providerID", smartcontract.IntegerType),
		manifest.NewParameter("reason", smartcontract.StringType))
	md = NewMethodAndPrice(s.suspendProvider, 1<<16, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getProvider - Get provider details
	desc = NewDescriptor("getProvider", smartcontract.ArrayType,
		manifest.NewParameter("providerID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.getProvider, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// getProviderByAddress - Get provider by address
	desc = NewDescriptor("getProviderByAddress", smartcontract.ArrayType,
		manifest.NewParameter("address", smartcontract.Hash160Type))
	md = NewMethodAndPrice(s.getProviderByAddress, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// ===== Authorization Management =====

	// authorizeAccess - Grant provider access to patient records
	desc = NewDescriptor("authorizeAccess", smartcontract.IntegerType,
		manifest.NewParameter("patient", smartcontract.Hash160Type),
		manifest.NewParameter("provider", smartcontract.Hash160Type),
		manifest.NewParameter("accessLevel", smartcontract.IntegerType),
		manifest.NewParameter("duration", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.authorizeAccess, 1<<17, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// revokeAccess - Revoke provider access
	desc = NewDescriptor("revokeAccess", smartcontract.BoolType,
		manifest.NewParameter("authID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.revokeAccess, 1<<16, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getAuthorization - Get authorization details
	desc = NewDescriptor("getAuthorization", smartcontract.ArrayType,
		manifest.NewParameter("authID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.getAuthorization, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// hasAccess - Check if provider has access to patient
	desc = NewDescriptor("hasAccess", smartcontract.BoolType,
		manifest.NewParameter("patient", smartcontract.Hash160Type),
		manifest.NewParameter("provider", smartcontract.Hash160Type))
	md = NewMethodAndPrice(s.hasAccess, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

	// ===== Emergency Access =====

	// emergencyAccess - Request emergency access (provider only)
	desc = NewDescriptor("emergencyAccess", smartcontract.IntegerType,
		manifest.NewParameter("patient", smartcontract.Hash160Type),
		manifest.NewParameter("reason", smartcontract.StringType))
	md = NewMethodAndPrice(s.emergencyAccess, 1<<17, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// reviewEmergencyAccess - Review emergency access (committee only)
	desc = NewDescriptor("reviewEmergencyAccess", smartcontract.BoolType,
		manifest.NewParameter("emergencyID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.reviewEmergencyAccess, 1<<16, callflag.States|callflag.AllowNotify)
	s.AddMethod(md, desc)

	// getEmergencyAccess - Get emergency access details
	desc = NewDescriptor("getEmergencyAccess", smartcontract.ArrayType,
		manifest.NewParameter("emergencyID", smartcontract.IntegerType))
	md = NewMethodAndPrice(s.getEmergencyAccess, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

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

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

	// getTotalAccounts - Get total healthcare accounts
	desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType)
	md = NewMethodAndPrice(s.getTotalAccounts, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

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

	// getTotalProviders - Get total healthcare providers
	desc = NewDescriptor("getTotalProviders", smartcontract.IntegerType)
	md = NewMethodAndPrice(s.getTotalProviders, 1<<15, callflag.ReadStates)
	s.AddMethod(md, desc)

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

	// HealthcareActivated event
	eDesc := NewEventDescriptor(HealthcareActivatedEvent,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("owner", smartcontract.Hash160Type))
	s.AddEvent(NewEvent(eDesc))

	// CreditsAllocated event
	eDesc = NewEventDescriptor(CreditsAllocatedEventSalus,
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("amount", smartcontract.IntegerType),
		manifest.NewParameter("total", smartcontract.IntegerType))
	s.AddEvent(NewEvent(eDesc))

	// MedicalRecordCreated event
	eDesc = NewEventDescriptor(MedicalRecordCreatedEvent,
		manifest.NewParameter("recordID", smartcontract.IntegerType),
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("recordType", smartcontract.IntegerType))
	s.AddEvent(NewEvent(eDesc))

	// ProviderRegistered event
	eDesc = NewEventDescriptor(ProviderRegisteredEvent,
		manifest.NewParameter("providerID", smartcontract.IntegerType),
		manifest.NewParameter("address", smartcontract.Hash160Type),
		manifest.NewParameter("specialty", smartcontract.StringType))
	s.AddEvent(NewEvent(eDesc))

	// ProviderSuspended event
	eDesc = NewEventDescriptor(ProviderSuspendedEvent,
		manifest.NewParameter("providerID", smartcontract.IntegerType),
		manifest.NewParameter("reason", smartcontract.StringType))
	s.AddEvent(NewEvent(eDesc))

	// AuthorizationGranted event
	eDesc = NewEventDescriptor(AuthorizationGrantedEvent,
		manifest.NewParameter("authID", smartcontract.IntegerType),
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("provider", smartcontract.Hash160Type))
	s.AddEvent(NewEvent(eDesc))

	// AuthorizationRevoked event
	eDesc = NewEventDescriptor(AuthorizationRevokedEvent,
		manifest.NewParameter("authID", smartcontract.IntegerType))
	s.AddEvent(NewEvent(eDesc))

	// EmergencyAccessGranted event
	eDesc = NewEventDescriptor(EmergencyAccessGrantedEvent,
		manifest.NewParameter("emergencyID", smartcontract.IntegerType),
		manifest.NewParameter("vitaID", smartcontract.IntegerType),
		manifest.NewParameter("provider", smartcontract.Hash160Type))
	s.AddEvent(NewEvent(eDesc))

	// EmergencyAccessReviewed event
	eDesc = NewEventDescriptor(EmergencyAccessReviewedEvent,
		manifest.NewParameter("emergencyID", smartcontract.IntegerType))
	s.AddEvent(NewEvent(eDesc))

	return s
}

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

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

	// Initialize counters
	s.setAccountCounter(ic.DAO, 0)
	s.setRecordCounter(ic.DAO, 0)
	s.setProviderCounter(ic.DAO, 0)
	s.setAuthCounter(ic.DAO, 0)
	s.setEmergencyCounter(ic.DAO, 0)

	// Initialize config with defaults
	cfg := &state.SalusConfig{
		DefaultAnnualCredits:     10000,  // 10000 healthcare credits per year
		EmergencyAccessDuration:  86400,  // ~24 hours (1-second blocks)
		PreventiveCareBonus:      500,    // Bonus for preventive care visits
		MaxAuthorizationDuration: 2592000, // ~30 days (1-second blocks)
	}
	s.setConfig(ic.DAO, cfg)

	// Initialize cache
	cache := &SalusCache{
		accountCount:       0,
		recordCount:        0,
		providerCount:      0,
		authorizationCount: 0,
		emergencyCount:     0,
	}
	ic.DAO.SetCache(s.ID, cache)

	return nil
}

// InitializeCache initializes the cache from storage.
func (s *Salus) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
	cache := &SalusCache{
		accountCount:       s.getAccountCounter(d),
		recordCount:        s.getRecordCounter(d),
		providerCount:      s.getProviderCounter(d),
		authorizationCount: s.getAuthCounter(d),
		emergencyCount:     s.getEmergencyCounter(d),
	}
	d.SetCache(s.ID, cache)
	return nil
}

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

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

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

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

func (s *Salus) makeAccountKey(vitaID uint64) []byte {
	key := make([]byte, 9)
	key[0] = salusPrefixAccount
	binary.BigEndian.PutUint64(key[1:], vitaID)
	return key
}

func (s *Salus) makeAccountByOwnerKey(owner util.Uint160) []byte {
	key := make([]byte, 21)
	key[0] = salusPrefixAccountByOwner
	copy(key[1:], owner.BytesBE())
	return key
}

func (s *Salus) makeRecordKey(recordID uint64) []byte {
	key := make([]byte, 9)
	key[0] = salusPrefixRecord
	binary.BigEndian.PutUint64(key[1:], recordID)
	return key
}

func (s *Salus) makeRecordByPatientKey(vitaID, recordID uint64) []byte {
	key := make([]byte, 17)
	key[0] = salusPrefixRecordByPatient
	binary.BigEndian.PutUint64(key[1:9], vitaID)
	binary.BigEndian.PutUint64(key[9:], recordID)
	return key
}

func (s *Salus) makeProviderKey(providerID uint64) []byte {
	key := make([]byte, 9)
	key[0] = salusPrefixProvider
	binary.BigEndian.PutUint64(key[1:], providerID)
	return key
}

func (s *Salus) makeProviderByAddressKey(address util.Uint160) []byte {
	key := make([]byte, 21)
	key[0] = salusPrefixProviderByAddress
	copy(key[1:], address.BytesBE())
	return key
}

func (s *Salus) makeAuthorizationKey(authID uint64) []byte {
	key := make([]byte, 9)
	key[0] = salusPrefixAuthorization
	binary.BigEndian.PutUint64(key[1:], authID)
	return key
}

func (s *Salus) makeActiveAuthKey(vitaID uint64, provider util.Uint160) []byte {
	key := make([]byte, 29)
	key[0] = salusPrefixActiveAuth
	binary.BigEndian.PutUint64(key[1:9], vitaID)
	copy(key[9:], provider.BytesBE())
	return key
}

func (s *Salus) makeEmergencyKey(emergencyID uint64) []byte {
	key := make([]byte, 9)
	key[0] = salusPrefixEmergencyAccess
	binary.BigEndian.PutUint64(key[1:], emergencyID)
	return key
}

// Counter getters/setters
func (s *Salus) getAccountCounter(d *dao.Simple) uint64 {
	si := d.GetStorageItem(s.ID, []byte{salusPrefixAccountCounter})
	if si == nil {
		return 0
	}
	return binary.BigEndian.Uint64(si)
}

func (s *Salus) setAccountCounter(d *dao.Simple, count uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, count)
	d.PutStorageItem(s.ID, []byte{salusPrefixAccountCounter}, buf)
}

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

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

func (s *Salus) getProviderCounter(d *dao.Simple) uint64 {
	si := d.GetStorageItem(s.ID, []byte{salusPrefixProviderCounter})
	if si == nil {
		return 0
	}
	return binary.BigEndian.Uint64(si)
}

func (s *Salus) setProviderCounter(d *dao.Simple, count uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, count)
	d.PutStorageItem(s.ID, []byte{salusPrefixProviderCounter}, buf)
}

func (s *Salus) getAuthCounter(d *dao.Simple) uint64 {
	si := d.GetStorageItem(s.ID, []byte{salusPrefixAuthCounter})
	if si == nil {
		return 0
	}
	return binary.BigEndian.Uint64(si)
}

func (s *Salus) setAuthCounter(d *dao.Simple, count uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, count)
	d.PutStorageItem(s.ID, []byte{salusPrefixAuthCounter}, buf)
}

func (s *Salus) getEmergencyCounter(d *dao.Simple) uint64 {
	si := d.GetStorageItem(s.ID, []byte{salusPrefixEmergencyCounter})
	if si == nil {
		return 0
	}
	return binary.BigEndian.Uint64(si)
}

func (s *Salus) setEmergencyCounter(d *dao.Simple, count uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, count)
	d.PutStorageItem(s.ID, []byte{salusPrefixEmergencyCounter}, buf)
}

// Config getter/setter
func (s *Salus) getConfigInternal(d *dao.Simple) *state.SalusConfig {
	si := d.GetStorageItem(s.ID, []byte{salusPrefixConfig})
	if si == nil {
		return &state.SalusConfig{
			DefaultAnnualCredits:     10000,
			EmergencyAccessDuration:  86400,
			PreventiveCareBonus:      500,
			MaxAuthorizationDuration: 2592000,
		}
	}
	cfg := new(state.SalusConfig)
	item, _ := stackitem.Deserialize(si)
	cfg.FromStackItem(item)
	return cfg
}

func (s *Salus) setConfig(d *dao.Simple, cfg *state.SalusConfig) {
	item, _ := cfg.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, []byte{salusPrefixConfig}, data)
}

// Account storage
func (s *Salus) getAccountInternal(d *dao.Simple, vitaID uint64) *state.HealthcareAccount {
	si := d.GetStorageItem(s.ID, s.makeAccountKey(vitaID))
	if si == nil {
		return nil
	}
	acc := new(state.HealthcareAccount)
	item, _ := stackitem.Deserialize(si)
	acc.FromStackItem(item)
	return acc
}

func (s *Salus) putAccount(d *dao.Simple, acc *state.HealthcareAccount) {
	item, _ := acc.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, s.makeAccountKey(acc.VitaID), data)
}

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

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

// Record storage
func (s *Salus) getRecordInternal(d *dao.Simple, recordID uint64) *state.MedicalRecord {
	si := d.GetStorageItem(s.ID, s.makeRecordKey(recordID))
	if si == nil {
		return nil
	}
	record := new(state.MedicalRecord)
	item, _ := stackitem.Deserialize(si)
	record.FromStackItem(item)
	return record
}

func (s *Salus) putRecord(d *dao.Simple, record *state.MedicalRecord) {
	item, _ := record.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, s.makeRecordKey(record.ID), data)
}

func (s *Salus) setRecordByPatient(d *dao.Simple, vitaID, recordID uint64) {
	d.PutStorageItem(s.ID, s.makeRecordByPatientKey(vitaID, recordID), []byte{1})
}

// Provider storage
func (s *Salus) getProviderInternal(d *dao.Simple, providerID uint64) *state.HealthcareProvider {
	si := d.GetStorageItem(s.ID, s.makeProviderKey(providerID))
	if si == nil {
		return nil
	}
	provider := new(state.HealthcareProvider)
	item, _ := stackitem.Deserialize(si)
	provider.FromStackItem(item)
	return provider
}

func (s *Salus) putProvider(d *dao.Simple, provider *state.HealthcareProvider) {
	item, _ := provider.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, s.makeProviderKey(provider.ProviderID), data)
}

func (s *Salus) getProviderIDByAddress(d *dao.Simple, address util.Uint160) (uint64, bool) {
	si := d.GetStorageItem(s.ID, s.makeProviderByAddressKey(address))
	if si == nil {
		return 0, false
	}
	return binary.BigEndian.Uint64(si), true
}

func (s *Salus) setProviderByAddress(d *dao.Simple, address util.Uint160, providerID uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, providerID)
	d.PutStorageItem(s.ID, s.makeProviderByAddressKey(address), buf)
}

// Authorization storage
func (s *Salus) getAuthInternal(d *dao.Simple, authID uint64) *state.ProviderAuthorization {
	si := d.GetStorageItem(s.ID, s.makeAuthorizationKey(authID))
	if si == nil {
		return nil
	}
	auth := new(state.ProviderAuthorization)
	item, _ := stackitem.Deserialize(si)
	auth.FromStackItem(item)
	return auth
}

func (s *Salus) putAuth(d *dao.Simple, auth *state.ProviderAuthorization) {
	item, _ := auth.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, s.makeAuthorizationKey(auth.ID), data)
}

func (s *Salus) getActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160) (uint64, bool) {
	si := d.GetStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider))
	if si == nil {
		return 0, false
	}
	return binary.BigEndian.Uint64(si), true
}

func (s *Salus) setActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160, authID uint64) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, authID)
	d.PutStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider), buf)
}

func (s *Salus) clearActiveAuth(d *dao.Simple, vitaID uint64, provider util.Uint160) {
	d.DeleteStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider))
}

// Emergency access storage
func (s *Salus) getEmergencyInternal(d *dao.Simple, emergencyID uint64) *state.EmergencyAccess {
	si := d.GetStorageItem(s.ID, s.makeEmergencyKey(emergencyID))
	if si == nil {
		return nil
	}
	emergency := new(state.EmergencyAccess)
	item, _ := stackitem.Deserialize(si)
	emergency.FromStackItem(item)
	return emergency
}

func (s *Salus) putEmergency(d *dao.Simple, emergency *state.EmergencyAccess) {
	item, _ := emergency.ToStackItem()
	data, _ := stackitem.Serialize(item)
	d.PutStorageItem(s.ID, s.makeEmergencyKey(emergency.ID), data)
}

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

// activateHealthcare activates healthcare account for a Vita holder.
func (s *Salus) activateHealthcare(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

	// Check owner has active Vita
	if s.Vita == nil {
		panic(ErrSalusNoVita)
	}
	vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner)
	if err != nil || vita == nil {
		panic(ErrSalusNoVita)
	}
	if vita.Status != state.TokenStatusActive {
		panic(ErrSalusNoVita)
	}

	// Check if account already exists
	existing := s.getAccountInternal(ic.DAO, vita.TokenID)
	if existing != nil {
		panic(ErrSalusAccountExists)
	}

	// Check healthcare rights
	if !s.checkHealthcareRight(ic, owner) {
		// Log but allow (EnforcementLogging)
	}

	// Get cache and increment counter
	cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
	cache.accountCount++
	s.setAccountCounter(ic.DAO, cache.accountCount)

	// Get default credits from config
	cfg := s.getConfigInternal(ic.DAO)

	// Create account
	acc := &state.HealthcareAccount{
		VitaID:           vita.TokenID,
		Owner:            owner,
		AnnualAllocation: cfg.DefaultAnnualCredits,
		CreditsUsed:      0,
		CreditsAvailable: cfg.DefaultAnnualCredits,
		BiologicalAge:    0,
		LastCheckup:      0,
		Status:           state.HealthcareAccountActive,
		CreatedAt:        ic.Block.Index,
		UpdatedAt:        ic.Block.Index,
	}

	// Store account
	s.putAccount(ic.DAO, acc)
	s.setOwnerToVitaID(ic.DAO, owner, vita.TokenID)

	// Emit event
	ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))),
		stackitem.NewByteArray(owner.BytesBE()),
	}))

	return stackitem.NewBool(true)
}

// getAccount returns healthcare account by owner.
func (s *Salus) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

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

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		return stackitem.Null{}
	}

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

// getAccountByVitaID returns healthcare account by Vita ID.
func (s *Salus) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	vitaID := toUint64(args[0])

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		return stackitem.Null{}
	}

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

// allocateCredits allocates healthcare credits to an account (committee only).
func (s *Salus) allocateCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])
	amount := toUint64(args[1])
	// reason := toString(args[2]) // for logging

	// Committee only
	if !s.checkCommittee(ic) {
		panic(ErrSalusNotCommittee)
	}

	if amount == 0 {
		panic(ErrSalusInvalidCredits)
	}

	// Get or create account
	vitaID, found := s.getVitaIDByOwner(ic.DAO, owner)
	if !found {
		// Auto-create account if Vita exists
		if s.Vita == nil {
			panic(ErrSalusNoVita)
		}
		vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner)
		if err != nil || vita == nil || vita.Status != state.TokenStatusActive {
			panic(ErrSalusNoVita)
		}
		vitaID = vita.TokenID

		cfg := s.getConfigInternal(ic.DAO)

		// Create account
		cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
		cache.accountCount++
		s.setAccountCounter(ic.DAO, cache.accountCount)

		acc := &state.HealthcareAccount{
			VitaID:           vitaID,
			Owner:            owner,
			AnnualAllocation: cfg.DefaultAnnualCredits,
			CreditsUsed:      0,
			CreditsAvailable: amount,
			BiologicalAge:    0,
			LastCheckup:      0,
			Status:           state.HealthcareAccountActive,
			CreatedAt:        ic.Block.Index,
			UpdatedAt:        ic.Block.Index,
		}
		s.putAccount(ic.DAO, acc)
		s.setOwnerToVitaID(ic.DAO, owner, vitaID)

		// Emit events
		ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{
			stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
			stackitem.NewByteArray(owner.BytesBE()),
		}))
		ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{
			stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
			stackitem.NewBigInteger(big.NewInt(int64(amount))),
			stackitem.NewBigInteger(big.NewInt(int64(amount))),
		}))

		return stackitem.NewBool(true)
	}

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		panic(ErrSalusAccountNotFound)
	}
	if acc.Status != state.HealthcareAccountActive {
		panic(ErrSalusAccountSuspended)
	}

	// Add credits
	acc.CreditsAvailable += amount
	acc.UpdatedAt = ic.Block.Index

	s.putAccount(ic.DAO, acc)

	// Emit event
	ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
		stackitem.NewBigInteger(big.NewInt(int64(amount))),
		stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable))),
	}))

	return stackitem.NewBool(true)
}

// getCredits returns available credits for an owner.
func (s *Salus) getCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	owner := toUint160(args[0])

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

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		return stackitem.NewBigInteger(big.NewInt(0))
	}

	return stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable)))
}

// recordMedicalEvent records a medical event (provider only).
func (s *Salus) recordMedicalEvent(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	patient := toUint160(args[0])
	recordType := state.MedicalRecordType(toUint64(args[1]))
	contentHashBytes := toBytes(args[2])
	credits := toUint64(args[3])

	// Convert bytes to Uint256
	var contentHash util.Uint256
	if len(contentHashBytes) == 32 {
		copy(contentHash[:], contentHashBytes)
	}

	// Check provider authority
	if !s.checkHealthcareProvider(ic) {
		panic(ErrSalusNotProvider)
	}

	// Get provider address
	provider := ic.VM.GetCallingScriptHash()

	// Get patient's Vita
	if s.Vita == nil {
		panic(ErrSalusNoVita)
	}
	vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient)
	if err != nil || vita == nil || vita.Status != state.TokenStatusActive {
		panic(ErrSalusNoVita)
	}

	// Check provider has access (unless emergency or authorized)
	vitaID, found := s.getVitaIDByOwner(ic.DAO, patient)
	if found {
		authID, hasAuth := s.getActiveAuthID(ic.DAO, vitaID, provider)
		if hasAuth {
			auth := s.getAuthInternal(ic.DAO, authID)
			if auth == nil || !auth.IsValid(ic.Block.Index) {
				panic(ErrSalusNoAccess)
			}
		}
		// If no auth, still allow (emergency can be logged separately)
	}

	// Get or auto-create account
	if !found {
		// Auto-create
		cfg := s.getConfigInternal(ic.DAO)
		cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
		cache.accountCount++
		s.setAccountCounter(ic.DAO, cache.accountCount)

		acc := &state.HealthcareAccount{
			VitaID:           vita.TokenID,
			Owner:            patient,
			AnnualAllocation: cfg.DefaultAnnualCredits,
			CreditsUsed:      0,
			CreditsAvailable: cfg.DefaultAnnualCredits,
			BiologicalAge:    0,
			LastCheckup:      0,
			Status:           state.HealthcareAccountActive,
			CreatedAt:        ic.Block.Index,
			UpdatedAt:        ic.Block.Index,
		}
		s.putAccount(ic.DAO, acc)
		s.setOwnerToVitaID(ic.DAO, patient, vita.TokenID)
		vitaID = vita.TokenID
	}

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		panic(ErrSalusAccountNotFound)
	}
	if acc.Status != state.HealthcareAccountActive {
		panic(ErrSalusAccountSuspended)
	}

	// Check sufficient credits
	if acc.CreditsAvailable < credits {
		panic(ErrSalusInsufficientCredits)
	}

	// Get next record ID
	cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
	recordID := cache.recordCount
	cache.recordCount++
	s.setRecordCounter(ic.DAO, cache.recordCount)

	// Deduct credits
	acc.CreditsUsed += credits
	acc.CreditsAvailable -= credits
	acc.UpdatedAt = ic.Block.Index

	// Update last checkup if appropriate
	if recordType == state.RecordTypeCheckup || recordType == state.RecordTypePreventive {
		acc.LastCheckup = ic.Block.Index
	}

	s.putAccount(ic.DAO, acc)

	// Create record
	record := &state.MedicalRecord{
		ID:          recordID,
		VitaID:      vitaID,
		Patient:     patient,
		Provider:    provider,
		RecordType:  recordType,
		ContentHash: contentHash,
		CreditsUsed: credits,
		CreatedAt:   ic.Block.Index,
		IsActive:    true,
	}

	s.putRecord(ic.DAO, record)
	s.setRecordByPatient(ic.DAO, vitaID, recordID)

	// Emit event
	ic.AddNotification(s.Hash, MedicalRecordCreatedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(recordID))),
		stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
		stackitem.NewBigInteger(big.NewInt(int64(recordType))),
	}))

	return stackitem.NewBigInteger(big.NewInt(int64(recordID)))
}

// getMedicalRecord returns medical record by ID.
func (s *Salus) getMedicalRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	recordID := toUint64(args[0])

	record := s.getRecordInternal(ic.DAO, recordID)
	if record == nil {
		return stackitem.Null{}
	}

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

// registerProvider registers a healthcare provider (committee only).
func (s *Salus) registerProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	address := toUint160(args[0])
	name := toString(args[1])
	specialty := toString(args[2])
	licenseHashBytes := toBytes(args[3])

	// Convert bytes to Uint256
	var licenseHash util.Uint256
	if len(licenseHashBytes) == 32 {
		copy(licenseHash[:], licenseHashBytes)
	}

	// Committee only
	if !s.checkCommittee(ic) {
		panic(ErrSalusNotCommittee)
	}

	// Validate inputs
	if len(name) == 0 || len(name) > 128 {
		panic(ErrSalusInvalidName)
	}
	if len(specialty) == 0 || len(specialty) > 64 {
		panic(ErrSalusInvalidSpecialty)
	}

	// Check if provider already exists
	_, exists := s.getProviderIDByAddress(ic.DAO, address)
	if exists {
		panic(ErrSalusProviderExists)
	}

	// Get next provider ID
	cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
	providerID := cache.providerCount
	cache.providerCount++
	s.setProviderCounter(ic.DAO, cache.providerCount)

	// Create provider
	provider := &state.HealthcareProvider{
		Address:      address,
		Name:         name,
		ProviderID:   providerID,
		Specialty:    specialty,
		LicenseHash:  licenseHash,
		Status:       state.ProviderStatusActive,
		RegisteredAt: ic.Block.Index,
		UpdatedAt:    ic.Block.Index,
	}

	s.putProvider(ic.DAO, provider)
	s.setProviderByAddress(ic.DAO, address, providerID)

	// Emit event
	ic.AddNotification(s.Hash, ProviderRegisteredEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(providerID))),
		stackitem.NewByteArray(address.BytesBE()),
		stackitem.NewByteArray([]byte(specialty)),
	}))

	return stackitem.NewBigInteger(big.NewInt(int64(providerID)))
}

// suspendProvider suspends a healthcare provider (committee only).
func (s *Salus) suspendProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	providerID := toUint64(args[0])
	reason := toString(args[1])

	// Committee only
	if !s.checkCommittee(ic) {
		panic(ErrSalusNotCommittee)
	}

	provider := s.getProviderInternal(ic.DAO, providerID)
	if provider == nil {
		panic(ErrSalusProviderNotFound)
	}
	if provider.Status == state.ProviderStatusRevoked {
		panic(ErrSalusProviderRevoked)
	}

	// Suspend
	provider.Status = state.ProviderStatusSuspended
	provider.UpdatedAt = ic.Block.Index
	s.putProvider(ic.DAO, provider)

	// Emit event
	ic.AddNotification(s.Hash, ProviderSuspendedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(providerID))),
		stackitem.NewByteArray([]byte(reason)),
	}))

	return stackitem.NewBool(true)
}

// getProvider returns provider details.
func (s *Salus) getProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	providerID := toUint64(args[0])

	provider := s.getProviderInternal(ic.DAO, providerID)
	if provider == nil {
		return stackitem.Null{}
	}

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

// getProviderByAddress returns provider by address.
func (s *Salus) getProviderByAddress(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	address := toUint160(args[0])

	providerID, found := s.getProviderIDByAddress(ic.DAO, address)
	if !found {
		return stackitem.Null{}
	}

	provider := s.getProviderInternal(ic.DAO, providerID)
	if provider == nil {
		return stackitem.Null{}
	}

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

// authorizeAccess grants provider access to patient records.
func (s *Salus) authorizeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	patient := toUint160(args[0])
	provider := toUint160(args[1])
	accessLevel := state.AccessLevel(toUint64(args[2]))
	duration := toUint32(args[3])

	// Get patient's Vita and account
	vitaID, found := s.getVitaIDByOwner(ic.DAO, patient)
	if !found {
		panic(ErrSalusAccountNotFound)
	}

	acc := s.getAccountInternal(ic.DAO, vitaID)
	if acc == nil {
		panic(ErrSalusAccountNotFound)
	}
	if acc.Status != state.HealthcareAccountActive {
		panic(ErrSalusAccountSuspended)
	}

	// Check caller is patient (self-authorization)
	caller := ic.VM.GetCallingScriptHash()
	if caller != patient && !s.checkCommittee(ic) {
		panic(ErrSalusNotPatient)
	}

	// Check max duration
	cfg := s.getConfigInternal(ic.DAO)
	if duration > cfg.MaxAuthorizationDuration {
		panic(ErrSalusExceedsMaxDuration)
	}

	// Check if authorization already exists
	existingAuthID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider)
	if exists {
		existingAuth := s.getAuthInternal(ic.DAO, existingAuthID)
		if existingAuth != nil && existingAuth.IsValid(ic.Block.Index) {
			panic(ErrSalusAuthorizationExists)
		}
	}

	// Get next auth ID
	cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
	authID := cache.authorizationCount
	cache.authorizationCount++
	s.setAuthCounter(ic.DAO, cache.authorizationCount)

	// Calculate expiry
	expiresAt := uint32(0)
	if duration > 0 {
		expiresAt = ic.Block.Index + duration
	}

	// Create authorization
	auth := &state.ProviderAuthorization{
		ID:          authID,
		VitaID:      vitaID,
		Patient:     patient,
		Provider:    provider,
		AccessLevel: accessLevel,
		StartsAt:    ic.Block.Index,
		ExpiresAt:   expiresAt,
		IsActive:    true,
		GrantedAt:   ic.Block.Index,
	}

	s.putAuth(ic.DAO, auth)
	s.setActiveAuthID(ic.DAO, vitaID, provider, authID)

	// Emit event
	ic.AddNotification(s.Hash, AuthorizationGrantedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(authID))),
		stackitem.NewBigInteger(big.NewInt(int64(vitaID))),
		stackitem.NewByteArray(provider.BytesBE()),
	}))

	return stackitem.NewBigInteger(big.NewInt(int64(authID)))
}

// revokeAccess revokes provider access.
func (s *Salus) revokeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	authID := toUint64(args[0])

	auth := s.getAuthInternal(ic.DAO, authID)
	if auth == nil {
		panic(ErrSalusAuthorizationNotFound)
	}

	// Check caller is patient or committee
	caller := ic.VM.GetCallingScriptHash()
	if caller != auth.Patient && !s.checkCommittee(ic) {
		panic(ErrSalusNotPatient)
	}

	// Revoke
	auth.IsActive = false
	s.putAuth(ic.DAO, auth)
	s.clearActiveAuth(ic.DAO, auth.VitaID, auth.Provider)

	// Emit event
	ic.AddNotification(s.Hash, AuthorizationRevokedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(authID))),
	}))

	return stackitem.NewBool(true)
}

// getAuthorization returns authorization details.
func (s *Salus) getAuthorization(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	authID := toUint64(args[0])

	auth := s.getAuthInternal(ic.DAO, authID)
	if auth == nil {
		return stackitem.Null{}
	}

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

// hasAccess checks if provider has access to patient.
func (s *Salus) hasAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	patient := toUint160(args[0])
	provider := toUint160(args[1])

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

	authID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider)
	if !exists {
		return stackitem.NewBool(false)
	}

	auth := s.getAuthInternal(ic.DAO, authID)
	if auth == nil {
		return stackitem.NewBool(false)
	}

	return stackitem.NewBool(auth.IsValid(ic.Block.Index))
}

// emergencyAccess requests emergency access to patient records.
func (s *Salus) emergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	patient := toUint160(args[0])
	reason := toString(args[1])

	// Check provider authority
	if !s.checkHealthcareProvider(ic) {
		panic(ErrSalusNotProvider)
	}

	if len(reason) == 0 || len(reason) > 256 {
		panic(ErrSalusInvalidReason)
	}

	// Get provider address
	provider := ic.VM.GetCallingScriptHash()

	// Get patient's Vita
	if s.Vita == nil {
		panic(ErrSalusNoVita)
	}
	vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient)
	if err != nil || vita == nil || vita.Status != state.TokenStatusActive {
		panic(ErrSalusNoVita)
	}

	// Get config for emergency duration
	cfg := s.getConfigInternal(ic.DAO)

	// Get next emergency ID
	cache := ic.DAO.GetRWCache(s.ID).(*SalusCache)
	emergencyID := cache.emergencyCount
	cache.emergencyCount++
	s.setEmergencyCounter(ic.DAO, cache.emergencyCount)

	// Create emergency access
	emergency := &state.EmergencyAccess{
		ID:          emergencyID,
		VitaID:      vita.TokenID,
		Patient:     patient,
		Provider:    provider,
		Reason:      reason,
		GrantedAt:   ic.Block.Index,
		ExpiresAt:   ic.Block.Index + cfg.EmergencyAccessDuration,
		WasReviewed: false,
	}

	s.putEmergency(ic.DAO, emergency)

	// Emit event
	ic.AddNotification(s.Hash, EmergencyAccessGrantedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(emergencyID))),
		stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))),
		stackitem.NewByteArray(provider.BytesBE()),
	}))

	return stackitem.NewBigInteger(big.NewInt(int64(emergencyID)))
}

// reviewEmergencyAccess marks emergency access as reviewed (committee only).
func (s *Salus) reviewEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	emergencyID := toUint64(args[0])

	// Committee only
	if !s.checkCommittee(ic) {
		panic(ErrSalusNotCommittee)
	}

	emergency := s.getEmergencyInternal(ic.DAO, emergencyID)
	if emergency == nil {
		panic(ErrSalusEmergencyNotFound)
	}

	// Mark as reviewed
	emergency.WasReviewed = true
	s.putEmergency(ic.DAO, emergency)

	// Emit event
	ic.AddNotification(s.Hash, EmergencyAccessReviewedEvent, stackitem.NewArray([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(emergencyID))),
	}))

	return stackitem.NewBool(true)
}

// getEmergencyAccess returns emergency access details.
func (s *Salus) getEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	emergencyID := toUint64(args[0])

	emergency := s.getEmergencyInternal(ic.DAO, emergencyID)
	if emergency == nil {
		return stackitem.Null{}
	}

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

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

// getTotalAccounts returns the total number of healthcare accounts.
func (s *Salus) getTotalAccounts(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	cache := ic.DAO.GetROCache(s.ID).(*SalusCache)
	return stackitem.NewBigInteger(big.NewInt(int64(cache.accountCount)))
}

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

// getTotalProviders returns the total number of healthcare providers.
func (s *Salus) getTotalProviders(ic *interop.Context, args []stackitem.Item) stackitem.Item {
	cache := ic.DAO.GetROCache(s.ID).(*SalusCache)
	return stackitem.NewBigInteger(big.NewInt(int64(cache.providerCount)))
}

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

// GetAccountByOwner returns a healthcare account by owner address.
func (s *Salus) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.HealthcareAccount, error) {
	vitaID, found := s.getVitaIDByOwner(d, owner)
	if !found {
		return nil, ErrSalusAccountNotFound
	}
	acc := s.getAccountInternal(d, vitaID)
	if acc == nil {
		return nil, ErrSalusAccountNotFound
	}
	return acc, nil
}

// HasValidAuthorization checks if provider has valid authorization for patient.
func (s *Salus) HasValidAuthorization(d *dao.Simple, patient util.Uint160, provider util.Uint160, blockHeight uint32) bool {
	vitaID, found := s.getVitaIDByOwner(d, patient)
	if !found {
		return false
	}

	authID, exists := s.getActiveAuthID(d, vitaID, provider)
	if !exists {
		return false
	}

	auth := s.getAuthInternal(d, authID)
	if auth == nil {
		return false
	}

	return auth.IsValid(blockHeight)
}

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